ブログ

【AWS】外部OAuthとCognitoのトークン交換。アプローチ結果を公開!

この記事をSNSでシェア!

はじめに

企業のシステム統合において、複数の認証システムを連携させる必要に迫られることがあります。今回、私も業務において外部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トークンのような外部トークンを渡すための標準的な方法が存在しません。

AuthParametersClientMetadataは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 AuthorizerLambda 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つのログイン方法から選択できます

  1. Cognito標準認証(SAML連携によるSSO認証)
  2. Salesforce OAuth → Custom Auth Flow (Lambda Trigger)
  3. 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トークンが返されます。

  1. Backend APIにSalesforceトークンを送信
  2. Custom Authorizerで認証
  3. Lambda関数でトークン検証とユーザーマッピング
  4. Cognitoトークンを返却

想定していたとおり、どちらの方法を利用してもSalesforceトークンを使用してのCognitoトークンの取得は問題なく行えました。ユーザー視点では特に処理時間に大きな差異があるわけでもなく、どちらを使用しても快適にAPI実行が行えます。

実装のポイント

トークンの分離管理

  • 今回の主題の2種類のトークンを cognitoTriggerTokencognitoBackendTokenとして別々に管理

Salesforce id_tokenの活用

  • IDトークンからemail、usernameを直接取得し、UserInfo API呼び出しによる情報取得フェーズをスキップ

まとめ

今回はSalesforce OAuth認証からAWS Cognitoトークンへの交換について、3つのアプローチを検討しました。その結果、独自のBackend APIを構築する方が、結果としてシンプルで堅牢なシステムになることが分かりました 。

調査結果

  1. Pre Auth Lambda: 実装不可能。外部IdPトークンの検証には適していない
  2. Custom Auth Challenge Lambda Trigger: 実装可能だが複雑で保守性に課題あり
  3. Backend API経由でのトークン生成: 新規にAPIやカスタム認証の作成が必要だが、保守がしやすい

推奨アプローチ

下記の理由から、アプローチ3: Backend API経由でのトークン生成を推奨します。

  • 実装がシンプルで理解しやすい
  • デバッグが容易
  • 保守性が高い
  • 拡張性がある
  • セキュアな実装が可能

最後に

認証システムの統合は複雑になりがちですが、各アプローチの特性を理解し、要件に合った方法を選択することが重要です。本記事が、同様の課題に直面している開発者の助けになれば幸いです。


参考資料

投稿者プロフィール

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