Reactで外部APIを使う際の備忘(+Lambda,APIGateway)

## 背景
・Reactを学習し始め、Reactと外部APIを使った簡単なアウトプットをしたかった
・外部APIとのやり取りでセキュリティまわりで考えることがあったので備忘として残す

## 環境
Vite, React, Firebase, (+ Lambda, APIGateway)

## どんなもの？
SpotifyAPIを使ってクリックするとプレイリストから曲を一つ取得して表示する
![react.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3683048/43f52a6f-3597-8b6a-5bca-d2c686c4f201.gif)


## (1) React, Firebaseのケース(情報が見えている)
FirebaseにReactページをデプロイしたかったが、SpotifyAPIとのやり取りに使うアクセストークンなどの情報が開発者ツールから見れる状態になっていた

例
```.env
VITE_PLAYLIST_ID=xxxxxxxx
VITE_ACCESS_TOKEN=yyyyyyyy
```
```xxx.jsx
// SpotifyAPIからデータを取ってくる処理
const spotify = () => {
  const apiUrl = `https://api.spotify.com/v1/playlists/${import.meta.env.VITE_PLAYLIST_ID}/tracks`;

  axios.get(apiUrl, { headers: { Authorization: `Bearer ${import.meta.env.VITE_ACCESS_TOKEN}` } })
};
```
これをFirebaseにデプロイして開発者ツールから見ると、実際の値が埋め込まれた状態になっていて見れてしまう
```
axios.get("https://api.spotify.com/v1/playlists/xxxxxxxx/tracks", { headers: { Authorization: "Bearer yyyyyyyy" } })
```

そのため上記のアクセストークンなどの情報を含む処理はサーバ側に持たせる必要があると認識し、候補としてLambdaが浮かんだ


## (2) React, Firebase, Lambdaのケース(他の人からAPIを叩かれる可能性)
Lambdaに **関数URL** という「APIGateway + Lambda」の構成を、Lambdaだけでもできるような機能があったのでそれを使ってみることにした(認証タイプはNONE)
![スクリーンショット 2024-05-03 13.37.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3683048/b87f6147-5273-8aa5-4d95-f90fca0058b4.png)

SpotifyAPIとのやり取りはLambdaに実装し、React側ではLambda関数URLにリクエストを送り、　Lambdaが得たデータを受け取る

例
```index.mjs(Lambdaコード)
export const handler = async (event) => {
  const apiUrl = `https://api.spotify.com/v1/playlists/${process.env.PLAYLIST_ID}/tracks`;
  const spotifyData = await axios.get(apiUrl, {
    headers: {
      Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
    },
  });
  return spotifyData;
};
```
```xxx.jsx
const spotify = async () => {
  const lambdaData = await axios.get({Lambda関数URLの値});
};
```

これで(1)であったアクセストークンなどの情報が見れてしまうという点は解消できたが、一点気になったことがあり、
Lambda関数URLの認証タイプをNONEにすると、誰からでもそのURLにアクセスできるということ。
Reactコードには上記のようにLambda関数URLを記述しているので、そうすると(1)と同じような感じで今度はLambda関数URLを知られAPIを叩くことができてしまう。
認証タイプにはNONEではなくIAM認証というのもあるが、今回の環境に対しては使えなさそうだったので、制御をかけれる他の機能としてAPIGatewayを使うことにした


## (3) React, Firebase, Lambda, APIGatewayのケース(自分しかAPIを叩けないように制御)
APIGatewayのリソースポリシーを使ってIPアドレスでの制御をかけることができる
例
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": [
               "execute-api:/*"
            ],
            "Condition" : {
                "IpAddress": {
                    "aws:SourceIp": ["192.0.2.0/24", "198.51.100.0/24" ]
                }
            }
        }
    ]
}
```

React側はリクエストを送るURLをLambda関数URLからAPIGatewayのURLに変える
```xxx.jsx
const spotify = async () => {
  const lambdaData = await axios.get({APIGatewayの値});
};
```

例えば許可するIPを自宅からのみにすれば、FirebaseにデプロイしてReactコードに書いてある上記APIGatewayのURLを知られても他の人からはAPIGatewayのURLにアクセスすることはできずAPIを叩かれることはなくなった


## 参考
https://qiita.com/ken-s/items/b6190283db28295155bb

https://vitejs.dev/guide/env-and-mode.html

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-urls.html

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-control-access-to-api.html