概要
RFC 7523 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants の Section 2.1. Using JWTs as Authorization Grants では、RFC 6749 で定義されている OAuth 2.0 標準フロー群とは異なる、アクセストークン発行のためのフローを定義しています。我々はそれを JWT 認可グラントフローと呼んでいます。
そのフローでは、JWT (RFC 7519) が認可グラントとして使用されます。認可グラントは、その保持者がアクセストークンを取得する認可を得ていることを示すものです。認可グラントとしての JWT は、認可コードフロー (RFC 6749 Section 4.1) の認可コードと同じ概念です。
アクセストークン要求者は、トークンエンドポイント (RFC 6749 Section 3.2) で JWT を提示することによりアクセストークンを取得できます。次の図はそのフローを示しています。
本記事は Authlete 社ウェブサイト上の記事『JWT 認可グラント (RFC 7523 2.1)』の一部転載です。
仕様
JWT の出所
認可グラントとしての JWT が誰によってどのように生成されるかに関する詳細を RFC 7523 では定義していません。 従って、JWT の署名を検証するのに用いる鍵の入手方法が仕様では定義されていません。そのため、署名検証用鍵を特定するのに必要な独自規則をそれぞれの運用が定義しなければなりません。
例えば、システムは 「JWT は https://example.com
が発行した ID トークンでなければならない」 という規則を定めるかもしれません。JWT が ID トークンであれば、認可サーバーの実装は標準的な仕組み (つまりディスカバリーエンドポイントと jwks_uri
サーバーメタデータ) を使って署名検証用鍵を見つけることができます。他のシステムは、OpenID Connect Federation 1.0 のエンティティーステートメントと同じように、JWT 自身に署名検証用鍵を埋め込むことを選択するかもしれません。
いずれにしても、RFC 7523 を採用する際は、同仕様を補完するための追加規則を定義しなければなりません。
トークンリクエスト
グラントタイプ
JWT 認可グラントフローと他のフローを区別するため、新しいグラントタイプ urn:ietf:params:oauth:grant-type:jwt-bearer
が仕様で定義されています。この値はトークンリクエストの grant_type
リクエストパラメーターの値として使用されます。
クライアントの特定と認証
仕様は、トークンエンドポイントにおける クライアント認証 を要求しておらず、さらにはクライアントの特定すら要求していません。仕様は次のように述べています。
JWT authorization grants may be used with or without client authentication or identification.
JWT 認可グラントの使用に際し、クライアント認証やクライアント特定が伴いうる。
技術的には 『クライアント認証が伴いうる』 とは、クライアントアプリケーションのクライアントタイプ (RFC 6749 Section 2.1) がパブリックなのかコンフィデンシャルなのかについて仕様は気にしないということを意味しています。また 『クライアント未特定』 とは、トークンリクエストがクライアントを特定するための情報を含んでいないことを意味します (例えば client_id
リクエストパラメーターの欠如など)。
スコープ
OAuth 2.0 標準フロー群でも可能なように、JWT 認可グラントフローのトークンリクエストにおいても、要求するスコープ群を scope
リクエストパラメーターで指定することができます。
アサーション
認可グラントとして用いる JWT は assertion
リクエストパラメーターで指定されます。このリクエストパラメーターは、RFC 7521 Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants の Section 4.1. Using Assertions as Authorization Grants で定義されています。
次の表は、JWT クレーム群の要否を要約したものです。クレーム群に対する詳細な要求事項は RFC 7523 の Section 3 で説明されています。
クレーム | 要否 |
---|---|
iss |
必須 |
sub |
必須 |
aud |
必須 |
exp |
必須 |
nbf |
任意 |
iat |
任意 |
jti |
任意 |
リクエストの例
次のものは、RFC 7523 の Section 2.1 に掲載されている例です。
POST /token.oauth2 HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0.
eyJpc3Mi[...omitted for brevity...].
J9l-ZhwP[...omitted for brevity...]
トークンレスポンス
JWT 認可グラントフローのトークンレスポンスは RFC 6749 に準拠します。RFC 7523 では追加のレスポンスパラメーターは定義されていません。
唯一の注意点は、与えられた JWT が無効の際、error
レスポンスパラメーターの値として invalid_grant
を用いなければならないということです。
実装
JWT 認可グラントフローは Authlete 2.3 以降でサポートされます。
実装に関する情報については Authlete 社ウェブサイト上の記事『JWT 認可グラント (RFC 7523 2.1)』の『実装』をご参照ください。
例
認可サーバー実装の例
Java で書かれたオープンソースの認可サーバー実装サンプルである java-oauth-server の JwtAuthzGrantProcessor.java が、JWT 認可グラントフローのトークンリクエスト処理の実装サンプルとなっています。
当実装は一例に過ぎず、商用利用可能な完璧さは意図していないのでご注意ください。
リクエストとレスポンスの例
1. 何らかの方法で JWT を用意する。
$ JWT=eyJraWQiOiJhdXRobGV0ZS1mYXBpZGV2LWFwaS0yMDE4MDUyNCIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJodHRwczovL2ZhcGlkZXYtYXMuYXV0aGxldGUubmV0LyIsInN1YiI6IjEwMDQiLCJhdWQiOlsiNTg5OTQ2MzYxNDQ0ODA2MyJdLCJleHAiOjE2NTg2NzExMzAsImlhdCI6MTY1ODY3MDgzMCwiYXV0aF90aW1lIjoxNjU4NjcwODMwLCJub25jZSI6IjEyMzQ1Njc4OSIsInZlcmlmaWVkX2NsYWltcyI6eyJ2ZXJpZmljYXRpb24iOnsidHJ1c3RfZnJhbWV3b3JrIjoibmlzdF84MDBfNjNBIn0sImNsYWltcyI6eyJnaXZlbl9uYW1lIjoiSW5nYSIsImZhbWlseV9uYW1lIjoiU2lsdmVyc3RvbmUiLCJiaXJ0aGRhdGUiOiIxOTkxLTExLTA2IiwiOmFnZV8xOF9vcl9vdmVyIjpudWxsfX19.mxE8FQaDb0edY_rWasSQ7pEMXbFon7oWr-Ccv1dB15q8eh2MaKRGrgvwPw_XjAdXlMNzkcV6iEUjRUvLGTvzm7_45cdoOxRX1xWzQw-vwvRbM46xd3Yht3EVjyRUUBJ_92J1yBmu7Nn93rygcnCE-fC_bSTSIJWgEnoC7dpxHYnoJ2QHrIOYFMBAA_3ZYCLGpgiWbIZnB2D1ib2eqwJ9zoJqeFNEBhXo9ThYkASHYaG-ZWofy7364lgeV4Rqy1r4XqzchFRW4yzWs_IM72bTtXTUkstlNOxZU12KEz50uVhtcOXv06iI71I9vceRP-ZVICpq7Knt0vEKWTM41E3ziw
2. トークンリクエストを行う。
$ curl http://localhost:8080/api/token -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer -d assertion=$JWT -d client_id=5908895171 -d scope=email
3. トークンレスポンスを受け取る。
{
"access_token":"NEdL-q9EfOI4S5XzaMeimXAXVqS139Jm9DTYeLUAd5o",
"token_type":"Bearer",
"expires_in":86400,
"scope":"email"
}