🔒

API Gateway x Cognito で client id 変更したら認証が通らなくなったので解消した

2025/03/10に公開

事象

  • AWS Cognito の認証と連携させて API Gateway を使っていた。
  • curl で Cognito の認証トークン発行APIを直接叩くことでトークンを取得し、それを載せて直接API Gateway にリクエスト送信していた。
$ cat auth.json
{
  "ClientId": "piyopiyo",
  "AuthFlow": "USER_PASSWORD_AUTH",
  "AuthParameters": {
    "USERNAME": "peroper@hoge.com",
    "PASSWORD": "munyamunya",
    "SECRET_HASH": "amoamo"
  }
}

$ IdToken=$(curl -s -X POST \
  -H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' \
  -H 'Content-Type: application/x-amz-json-1.1' \
  --data @auth.json \
  https://cognito-idp.us-east-1.amazonaws.com/ | jq -r .AuthenticationResult.IdToken)

$ curl \
  -H "Authorization: Bearer ${IdToken}" \
  "https://XXXX.execute-api.us-east-1.amazonaws.com/mogemoge" | jq .
  • API Gateway にセットしていた Cognito の client_id(old) を clinet_id(new) に差し替えたところ、認証が通らなくなった。

原因

  • client_id(new)はCognitoのマネージドログイン画面からログインできるようにするために新規に発行した client_id だった。
  • したがってclinet_idと認証方式が異なっていたため、アクセスできないようになっていた。

対応策

  • client_id を (new) と (old) 二つセットする。
  • serverless framework を使って管理しているため、このようなセットになる。
provider:
  name: aws
  architecture: arm64
  runtime: provided.al2
  stage: ${opt:stage, self:custom.defaultStage}
  region: us-east-1
  httpApi:
    authorizers:
      cognitoAuthorizer:
        type: jwt
        identitySource: $request.header.Authorization
        issuerUrl: https://cognito-idp.us-east-1.amazonaws.com/${ssm:/cognito/user-pool-id/${self:provider.stage}}
        audience:
          - ${ssm:/cognito/client-id/${self:provider.stage}}
          - ${ssm:/cognito/client-id-backend/${self:provider.stage}} # ←ここ
    cors: true

詳細説明

Cognitoの認証フローには以下がある。

  1. USER_SRP_AUTH
  2. USER_PASSWORD_AUTH
  3. ADMIN_USER_PASSWORD_AUTH
  4. REFRESH_TOKEN_AUTH
  5. CUSTOM_AUTH
  6. USER_PASSWORD_AUTH_WITH_MFA
  7. ADMIN_NO_SRP_AUTH

このうち、Cognitoのホスト型UI(マネージドログイン画面)で利用可能な認証フローは以下

  1. USER_SRP_AUTH
    マネージドUI用の標準的な認証フロー。デフォルトで有効で最もセキュアな方式。
  2. REFRESH_TOKEN_AUTH
    デフォルトで有効。セッション維持やマネージドUIでの自動更新に使用。
  3. USER_PASSWORD_AUTH_WITH_MFA
    MFAが有効な場合に使用。マネージドUIでMFA画面を表示。

一方、最初に発行した client_id(old) に設定されていた認証フローはUSER_PASSWORD_AUTH。異なるものだったのでそりゃあ通らない。

感想(反省点)

生成AIに対応策を聞いてみたら強引にUSER_SRP_AUTHを乗り越える長いスクリプトを書いたりして大変だった...。
そもそも一つのuser_poolに複数のclient_idが発行できる=複数のアプリを管理できるということは、API Gatewayにも複数のclient_idがセットできるのは自然な発想。これに早く気づけばよかった...。

Discussion