
はじめに
企業のシステム統合において、複数の認証システムを連携させる必要に迫られることがあります。今回、私も業務において外部OAuthで認証されたユーザーに対して、AWS Cognitoのトークンをどのように発行するかという課題が発生し、調査しました。
当初は簡単にできるだろうと描いていた構想とは大きく異なった経緯も含め、実装可能な方法と実装不可能な方法を明確にし、それぞれのメリット・デメリットを詳しく解説します。
本検証では外部OAuthとして、Salesforce OAuth認証を使用していますが、記事内容はOAuth認証を使用するサービス全般に役立てることができるはずです。
背景と課題
今回の想定システム構成を端的に図で示すと下記のようなイメージです。

以降でその詳細について説明します。
システム構成
- 同一のSalesforce環境をOAuthプロバイダーとSAML IdPとしてそれぞれ認証に利用する2つのシステムが存在する
- OAuthプロバイダーとして利用するシステムAはバックエンドサーバーを持たず、フロント処理で完結するシンプルなアプリ
- SAML IdPとして利用するシステムBはAWS上に構築されたwebアプリケーションであり、Amazon Cognito経由でログインやAPI認証を管理している
- それぞれのシステムはユーザー名をキーに同一のユーザーが一対一で紐づけられている
解決したい課題
今回の最終的な目的は図にもある通り、システムAから直接システムBのAWS環境内のAPIを実行するというものです。そのためには、外部OAuth(Salesforce)で既に認証済みのユーザーに対して、どのようにCognitoトークンを発行(トークン交換)するか、という点が主な課題となります。
しかしながら、現状Cognitoには公式のトークン交換機能は存在しません。そのため、Salesforceの認証情報を使ってCognitoトークンを取得するために一工夫が必要になります。
また今回は、API実行の権限は紐づけられたユーザーの認可に準ずる仕様と通常通りCognito認証でAPI実行するユーザーへ影響を与えてはいけないという要件も満たす必要があります。
検証した3つのアプローチ
この課題に対し、私は以下の3つのアプローチで検討しました 。
アプローチ1: Pre Auth Lambda(実装不可)
概要
最初に検討したのは、CognitoのPre Auth(認証前)Lambdaトリガーを使用する方法です。Pre Auth Lambdaは、Cognito認証の前に実行されるLambda関数で、認証の可否を制御できます。
「Pre Auth LambdaでSalesforceトークンを検証してから、Cognito認証を許可すれば良いのでは?」と簡単に考えていましたが、この考えは早々に失敗に終わります。
なぜ実装できないのか
結論から言うとこのアプローチでは実装不可能です。理由は下記の通りです。
1. トリガーのタイミングの問題
Pre Auth Lambdaは、Cognitoの既存の認証フロー(パスワード認証、SRP認証など)の前に実行されます。外部IdP(Salesforce)のトークンを受け取る設計にはなっていません。
2. 外部トークンを渡す手段がない
Pre Auth Lambdaに渡される event.requestには、Cognitoの認証パラメータのみが含まれます。Salesforceトークンのような外部トークンを渡すための標準的な方法が存在しません。
AuthParametersや ClientMetadataはCognito認証フロー用に設計されており、外部IdPのトークン検証には適していません。
3. 認証フローの制約
Pre Auth Lambdaは、既存のCognito認証フローを補完するためのものです。独立した認証メカニズム(Salesforce OAuth)を統合する用途には適していません。
結論
Pre Auth Lambdaは、Cognito認証の事前処理(ログイン試行回数チェック、IP制限など)には有効ですが、外部IdPトークンの検証とトークン交換には使用できませんでした。
簡単に実装できると考えていた私にとっては、序盤で大きくつまづくことになりましたが、そもそもAWSの各サービスは特定のユースケースを想定して設計されているため、本来の用途以外での利用は困難というのも当然と言えば当然です。
アプローチ2: Custom Auth Challenge Lambda Trigger(実装可能だが複雑)
概要
次に検討したのは、CognitoのCustom Authentication Flow(カスタム認証フロー)を使用する方法です。このアプローチでは、Cognito Lambdaトリガーを活用してトークン検証を行います。
アーキテクチャフロー

必要なコンポーネント
1. 3つのLambda Trigger
Custom Authentication Flowを実施するためには下記の3つの工程を担当するLambdaをそれぞれ作成する必要があります。
- Define Auth Challenge: カスタム認証フローの定義
- Create Auth Challenge: チャレンジパラメータの設定
- Verify Auth Challenge Response: Salesforceトークンの検証 ←ここが重要
2. トークン検証ロジック
実際にトークンの検証を行うのはVerify Auth Challenge Response Lambda内で、Salesforce UserInfo APIでトークンの検証をし、Cognitoで該当ユーザーを検索、マッチングに成功したらトークンの払い出しを行います。
3. フロントエンド処理
通常のCognitoログインフローではなく、Cognito SDKを使用して、カスタム認証へSalesforceトークンを引き渡す必要があります。
| メリット | デメリット |
|---|---|
| Cognitoの標準機能を最大限活用できる | Lambda Triggerの実装が複雑(3つのLambda関数が必ず必要) |
| トークンライフサイクルをCognitoが自動管理してくれる | デバッグが難しい(Triggerの動作確認が複雑) |
| フロントエンドでCognito SDKの呼び出し処理が追加で必要 | |
| Triggerの実行順序や状態管理が複雑 |
評価
このアプローチは既存のCognito認証フローを活用可能で、一見スマートですがLambda Triggerを使用する都合、その複雑さや制限事項があり、保守性に課題あると言えます。
アプローチ3: Backend API経由でのトークン生成(推奨)
概要
最終的に採用したのは、新規APIエンドポイント(POST /auth/exchange)を作成し、Lambda関数でSalesforceトークンを検証してCognitoトークンを生成する方法です。
一度別APIを経由するので遠回りのようですが、このアプローチではLambda Triggerを使用せず、シンプルで保守性の高い実装が実現できます。
アーキテクチャフロー

必要なコンポーネント
1. トークン交換API(Lambda Function)
ヘッダー情報にてSalesforceトークンをトークン交換処理へ引き渡し、処理内でSalesforce UserInfo APIを用いてトークンを検証とユーザー情報の取得をします。取得したユーザー情報でCognitoユーザーを検索し、マッチングした場合、トークンを払い出しを行います。
2. Custom Authorizer(Lambda Function)
処理内でSalesforce UserInfo APIでトークンの有効性を検証しリクエストの可否をレスポンスします。
3. API Gateway設定
Salesforceトークンで認証可能なCustom Authorizerを作成し、API Gatewayの認証にそれを使用してAuthorizationヘッダーを検証するよう設定。
| メリット | デメリット |
|---|---|
| マッピングロジックをカスタマイズ可能 | 新規APIエンドポイントの作成が必要 |
| エラーハンドリングが柔軟 | Custom Authorizerの作成と設定が必要 |
| Lambda Trigger不要 | |
| Lambdaが独立しているためテスト・デバッグが容易 | |
| 複数のIdPに対応可能で拡張性が高い |
評価
新規APIエンドポイントの作成というコストはありますが、実装のシンプルさ、保守性、拡張性を考慮すると、最も推奨できるアプローチです。
アプローチの比較表
| 項目 | アプローチ1 Pre Auth Lambda | アプローチ2 Custom Auth Challenge | アプローチ3 Backend API |
|---|---|---|---|
| 実装可否 | 不可能 | 可能 | 可能 |
| 新規APIエンドポイント | – | 不要 | 必要 |
| Lambda Triggers | – | 必要(3つ) | 不要 |
| 実装の複雑さ | – | 高い | 低い |
| デバッグ | – | 難しい | 容易 |
| カスタマイズ性 | – | 低い | 高い |
| 保守性 | – | 低い | 高い |
| 推奨度 | 対象外 | 中 | 高 |
表からも、特別な要件がなければ素直にトークン交換のためのAPIを作成するアプローチ3が良さそうです。
実際にサンプルアプリを構築
ここまでの調査で実装可能と判断した2つのアプローチについて実際にAWS上にサンプルアプリを構築して挙動を確かめてみました。
アプリの実装は手慣れているReact + TypeScript + Viteを使用しました。
アプリの特徴
- 3つの認証方法を比較検証:Cognito標準認証、Custom Auth Flow、Backend API方式をすべて実装
- 認証フローの可視化:各ステップがリアルタイムでハイライトされ、処理の流れが直感的に理解できる
- トークン情報の表示:取得したトークンを確認でき、実際の動作を検証可能
- サンプルAPI呼び出し:どの認証方法でも同じAPIが呼び出せることを実証
ログイン方法選択画面

3つのログイン方法から選択できます
- Cognito標準認証(SAML連携によるSSO認証)
- Salesforce OAuth → Custom Auth Flow (Lambda Trigger)
- Salesforce OAuth → Backend API (トークン交換)
2もしくは3のログイン方法を選択し、ブラウザで既にSalesforceへログイン済みだった場合は、下記のようなアクセス許可画面へ遷移します。

Custom Auth Flow(アプローチ2)の実装例

左カラム:認証フロー図
- 現在のステップがハイライト表示
- Frontend → Salesforce → Cognito Trigger → Cognito → APIの流れを可視化
中央カラム:認証情報とアクション
Salesforceトークン取得後、「カスタム認証フロー実行」ボタンを押すと下記のフローでCognitoトークンが返されます。
- 1. Cognito SDK(InitiateAuth)でカスタムチャレンジを開始
- 2. SalesforceトークンをChallengeパラメータとして送信(RespondToAuthChallenge)
- 3. Lambda Trigger(Verify Auth Challenge Response)でSalesforceトークンを検証
- 4. emailでCognitoユーザーをマッピング
- 5. 検証成功時、Cognitoが自動的にトークンを発行
Cognitoトークン取得後、「サンプルAPI呼び出し」が可能になります。
右カラム:APIレスポンス
- API呼び出し結果をJSON形式で表示
Backend API(アプローチ3)の実装例

Custom Auth Flowとほぼ同じUIですが、中央カラムのボタンが「トークンエクスチェンジAPI呼び出し」になっています。
このボタンを押すと下記のフローでCognitoトークンが返されます。
- Backend APIにSalesforceトークンを送信
- Custom Authorizerで認証
- Lambda関数でトークン検証とユーザーマッピング
- Cognitoトークンを返却
想定していたとおり、どちらの方法を利用してもSalesforceトークンを使用してのCognitoトークンの取得は問題なく行えました。ユーザー視点では特に処理時間に大きな差異があるわけでもなく、どちらを使用しても快適にAPI実行が行えます。
実装のポイント
トークンの分離管理
- 今回の主題の2種類のトークンを
cognitoTriggerTokenとcognitoBackendTokenとして別々に管理
Salesforce id_tokenの活用
- IDトークンからemail、usernameを直接取得し、UserInfo API呼び出しによる情報取得フェーズをスキップ
Tips: その他懸念事項の調査はこちら
トークンのライフサイクルと有効期限
2種類のトークンをやり取りするため、トークンのライフサイクルを正しく理解する意図で整理をしました。
Cognitoトークンの独立性
一度取得したCognitoトークンは、Salesforceトークンとは完全に独立して動作します。そのためどちらかのトークンの有効期限依存して両者の有効期限が切れるわけではありません。
Salesforceトークンの役割
SalesforceトークンはCognitoトークン取得時の認証情報として1回だけ使用されます。有効期限切れ後には新しいCognitoトークンの取得ができなくなるだけで、既存のCognitoトークンには影響しません。
Cognitoトークン更新の流れ
基本的にはRefreshトークンを使用して自動更新が可能です。その際、Salesforceトークンは不要です。
▼Cognito Accessトークン期限切れ時のフローはこちら
[Frontend] Refreshトークンで更新リクエスト
↓
[Cognito] 新しいAccessトークン/IDトークンを発行
↓
[Frontend] APIを継続利用可能
▼Cognito Refreshトークン期限切れ時のフローはこちら
この場合のみ、再度Salesforceログインが必要です。
[Frontend] 再度Salesforceログイン
↓
[Salesforce] 新しいSalesforceトークン取得
↓
[Frontend] POST /auth/exchange
↓
[Backend] 新しいCognitoトークンセット発行
ユーザー体験への影響
- ユーザーは頻繁にSalesforceへログインし直す必要がありません
- Refreshトークンの有効期限内(最大30日)はシームレスにAPI利用が可能です
- Refreshトークン自動更新により、バックグラウンドでトークンが更新されます
このライフサイクル設計を理解してシステムを構築することにより、優れたユーザー体験を提供できます。
セキュリティ上の考慮点
実際に実装することを想定し、下記のセキュリティの観点を考慮し対策を検討しました。
1. トークン交換APIでSalesforceトークン自体を認証情報として機能させる
- Salesforceトークンは短命(通常1-2時間)
- 盗まれても有効期限がある
- Salesforce側で無効化可能
静的なAPIキーではなく、Custom Authorizerを用意し、動的で短命なトークンを認証情報として使用することで、セキュリティリスクを低減しています。
2. 二重検証
- Salesforceでトークン検証(UserInfo APIで検証)
- Cognitoでユーザー存在確認
- 両方成功した場合のみトークン発行
2つの独立したシステムでダブルチェックをすることで、セキュリティを強化しています。
3. ワンタイムユース
さらにセキュリティを強化したい場合、同じSalesforceトークンで複数回交換できないようにする実装も可能です。
想定実装方法(未検証)
- TTLはSalesforceトークンの有効期限と同じにしておく(通常1-2時間)
- DynamoDBテーブルでトークンハッシュを管理
- トークン交換前にハッシュの存在を確認
- 使用済みトークンはTTL(Time To Live)で自動削除
まとめ
今回はSalesforce OAuth認証からAWS Cognitoトークンへの交換について、3つのアプローチを検討しました。その結果、独自のBackend APIを構築する方が、結果としてシンプルで堅牢なシステムになることが分かりました 。
調査結果
- Pre Auth Lambda: 実装不可能。外部IdPトークンの検証には適していない
- Custom Auth Challenge Lambda Trigger: 実装可能だが複雑で保守性に課題あり
- Backend API経由でのトークン生成: 新規にAPIやカスタム認証の作成が必要だが、保守がしやすい
推奨アプローチ
下記の理由から、アプローチ3: Backend API経由でのトークン生成を推奨します。
- 実装がシンプルで理解しやすい
- デバッグが容易
- 保守性が高い
- 拡張性がある
- セキュアな実装が可能
最後に
認証システムの統合は複雑になりがちですが、各アプローチの特性を理解し、要件に合った方法を選択することが重要です。本記事が、同様の課題に直面している開発者の助けになれば幸いです。
参考資料
- OAuth 2.0 Token Exchange (RFC 8693)
- AWS Cognito Custom Authentication Flow
- Salesforce OAuth 2.0 Endpoints
- AWS Cognito Admin APIs
投稿者プロフィール

-
2021年入社。BS事業部所属。
入社当初から一貫してAWSを用いた開発案件に携わっており、現在はサーバレス構成を基本としたプロジェクトに従事しています。
クラウドは進歩の早い技術領域ですので、最新技術のキャッチアップを日々積極的に行っています。
