Table of Contents
"Amazon Cognito user pools implements ID, access, and refresh tokens as defined by the OpenID Connect (OIDC) open standard" (Amazon Cognito は OpenID Connect (OIDC) オープン標準で定められている通りに ID トークン、アクセストークン、レフレッシュトークンを実装しています) — “Using Tokens with UserPools” より抜粋
しかしながら、Cognito の OIDC 実装は非常に限定的で柔軟性もないため、Cognito の OIDC 実装がシステム要件を満たせないということはよくあります。例えば、Cognito が発行する ID トークンの署名アルゴリズムは RS256
ですが、セキュリティ上の理由により Financial-grade API (FAPI) が同署名アルゴリズムを禁止しているにも関わらず、それを変更する方法がありません。
このチュートリアルでは、Cognito をユーザーデータベースとしてだけ用いて OAuth/OIDC 関連のタスクを Authlete に委譲することで、Cognito を使いつつも同時に Financial-grade API などの最新 OAuth/OIDC 仕様 (参考: Authlete スペックシート) をサポートする方法を説明します。
OAuth 2.0 の文脈では、アクセストークン (及び任意でリフレッシュトークン) を発行するサーバーを認可サーバーと呼びます。一方、OpenID Connect の文脈では、ID トークンを発行するサーバーを OpenID Provider (IdP) と呼びます。OIDC は意図的に OAuth 2.0 上に定義されたため、一つのサーバーが両方の役割を持つことはよくあります。そのため、同じサーバーが文脈によって認可サーバーと呼ばれたり IdP と呼ばれたりします。このチュートリアルではそのようなサーバーを統一的に認可サーバーと呼びます。
OAuth/OIDC で最も一般的なフローである認可コードフロー (RFC 6749 Section 4.1) をサポートするためには、認可サーバーは二つのエンドポイントを実装しなければなりません。それらは認可エンドポイント (RFC 6749 Section 3.1)、トークンエンドポイント (RFC 6749 Section 3.2) と呼ばれます。Cognito ユーザープールはこれら二つのエンドポイントの実装を提供しますが、Cognito の OIDC 実装に満足できない場合は、自分でこれらのエンドポイントを実装する必要があります。
次の図は、Cognito と Authlete を併せて使った場合の認可コードフローにおけるコンポーネント間の関係を示しています。
ユーザー認証は Cognito が行うが OAuth/OIDC 関連のタスクは Authlete に委譲されるというのがこの図の要点です。OAuth 2.0 の中心となる仕様 (RFC 6749) が “The way in which the authorization server authenticates the resource owner (e.g., username and password login, session cookies) is beyond the scope of this specification.” (認可サーバーがリソースオーナーを認証する方法はこの仕様の範囲外である) と述べていることを考慮すると、この明確な分離には、ユーザー管理ソリューションが直接 OAuth/OIDC をサポートするアプローチよりも多くの利点があります。
後続のセクションでは、このチュートリアル用に用意された認可サーバーが認可エンドポイントとトークンエンドポイントの実装内で行っていることを詳細に説明します。
サンプル認可サーバーの認可エンドポイントは次のことを行います。
サンプル認可サーバーのトークンエンドポイントは次のことを行います。
上記に説明したアーキテクチャは django-oauth-server に実装されています。django-oauth-server は Django Web フレームワークを用いて Python で書かれたオープンソースの認可サーバーです。このサーバーを実行するには、次の手順を踏んでください。
email
属性を含めておいてください。ALLOW_ADMIN_USER_PASSWORD_AUTH
を有効にしてください。必要な Python ライブラリをインストールしてください。
$ pip install authlete # Python 用 Authlete ライブラリ
$ pip install authlete-django # Django 用 Authlete ライブラリ
$ pip install boto3 # Python 用 AWS SDK
認可サーバー実装のソースコードをダウンロードしてください。
$ git clone https://github.com/authlete/django-oauth-server.git
$ cd django-oauth-server
Authlete API にアクセスするため、Authlete 設定ファイル (authlete.ini
) を編集してください。
$ vi authlete.ini
Django 設定ファイル (django_oauth_server/settings.py
) を開き、
$ vi django_oauth_server/settings.py
backends.CognitoBackend
を AUTHENTICATION_BACKENDS
に追加してください。Django の認証バックエンドの詳細については、Customizing authentication in Django の Specifying authentication backends を参照してください。
AUTHENTICATION_BACKENDS = ('backends.CognitoBackend',)
また、同ファイル内の COGNITO_USER_POOL_ID
と COGNITO_CLIENT_ID
を適切に編集してください。
COGNITO_USER_POOL_ID = 'YOUR_COGNITO_USER_POOL_ID'
COGNITO_CLIENT_ID = 'YOUR_COGNITO_CLIENT_ID'
Cognito の AdminInitiateAuth API と AdminGetUesr API の呼び出し方に興味がある場合は、ソースコード cognito_backend.py
を調べてみてください。
認可サーバーを起動するには、次のコマンドを入力してください。
$ python manage.py runserver
“make run
” でも同じことができます。
$ make run
認可サーバーは下表にリストされているようなエンドポイントを幾つか公開しています。Authlete の設定 (authlete.ini
) が正しいかどうかを確認する簡単な方法は、ディスカバリーエンドポイント (http://localhost:8000/.well-known/openid-configuration) にアクセスし、OpenID Connect Discovery 1.0 に準拠する JSON が返ってくるかどうかを確認することです。
エンドポイント | URL |
---|---|
認可エンドポイント | http://localhost:8000/api/authorization |
トークンエンドポイント | http://localhost:8000/api/token |
ディスカバリーエンドポイント | http://localhost:8000/.well-known/openid-configuration |
全ての準備が整いました。認可コードフローでアクセストークンと ID トークンを取得してみましょう。
認可コードフローの最初のステップは、認可サーバーの認可エンドポイントに Web ブラウザ経由で認可リクエストを送ることです。このチュートリアルでは、認可エンドポイントは django-oauth-server が提供する http://localhost:8000/api/authorization
です。認可リクエストを表す下記の URL の CLIENT_ID
と REDIRECT_URI
を適切に置き換え、Web ブラウザを使ってその URL にアクセスしてください。
http://localhost:8000/api/authorization?response_type=code&client_id=CLIENT_ID&scope=openid+email&state=123&nonce=abc&redirect_uri=REDIRECT_URI
Web ブラウザには認可サーバーが生成した認可ページが表示されます。次のように見えるでしょう。
ページにはログイン ID とパスワードを入力するフィールドがあります。そこに Cognito ユーザープールに登録したユーザーのユーザー名とパスワードを入力し、Authorize ボタンを押してください。Web ブラウザはあなたのクライアントアプリケーションのリダクレクトエンドポイントにリダイレクトされます。
ブラウザのアドレスバーに表示されているリダイレクトエンドポイントの URL には、次のように code
レスポンスパラメーターが含まれています。
REDIRECT_URI?state=123&code=RwRq2Lp0bJVMiLPKAFz4qB1hxieBD1X5HKuv8EPkJeM
code
レスポンスパラメーターの値は、あなたのクライアントアプリケーションに対して認可サーバーから発行された認可コードです。この認可コードはクライアントアプリケーションがトークンリクエストを投げる際に必要となります。
認可コード取得後、クライアントアプリケーションは認可サーバーのトークンエンドポイントにトークンリクエストを投げます。このチュートリアルでは、トークンエンドポイントは django-oauth-server が提供する http://localhost:8000/api/token
です。
トークンリクエストはシェル端末で curl
コマンドを用いて投げることができます。下記はトークンリクエストの例です。タイプする前に、CLIENT_ID
、REDIRECT_URI
、CODE
を実際の値で置き換えてください。
$ curl http://localhost:8000/api/token -d grant_type=authorization_code -d client_id=CLIENT_ID -d redirect_uri=REDIRECT_URI -d code=CODE
引数 | 説明 |
---|---|
http://localhost:8000/api/token |
トークンエンドポイントの URL。 |
-d grant_type=authorization_code |
認可コードフローであることを示す。 |
-d client_id=CLIENT_ID |
クライアント ID を指定する。CLIENT_ID を実際のクライアント ID で置き換えること。 |
-d redirect_uri=REDIRECT_URI |
リダイレクト URI を指定する。REDIRECT_URI を認可リクエストで用いたものと同じ値で置き換えること。 |
-d code=CODE |
認可コードを指定する。CODE を実際の認可コードで置き換えること。 |
トークンリクエストが成功すると、トークンエンドポイントは access_token
と id_token
を含む JSON を返します。
{
"access_token": "FrGIJQpW51-l5mYJHcqGUNIKGJ1W23fFlW6c9AQEZEc",
"refresh_token": "jhvKm9-haLQwnIR4CfkL6bfPIBBlqluFeqZKAgdPNjM",
"scope": "email openid",
"id_token": "eyJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InRha2FAYXV0aGxldGUuY29tIiwiaXNzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsInN1YiI6IjIiLCJhdWQiOlsiNDMyNjM4NTY3MCJdLCJleHAiOjE2MTY0MTI3NDIsImlhdCI6MTYxNjMyNjM0MiwiYXV0aF90aW1lIjoxNjE2MzI2MTAwLCJub25jZSI6ImFiYyIsInNfaGFzaCI6InBtV2tXU0JDTDUxQmZraG43OXhQdUEifQ.7sXy2FcELxHo3LCQkb9teLaUE9jtRxXsa8diJKnkwAo",
"token_type": "Bearer",
"expires_in": 86400
}
access_token
の値が発行されたアクセストークンです。同様に、id_token
の値が発行された ID トークンです。
このチュートリアルで発行された ID トークンのペイロード部は、デコードすると下記のようになります。ペイロード内の email
の値が Cognito ユーザープール内のユーザーの email
属性と一致するかどうかをチェックすることで、認可サーバーと Cognito ユーザープールの通信が成功したかどうかを確認することができます。
{
"email": "taka@authlete.com",
"iss": "https://example.com",
"sub": "2",
"aud": [
"4326385670"
],
"exp": 1616412742,
"iat": 1616326342,
"auth_time": 1616326100,
"nonce": "abc",
"s_hash": "pmWkWSBCL51Bfkhn79xPuA"
}
あなたはこのチュートリアルを完了しました。Authlete を使うことで、認可サーバーに Amazon Cognito をユーザーデータベースとして使わせつつ同時に最新の OAuth/OIDC 仕様をサポートさせる方法を学びました。
サポートが必要であれば**お問い合わせ**ください。いつでも歓迎します!