OAuth 2.0 for First-Party Applications
OAuth 2.0 for First-Party Applications (draft-parecki-oauth-first-party-apps-01)
概要
OAuth 2.0 でアクセストークンを得る方法として最も一般的なものに認可コードフローがある。これはブラウザを用いてリソースオーナーと認可サーバーがやり取りする必要があり、これはクライアントがネイティブアプリの場合には (アプリに閉じた体験にならないことから) あまり良くない体験であると思われる。
そこで本仕様ではクライアントが First-Party のネイティブアプリの場合に、クライアントが直接ユーザー (リソースオーナー) とやりとりして認可サーバーの Authorization Challenge Endpoint (本仕様で導入される) へ通知し、そのレスポンスとして認可コードを得る手順が定められている。
この仕様はクライアントが First-Party である場合、すなわち認可サーバーとクライアントが同一の Entity により運営されており、それがユーザーにとってもわかるような場合にのみ利用可能であり、それ以外の場合には用いてはならない。
なお、First-Party アプリが複数あり、それらが同一の端末にインストールされていることが期待される場合は、各アプリでこの仕様を実装するのではなく OpenID Connect Native SSO を実装する方が、ユーザーによるインタラクションが 1 回で済むことから望ましい。
以下に処理のフローを示す。
Authorization Challenge Endpoint
Authorization Challenge Request
クライアントは認証等に必要な情報 (パスワードや Passkeys のレスポンス、MFA コードなど) をアプリ上で収集して Authorization Challenge Endpoint へ送る。
このエンドポイントは POST リクエストにのみ対応し、リクエストボディの Content-Type は application/x-www-form-urlencoded
である。当該クライアントがトークンエンドポイントでクライアント認証を求めるものの場合、当エンドポイントでもクライアント認証を行う。また、リクエストボディのパラメータとしては認可エンドポイントのすべてのリクエストパラメータをサポートするので、PKCE[1] や Resource Inditacot [2]、OpenID Connect などを利用することも可能である。ただし response_mode=query
など本仕様においては無意味なパラメータが指定された場合に認可サーバーがどのように振る舞うべきであるかは本仕様では示されない。
本エンドポイントではリクエストパラメータとして以下の追加 / 仕様変更がある。また、必要に応じて独自のリクエストパラメータ (パスワード、Passkeys のレスポンスなどのためのもの) を導入してもよい。
parameters | description |
---|---|
client_id | クライアント認証をせず、かつ auth_session を指定しない場合は必須 |
acr_values | 任意。認証コンテキストリファレンス[3] |
auth_session | それまでの Authorization Challenge Response ですでに auth_session を受け取っていれば必須 |
以下は Section 5.1 に記載のリクエストの例である。
POST /authorize HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
login_hint=%2B1-310-123-4567&scope=profile
&client_id=bb16c14c73415
レスポンス
成功レスポンス
このとき認可コードが返却される。以下はレスポンスの例である。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
{
"authorization_code": "uY29tL2F1dGhlbnRpY"
}
ブラウザを用いた通常の認可コードフローに乗せたい場合
このとき Pushed Authorization Request (PAR)[4] の Pushed Authorization Request Endpoint における request_uri
が返却される。この request_uri
はAuthorization Challenge Request で指定していたリクエストパラメータに紐づく。PAR では request_uri
は有効期限があったが本仕様では今のところ記載がない。
request_uri
はクライアントにとってはオペークな値で、 urn:ietf:params:oauth:request_uri:<reference-value>
の形式である。
以下はレスポンスの例である
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-cache, no-store
{
"error": "redirect_to_web",
"request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c",
"expires_in": 600
}
クライアントはこの request_uri
パラメータを用いて認可エンドポイントにリクエストすることで、通常のブラウザにおける認可コードフローを開始する。以下は認可リクエストの例である。
GET /authorize?client_id=s6BhdRkqt3&request_uri=urn%3Aietf%3Aparams
%3Aoauth%3Arequest_uri%3A6esc_11ACC5bwc014ltc14eY22c HTTP/1.1
Host: as.example.com
エラーレスポンス
仕様にはそのように記載されてはいないが、エラーレスポンスは RFC 6749 Section 5.2 で示されるトークンエンドポイントのエラーレスポンスと同様であるが、以下のパラメータが新規に追加される。
parameter | description |
---|---|
auth_session | クライアントはここで受け取った値を以降の Authorization Challenge Request で指定しなければならない。これによって以降の Authorization Challenge Request を関連付けることができる。これはクライアントにとってはオペークな値であり、Authorization Challenge Response によって更新される場合もある[5]。 |
認可サーバーはクライアントに要求する情報がある場合は、その内容に応じて error
レスポンスパラメータを自由に定義して返却しなければならない。
トークンエンドポイント
トークンエンドポイントで認可コードと引き換えにアクセストークンや ID トークンを得る。ここでトークンリクエストは RFC 6749 Section 4.1.3 で定義されたものと同様だが、redirect_uri
は指定しない。
レスポンスも RFC 6749 Section 4.1.4 (成功レスポンス) や同 Section 5.2 (エラーレスポンス) で定義されたものと同様だが、リクエストパラメータとして auth_session
が追加される。
以下は Section 6.1 に記載の成功レスポンスの例である。
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"auth_session": "uY29tL2F1dGhlbnRpY"
}
以下は同様にエラーレスポンスの例である。
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store
{
"error": "authorization_required",
"auth_session": "uY29tL2F1dGhlbnRpY"
}
所感
さすがにパスワード認証でこれを使うのはどうかと思うが、Passkeys 認証ではこの仕様を使うことでアプリでの認証体験を向上させることができそう。