Skip to content

ユースケース — Rich authorization requests(RFC 9396) ​

scope は粗い単位です。scope=payments は このクライアントは送金してよい を表しますが、IBAN X から IBAN Y へ €40 を一度だけ動かす までは表せません。RFC 9396(Rich Authorization Requests、通称 RAR)は authorization_details パラメータを追加します。型付きオブジェクトの JSON 配列で、クライアントは要求内容を細かく記述でき、OP はその内容を grant に束ね、その grant から発行されるトークンへ引き継げます。

本ライブラリは authorization_details を /auth、/par、/token で受理し、各要素を登録済みの type で検証し、許可済みの内容を grant に永続化し、JWT アクセストークン(RFC 9068 §2.2.3)と introspection(RFC 9396 §9)に反映します。/oidc に mount した場合の認可 endpoint は /oidc/auth です。

RAR は、リソースサーバが金額、口座、location、action、datatype のような構造化された事実を見て判断する必要がある場合に使います。単に scope 名を増やしたいだけなら使わない方がよいです。read:orders や payments:initiate で十分に表せるなら、scope の方が単純で既存クライアントからも扱いやすいです。

このページで触れる仕様
  • RFC 9396 — OAuth 2.0 Rich Authorization Requests(§2 構造、§5 エラー、§9 introspection、§10 discovery)
  • RFC 9068 — JWT アクセストークン(§2.2.3 が authorization_details を保持)
  • OpenID Connect Core 1.0 — §5.5(claims request、より粗い兄弟分)
用語の補足
  • authorization_details — JSON 配列。各要素は type 文字列に加えて、type 固有のメンバ(actions、locations、datatypes など、その type が定義するもの)を持つオブジェクト。
  • type — RFC 9396 §2 の識別子(例: payment_initiation)。OP が受理するのは、登録された type だけです。未知の type は拒否されます。
  • validator — 構造の検査は OP が行います。type 以外の各メンバの 意味 は、type ごとの validator で組み込み側が検査します。

ライブラリが見る部分、組み込み側が見る部分 ​

この分担は意図的です。本ライブラリは RFC 9396 §2.1 の 構造 を担います。値がオブジェクトの JSON 配列であること、各要素が OP の認識する非空文字列 type を持つこと、リクエスト全体が控えめなサイズ上限に収まることを確認します。type 固有の事柄、たとえば payment_initiation 要素が非空の actions 配列を持つこと、金額が正であること、location がこのクライアントの触れてよいものであることは、type に登録する Validate 関数へ委譲されます。

validator なしで登録された type は、その type の下で任意の中身を受理してしまいます。そのため、op.New は nil の Validate を拒否します。

OP は外形を、組み込み側のコードは中身を見る
authorization_details の 1 要素について、OP は配列の形と type メンバーを確認し、登録した validator がそれ以外のメンバーを確認します。許可された配列はトークン応答、JWT アクセストークンの中、introspection の 3 か所に反映されます。配列のうちの 1 要素"type": "payment_initiation","actions": ["initiate"],"amount": "40.00"OP が見る部分配列の形と、type が登録済みかどうか組み込み側の Validate が見る部分それ以外のメンバー — 語彙は組み込み側のもの許可された配列は 3 か所に反映されるトークン応答authorization_details としてJWT アクセストークンRFC 9068 §2.2.3introspectionRFC 9396 §9
この分担がこの機能の要点です。OP は金額や操作が何を意味するかについて見解を持たないため、外形だけを検証し、中身は意味を知っているコードに渡します。

設定 ​

受理する type を op.WithAuthorizationDetailTypes で登録します。type を登録すると RAR が有効になり、authorization_details が 3 つのエンドポイントで受理可能になり、許可済みの内容が永続化・反映され、discovery が authorization_details_types_supported を公開します。別途有効化する feature フラグはありません。type を登録しないと、このパラメータは未知の拡張として無視されます。

go
import (
  "context"
  "errors"

  "github.com/libraz/go-oidc-provider/op"
  "github.com/libraz/go-oidc-provider/op/grant"
  "github.com/libraz/go-oidc-provider/op/store"
  "github.com/libraz/go-oidc-provider/op/storeadapter/inmem"
)

st := inmem.New()
loginFlow := op.LoginFlow{Primary: op.PrimaryPassword{Store: st.UserPasswords()}}

op.New(
  op.WithIssuer("https://op.example.com"),
  op.WithStore(st),
  op.WithKeyset(myKeyset),
  op.WithCookieKeys(myCookieKey),
  op.WithLoginFlow(loginFlow),
  op.WithGrants(grant.AuthorizationCode, grant.RefreshToken),
  op.WithAuthorizationDetailTypes(op.AuthorizationDetailType{
    Type: "payment_initiation",
    Validate: func(_ context.Context, el map[string]any, _ *store.Client) error {
      if _, ok := el["actions"].([]any); !ok {
        return errors.New("payment_initiation requires an actions array")
      }
      return nil
    },
  }),
)

Validate は復号済みの要素(el["type"] は登録した Type と一致済み)と、リクエストの属する認証済み *store.Client を受け取ります。そのため validator は、クライアントに権限のない location などを拒否できます。el を変更してはなりません。nil 以外のエラーを返すと、リクエスト全体を invalid_authorization_details で拒否します。

各 Type は非空かつ一意である必要があり、重複は構築時に拒否されます。呼び出しを繰り返すと追記されるので、基本セットに配備固有の上書きを重ねられます。

discovery 文書はこう公開します:

json
{
  "authorization_details_types_supported": ["payment_initiation"]
}

動作確認 ​

クライアントは authorization_details を URL エンコードした JSON 配列として送ります:

sh
DETAILS='[{"type":"payment_initiation","actions":["initiate"]}]'
curl -G --data-urlencode "authorization_details=$DETAILS" \
  --data-urlencode 'response_type=code' \
  --data-urlencode 'client_id=demo' \
  --data-urlencode 'redirect_uri=https://rp.example.com/callback' \
  --data-urlencode 'scope=openid' \
  --data-urlencode 'code_challenge_method=S256' \
  --data-urlencode "code_challenge=$CHALLENGE" \
  https://op.example.com/oidc/auth

同じパラメータは PAR の push でも受理されます。ユーザが同意すると、token レスポンスに許可された内容が入ります。

json
{
  "access_token": "...",
  "token_type": "Bearer",
  "authorization_details": [{"type": "payment_initiation", "actions": ["initiate"]}]
}

JWT アクセストークンは同じ配列を authorization_details claim として保持し(RFC 9068 §2.2.3)、トークンを introspect すると introspection レスポンスにもその配列が返ります(RFC 9396 §9)。そのためリソースサーバは、JWT を自分でパースしても OP に問い合わせても、許可された内容にたどり着けます。

認可コード、リフレッシュトークンのチェーン、アクセストークンは、それぞれを発行した認可の detail を持ちます。同じユーザとクライアントによる後の認可が、すでにあるトークンに detail を足すことはなく、Grant Management の replace が opaque アクセストークンとリフレッシュトークンのチェーンから取り除けるのは detail だけです(Grant management を参照)。

token エンドポイントでの扱い ​

/token の authorization_details も、登録済みの同じ type で検証されますが、権限を広げることはありません。

リクエストauthorization_details の効果
authorization_code または refresh_token絞り込みです。要求した各要素は、その認可で許可された要素と deep-equal でなければならず、そうでなければ invalid_authorization_details で失敗します。省略すると、許可された内容がすべて保たれます。
client_credentials検証を通った要素が、要求どおりにトークンへ載ります。ユーザも同意の記録もないため、クライアントが何を要求できるかを検査するのは Validate 関数だけです。

再同意と detail の適用範囲 ​

OP が既存の grant を同意画面なしで再利用するのは、要求されたすべての scope がすでに grant にあり、要求されたすべての authorization_details 要素が grant の要素と deep-equal の場合だけです。新しい要素や、amount: "100" → "999" のように値が変わった要素があると、ユーザは再び同意画面を通ります。通常の同意が済むと、その要素が grant に追加されます。prompt=consent は、両方の集合が含まれていても同意を強制します。ファーストパーティのクライアントに対する同意の省略は、authorization_details を含むリクエストには適用されません。

組み込みの同意ステップが表示するのは scope だけです。要素ごとの表示や承認はありません。ユーザは scope のチェックを外せますが、要求された detail は、ユーザが何を承認しても、まとめて保存され、そのまま反映されます。金額や受取人のような内容を見せる必要がある場合は、自前の同意 UI で表示してください。要素そのものを拒否したければ、Validate で行います。

エラー ​

条件エラー
値がサイズ上限を超えているinvalid_request
値がオブジェクトの JSON 配列でない、または要素に登録済みの type がないinvalid_authorization_details
登録済み type の Validate が nil 以外のエラーを返すinvalid_authorization_details
/token で、要求した要素が許可された集合にないinvalid_authorization_details

RAR と claims パラメータ ​

どちらも粗い scope では言い表せない内容を扱いますが、答える問いが違います。claims パラメータ は、id_token や userinfo の どこにどの claim を載せるか を要求します。authorization_details は、リソースサーバ上で アクセストークンが何をしてよいか を記述します。1 つのリクエストに両方を含めることもできます。

続きはこちら ​