JWT 認可グラント (RFC 7523 2.1)


title: JWT認可グラント (RFC 7523 2.1) description: RFC 7523 セクション 2.1 で定義される JWT 認可グラントと Authlete の実装についての説明。 date: 2024-11-26T16:51:41.331Z group: standards weight: 25 image: img/developers/jwt_authorization_grant/jwt_authorization_grant.png

概要

RFC 7523 JSON Web Token (JWT) プロファイルセクション 2.1. JWT を認可グラントとして使用する では、RFC 6749 で定義された OAuth 2.0 標準フローとは異なるアクセストークン発行のフローが定義されています。このフローを JWT認可グラント フローと呼びます。

このフローでは、JWT (RFC 7519) を 認可グラント として使用します。これは、JWT の保有者がアクセストークンを取得する権限を持っていることを示します。JWT を認可グラントとして使用する という概念は、認可コードフロー (RFC 6749 セクション 4.1) における 認可コード と同じです。

アクセストークンを取得するには、リクエスターが トークンエンドポイント (RFC 6749 セクション 3.2) に JWT を提示します。以下の図は、このフローを示しています。

仕様

JWT のソース

RFC 7523 は、JWT を認可グラントとしてどのように、誰が生成するかの詳細を定義していません。 そのため、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 認可グラントは、クライアント認証または識別の有無に関わらず使用することができます。

技術的に言うと、“クライアント認証の有無” とは、クライアントアプリケーションの クライアントタイプ (RFC 6749 セクション 2.1) が パブリックコンフィデンシャル かを気にしないことを意味し、“クライアントの識別なし” とは、トークンリクエストにクライアントを識別する情報 (例: client_id リクエストパラメータの欠如) が含まれないことを意味します。

スコープ

JWT 認可グラントフローのトークンリクエストには、標準の OAuth 2.0 フローと同様に、scope リクエストパラメータを含めてリクエストするスコープを指定することができます。

アサーション

JWT を認可グラントとして使用する場合、RFC 7521セクション 4.1 に定義されている assertion リクエストパラメータを使用します。

以下の表は、JWT に含めるべきクレームの要否を示したものです。各クレームの詳細な要件については、RFC 7523セクション 3 を参照してください。

クレーム 必須か否か
iss 必須
sub 必須
aud 必須
exp 必須
nbf 任意
iat 任意
jti 任意

リクエストの例

以下は、RFC 7523 セクション 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[...省略...].
J9l-ZhwP[...省略...]

トークンレスポンス

JWT 認可グラントフローにおけるトークンレスポンスは、RFC 6749 に準拠しています。RFC 7523 により追加されるレスポンスパラメータはありません。

唯一の注意点として、提供された JWT が無効な場合、error レスポンスパラメータの値として invalid_grant を使用しなければなりません。

実装

JWT 認可グラントフローは、Authlete 2.3 以降でサポートされています。

/auth/token API のレスポンス

JWT 認可グラントフローをサポートするために、新しい actionJWT_BEARER が追加されました。トークンリクエストの grant_type リクエストパラメータの値が urn:ietf:params:oauth:grant-type:jwt-bearer であり、Authlete サーバー側での基本的な検証ステップを通過すると、Authlete の /auth/token API のレスポンスにおける action パラメータの値として JWT_BEARER が設定されます。

認可サーバーの実装では、JWT 認可グラントフローをサポートするために JWT_BEARER アクションを処理する必要があります。以下は、authlete-java-jaxrs ライブラリの TokenRequestHandler.java から抜粋した switch 文の例であり、JWT_BEARERcase エントリが含まれています。

// アクションに応じて処理を分岐
switch (action)
{
    case INVALID_CLIENT:
        // 401 Unauthorized
        return ResponseUtil.unauthorized(content, CHALLENGE);

    case INTERNAL_SERVER_ERROR:
        // 500 Internal Server Error
        return ResponseUtil.internalServerError(content);

    case BAD_REQUEST:
        // 400 Bad Request
        return ResponseUtil.badRequest(content);

    case PASSWORD:
        // "リソースオーナーパスワードクレデンシャル" フローの処理
        return handlePassword(response);

    case OK:
        // 200 OK
        return ResponseUtil.ok(content);

    case TOKEN_EXCHANGE:
        // トークン交換リクエスト (RFC 8693) の処理
        return handleTokenExchange(response);

    case JWT_BEARER:
        // JWT 認可グラント (RFC 7523) を使用するトークンリクエストの処理
        return handleJwtBearer(response);

    default:
        // あり得ないケース
        throw getApiCaller().unknownAction("/api/auth/token", action);
}

以下の表は、/auth/token API のレスポンスにおいて JWT 認可グラントに関連するパラメータを示しています。

レスポンスパラメータ 説明
assertion string トークンリクエストの assertion リクエストパラメータの値。
scopes string array トークンリクエストの scope リクエストパラメータの値。

リクエストの検証

トークンリクエストのグラントタイプが urn:ietf:params:oauth:grant-type:jwt-bearer の場合、Authlete (具体的には、Authlete の /auth/token API の実装) は以下の検証ステップを順番に実行します。そのため、認可サーバーの実装では、同じ検証ステップを省略できます。

No. 検証内容
1 assertion リクエストパラメータが指定されており、その値が空でないことを確認する。
2 JWT のフォーマットが RFC 7519 JSON Web Token (JWT) に準拠していることを確認する。
3 JWT が暗号化されているかどうかを確認し、(a) jwtGrantEncryptedJwtRejected フラグが true の場合はリクエストを拒否する、(b) false の場合は残りの検証をスキップする。
4 JWT に iss クレームが含まれており、その値が JSON 文字列であることを確認する。
5 JWT に sub クレームが含まれており、その値が JSON 文字列であることを確認する。
6 JWT に aud クレームが含まれており、その値が JSON 文字列または JSON 文字列の配列であることを確認する。
7 サービスの発行者識別子 (issuer サーバーメタデータ) またはトークンエンドポイントの URL (token_endpoint サーバーメタデータ) が aud クレーム内に含まれていることを確認する。
8 JWT に exp クレームが含まれており、現在時刻が exp で示される時刻を超えていないことを確認する。
9 JWT に iat クレームが含まれている場合、現在時刻が iat で示される時刻以上であることを確認する。
10 JWT に nbf クレームが含まれている場合、現在時刻が nbf で示される時刻以上であることを確認する。
11 JWT が署名されているかを確認し、(a) jwtGrantUnsignedJwtRejected フラグが true の場合はリクエストを拒否する、(b) それ以外の場合は検証を終了する。

設定

JWT 認可グラントに関連するオプションを設定するには、以下の手順に従ってください。

  1. Authlete Management Console にログインします。
  2. Service Settings > Endpoints > Token > JWT Authz Grant に移動します。

次のオプションが設定可能です。

設定オプション 説明
Client ID クライアント識別子を含まないトークンリクエストを拒否するかどうかを設定します。
Encrypted JWT 暗号化された JWT を認可グラントとして使用するトークンリクエストを許可するか拒否するかを設定します。
Unsigned JWT 署名なしの JWT を認可グラントとして使用するトークンリクエストを許可するか拒否するかを設定します。

認可サーバーの実装例

JwtAuthzGrantProcessor.java は、java-oauth-server という 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"
}