API GatewayのJWTオーソライザーでGoogle IDトークンを検証してみた

- toCアプリの認証認可ソリューションを選定する場合、OpenID ConnectやOAuth2.0等の技術を利用したソーシャルログインの実装が候補に上がることが多いのではないでしょうか。
- 今回はAmazon API GatewayのJWTオーソライザーを利用して、Googleアカウントで認証済み利用者への認可処理を実装してみました。

## 環境構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/38d33f32-8cac-43de-f8b1-f830ff5c978f.png)

## 設定手順

### Google OAuth 2.0をセットアップする

※事前にGoogle Cloud Platformでプロジェクト作成が完了していることを想定しています。

- Google Cloud Platformの「APIとサービス」にアクセスします。
- 左メニューから「認証情報」を選択した後、「認証情報を作成」を押下します。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/115e4652-59c2-f3ee-8fa5-e54365376ebe.png)


- 「OAuthクライアントIDの作成」を選択し、必要な情報を入力します。
    - アプリケーションの種類：ウェブアプリケーション
    - 名前：任意の名前を入力
    - 承認済みのリダイレクトURI：http://127.0.0.1/callback
![Untitled (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/c00b7607-cd37-ae53-33b1-5cd3ceb93743.png)

- クライアント作成後に表示される、クライアントIDとシークレットを控えておきます。


### Lambdaを作成する

- AWSコンソールにログイン後、Lambdaコンソールへ遷移します。
- 「関数の作成」を押下し、ランタイムに「Python 3.11」を選択して、Lambda関数を作成します。(ソースコードは変更不要です。)
![Untitled (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/8e9311e7-4161-c6bb-31e9-6c1e1bb92588.png)


### API Gatewayを作成する

- AWSコンソールにログイン後、API Gatewayコンソールへ遷移します。
- 「APIを作成」を押下後、APIタイプとして「HTTP API」を選択して構築します。
![Untitled (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/1009e667-8980-ebf8-2479-b274cf08314e.png)

- 先程作成したLambdaとの統合を設定します。
    - 統合：Lambda
    - AWSリージョン：先程作成したLambdaのリージョンを選択してください。
    - Lambda関数：先程作成したLambda関数を選択してください。
    - API名：任意の名前を入力してください。
![Untitled (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/22956ce1-95a5-33ba-754e-65b697d48f45.png)


- 作成完了後、https://[APIのURL]/[Lambda関数名]をWebブラウザで開き、API Gateway経由でLambda関数を呼び出せることを確認します。
![Untitled (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/42051193-7c26-ce6a-c8d0-5d53660b1810.png)


### API GatewayにJWTオーソライザーを設定する

- 作成したAPI Gatewayの設定画面にて、「Authorization」メニューを選択します。
- 「オーソライザーを作成してアタッチ」を押下します。
![Untitled (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/3f01dc15-7cdb-9a8b-c38e-5471328675be.png)
- オーソライザーを作成します。
    - タイプ：JWT
    - 発行者URL：[https://accounts.google.com](https://accounts.google.com/)
        - OpenID Configurationエンドポイントから情報取得可能です。
            - https://accounts.google.com/.well-known/openid-configuration
    - 対象者：手順「Google OAuth 2.0をセットアップする」で取得したクライアントIDを指定してください。
![Untitled (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/cb1d6009-d5d2-8f77-530c-20503ffc92f3.png)


## 動作確認手順

### 異常系

- https://[APIのURL]/[Lambda関数名]をWebブラウザで開いた場合、アクセスが拒否されることを確認します。
![Untitled (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/c4284501-b058-6ca6-dade-13bdbfd4721c.png)


### 正常系
OIDCの認可コードフローでIDトークンを取得した上で、API Gatewayにアクセスできることを確認します。

- 認証リクエスト
    - Webブラウザで認可エンドポイントにアクセスし、遷移後の画面でご自身のGoogleアカウントでログインします。
        - response_type
            - レスポンスとして認可コードを取得するために、`code`を指定します。
        - state
            - CSRF対策のために、ランダムな値を指定します。
        - scope
            - トークンエンドポイントからアクセストークンだけでなくIDトークンも取得するために、`oidc`を指定します。
        - redirect_uri
            - 認証レスポンス時のリダレクト先として、`http://127.0.0.1/callback`を指定します。(今回はローカルループバックアドレスを指定していますが、実際にアプリケーションを作成する場合は適切なURLを指定してください。)
        ```text
        https://accounts.google.com/o/oauth2/v2/auth
        ?response_type=code
        &client_id=[クライアントID]
        &state=xyz
        &scope=openid
        &redirect_uri=http://127.0.0.1/callback
        ```
- 認証レスポンス
    - `http://127.0.0.1/callback`にリダイレクトされるので、URLバーに表示される認可コード(クエリ文字列`code`)の値を控えておきます。

- トークンリクエスト
    - CurlでトークンエンドポイントにHTTPリクエストし、レスポンスに含まれる`id_token`の値を控えておきます。
    
        ```jsx
        curl \
        -d "client_id=[クライアントID]" \
        -d "client_secret=[クライアントシークレット]" \
        -d "redirect_uri=http://127.0.0.1/callback" \
        -d "grant_type=authorization_code" \
        -d "code=[認可コード]" \
        https://www.googleapis.com/oauth2/v4/token
        ```

- IDトークンを用いたAPI Gatewayへのアクセス
    - Curlで`id_token`の値を用いてAPI GatewayにHTTPリクエストし、「Hello from Lambda!」と表示されれば成功です！
        ```jsx
        curl \
        -H 'Authorization: Bearer [IDトークン]' \
        https://[API GatewayのURL]/[Lambda関数名]
        ```

## まとめ

- API GatewayのJWTオーソライザーを利用して、Googleログインユーザへの認可処理を実装してみました。

- 今回はJWTの検証及び認可までを実装しましたが、Lambda関数でクレーム情報にアクセスすることもできますので、ぜひお試しください〜

## 参考

- OAuth 2.0 を使用した Google API へのアクセス
    - https://developers.google.com/identity/protocols/oauth2?hl=ja
- JWT オーソライザーを使用した HTTP API へのアクセスの制御
    - https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/http-api-jwt-authorizer.html


## 注意事項
- 本記事は万全を期して作成していますが、お気づきの点がありましたら、ご連絡よろしくお願いします。
- なお、本記事の内容を利用した結果及び影響について、筆者は一切の責任を負いませんので、予めご了承ください。