Table of Contents
RFC 9126: OAuth 2.0 Pushed Authorization Requests (PAR) は、OAuth2 フレームワークにおける強力なセキュリティ拡張のひとつです。これは、クライアントが認可サーバーに対して、認可リクエストを従来の方法(ブラウザリダイレクトによる間接通信)で送信する前に、その内容を「プッシュ」(直接送信)できるようにするものです。
PAR 仕様が定義するのは、認可リクエストの内容の事前送信を受けつけるエンドポイント (PAR EP) です。PAR EP は、リクエストを受信すると、識別子 (request_uri) を含むレスポンスを返却します。その返却された識別子を、クライアントは認可リクエストに含めます。
認可リクエストの内容を直接送信し、従来の間接通信による認可リクエストと分離することにより、セキュリティ強化の選択肢が広がります。たとえば SPA (Single Page Application) では、サーバーサイドで認可リクエストの内容を生成・送信し、ブラウザを経由する認可リクエスト自体には詳細を含めないようにできます。またモバイルアプリケーションにおいても、ブラウザに認可リクエスト処理を引き継ぐ前に、認可リクエストの内容を生成・送信できるようになります。
本記事では、Authlete における PAR のサポートの概要と、設定方法について記述します。
PAR をサポートするためには、認可サーバー(OIDC における OP)に PAR EP を実装し、Authlete の PAR サポートを設定する必要があります。
認可サーバーの PAR EP のバックエンドとなるのは Authlete の /pushed_auth_req API です。この API は、他の OAuth/OIDC エンドポイントに対応する Authlete API と同様に設計されています。認可サーバーは、クライアントから受信した「認可リクエストの内容」を、そのまま Authlete に転送すれば良いのです。
Authlete の /pushed_auth_req API は、/auth/authorization API と同様のペイロードと、それに加えてクライアント認証の情報も受けつけます。そして認可サーバーに、クライアントに返却されることになるレスポンスの内容 (responseContent) を提供します。レスポンスの内容には request_uri が含まれています。クライアントはこの request_uri を取り出し、認可リクエストに指定することになります。
クライアントが PAR EP にプッシュする認可リクエストの内容は、従来の認可エンドポイントに対する認可リクエストの内容と同じです。異なるのは、クライアントが POST メソッドかつ application/x-www-form-urlencoded メディアタイプを用いて、認可リクエストの内容を送信する点です。
POST /as/par HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
response_type=code&
client_id=3280859750204&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_challenge=W78hCS0q72DfIHa...kgZkEJuAFaT4&
code_challenge_method=S256
以下は認可サーバーと Authlete の /pushed_auth_req API との間のやりとりの例です。PKCE を含む認可コードフローの要求を、クライアントが PAR EP に事前送信しています。Authlete は、action として “CREATED”、そして requestUri を含むレスポンスを返却しています。これは、Authlete が認可リクエストの内容を受けつけ、 その識別子として requestUri を生成したことを示しています。同時に Authlete は responseContent を返却しています。認可サーバーはこの responseContent の値(コンテンツ)を、PAR EP からのレスポンスとして、クライアントに返却することになります。
curl --request POST 'https://api.authlete.com/api/pushed_auth_req' \
--header 'Authorization: Basic ************' \
--header 'Content-Type: application/json' \
--data '{
"parameters": "response_type=code&
client_id=3280859750204&
redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb&
code_challenge=W78hCS0q72DfIHa...kgZkEJuAFaT4&
code_challenge_method=S256",
"clientId": "3280859750204"}'
{
"type": "pushedAuthReqResponse",
"resultCode": "A245001",
"resultMessage": "[A245001] Successfully registered a request object for client (3280859750204), URI is urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4.",
"action": "CREATED",
"requestUri": "urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4",
"responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4\"}"
}
クライアントは PAR EP から返却された request_uri の値を用いて、認可サーバーに対し、ユーザーエージェントを経由して認可リクエストを送信します。その認可リクエストを受信した認可サーバーは、Authlete の /auth/authorization API に対して、以下のようにリクエストを送信します。
curl --request POST 'https://api.authlete.com/api/auth/authorization' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic *************' \
--data '{
"parameters":
"client_id=3280859750204&
request_uri=urn:ietf:params:oauth:request\_uri:UymBrux4ZEMrBRKx9UyKyIm98zpX1cHmAPGAGNofmm4
"
}'
Authlete は、従来の認可リクエストを処理する場合と同様、チケットを生成・返却します。Authlete のチケットについてはナレッジベースの記事 をご参照ください。
管理者はサービスオーナーコンソールを用いて、Authlete サービスが認可サーバーの PAR EP のバックエンドとしてどのように機能するかを設定できます。具体的には、プッシュされた認可リクエストの内容の有効期間と、PAR の使用をすべてのクライアントに必須化するかどうかです。
PAR の必須化は、Authlete サービス全体としてはそのように設定していない場合でも、クライアント単位で個別に有効化できます。開発者コンソールの「認可」タブにある、以下の設定項目を用います。
PAR EP において、認可サーバーはクライアント認証を行うかもしれません。その際のクライアント認証方法の設定はトークンエンドポイントと共通です。詳細は以下の記事をご参照ください。
クライアント認証方法の設定は開発者コンソールの「認可」タブにあります。
CLIENT_SECRET_BASIC の場合、PAR EP は HTTP Basic 認証を用いてクライアント認証を行うことになります。 認可サーバーは Authlete の /pushed_auth_req API を呼び出す際に、クライアントから提示されたクレデンシャルを、“clientId” と “clientSecret” を用いて送信します。(以下は抜粋です)
curl --location --request POST 'https://api.authlete.com/api/pushed_auth_req' \
--header 'Authorization: Basic MjQzNDE1ND*********LWtv' \
--header 'Content-Type: application/json' \
--data-raw '{
"parameters": "response_type=code%20id_token&client_id=3280859750204&redirect_uri=https%3A%2F%2Fserver.example.com%2Fcb&state=SOME_VALUE_ABLE_TO_PREVENT_CSRF&scope=openid&nonce=SOME_VALUE_ABLE_TO_PREVENT_REPLAY_ATTACK&code_challenge=GyeodZxSpq0iyjNbEQE6N96MxomMXYpYUkfuEpvQ3Js&code_challenge_method=S256",
"clientId": "3280859750204",
"clientSecret": "qfd0ScLHhD**************YDg"
}'
クライアント証明書を用いてクライアント認証を行うよう設定した場合には、認可サーバーから Authlete へのリクエストは以下のようになります。
curl --location --request POST 'https://api.authlete.com/api/pushed_auth_req' \
--header 'Authorization: Basic MjQzNDE1ND*********LWtv' \
--header 'Content-Type: application/json' \
--data-raw '{
"parameters":"response_type=code&client_id=....",
"clientId":3282602314604,
"clientCertificate":"\n-----BEGIN CERTIFICATE-----\nMIICnDCCAYSgAwIBAgIGAXqsMta0MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMM\nBGtleTEwHhcNMjEwNzE1MjIwNDEwWhcNMjIwNTExMjIwNDEwWjAPMQ0wCwYDVQQD\nDARrZXkxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh517rALrL+I9\npvOpCVr9wuJ/TE3l3CndvE9oRrU2BpBYSn0LVnIT6anKgrYSJNP/YOkgHqUQQIoq\n0j7Uv7fiYL02OuAuVouOP3pxC1QiRGNInZkmYVJ0EsNz8Gft3JW7A9pUHc/Sx0P1\nTbN1hL9J5auasCNjUhd3GCB7bEJeIlez066qkUeZR/Jtpqdh9TVJrnBjEiihrcwL\nlixo4G5Y2Tg9vpjOCKgoL1tni6wbxY64BzksF2y10OEfvcwacmLBsMxHhN3l0qU2\nnbKbYKb2R0xRq8DU5woG0Rkbi5z6FRF4DLzpjig6vk6ENjwenHFYt8XMhulmSdnX\nGvDe2/BWYQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQABRsoLK5hn5DesBpnDCBfq\nZnyMiWyUbh8qmIhO5Ta6Hq/AeUSM16gqJqBsLQm6UllfsW30Qn9EwkCMG1Fb4g8t\n5TVigtvtVcTkn3H2Ib6EhtsB5Evs1U273W5Z/y7QUDrS2TahraKNKK2k81UbHhZf\nY1qyDMTK1+a+EAcuUaFOPsOzZo3Yxa2GDXQ8ZjHwk4E7tIri953P66gGHC3GNTy\n92hrXw8KgoIXJXKyZ5WeyziTIfAypnlI6EzUU\n-----END CERTIFICATE-----"
}'
PKI に基づく TLS_CLIENT_AUTH、すなわち Authlete がクライアントの証明書チェインを検証する場合には、認可サーバーから Authlete へのリクエストは以下のようになります。
curl --location --request POST 'https://api.authlete.com/api/pushed_auth_req' \
--header 'Authorization: Basic MjQzNDE1ND*********LWtv' \
--header 'Content-Type: application/json' \
--data '{
"parameters":"response_type=code&client_id=3289644915401&...",
"clientId":3289644915401,
"clientCertificate":"\n-----BEGIN CERTIFICATE-----\nMIIDmzCCAoOgAwIBAgI.....ftMPIhU1ocI0Uh9ObkPq5atK0lx\n39OTMXLj1kHxlf3RnoRo\n-----END CERTIFICATE-----",
"clientCertificatePath":["\n-----BEGIN CERTIFICATE-----\nMIIE3TCCAsWgAwI.....9HEtxsOeIDWmILz453xtSBdorV7rN7QcEK6Hd62czruZtk/ItPjQMnB1moBT3d\n5g==\n-----END CERTIFICATE-----",
"\n-----BEGIN CERTIFICATE-----\nMIIFuDCCA6CgAwI.....FRVZqvemtV0gZM0C3tkDBQzGsb/KW\nnFWbOABBQequSMJN0MjWd+fkiDZAJq/X0Gw==\n-----END CERTIFICATE-----"]}'