Skip to content

scope と claim ​

scope と claim は OIDC を学び始めた人が最も混同する 2 語です。同じものではありません。

  • scope はクライアントが authorize 要求で 要求する もの。OP が scopes_supported で公開している小さな識別子集合の中の 1 つです。
  • claim は OP が 返す key-value のペアです。構造化された応答、たとえば ID トークン、JWT アクセストークン、/userinfo 応答の中に入ります。

scope は要求、claim は応答です。scope を grant すると、claim ソースが値を返せる範囲で、対応する claim を OP が開示できるようになります。

30 秒でつかむ要点

  • scope = 「あなたの email が見たい」 — scope=openid email
  • claim = "email": "[email protected]" — 返ってきた値。
  • 1 つの scope が複数の claim を開示することがあります。下表の OIDC 標準対応がそのマッピングです。
このページで触れる仕様

scope: クライアントが要求するもの ​

/auth の scope パラメータは、半角スペース区切りの不透明な識別子のリストです。ユーザはその集合に同意し、OP は grant された集合を認可コードに記録し、アクセストークンとリフレッシュトークンの状態へ引き継ぎます。ID トークンには標準の scope claim は入りません。OP は既定で /oidc 配下にマウントされるため、認可エンドポイントは /oidc/auth になります。

GET /auth?response_type=code&client_id=...&scope=openid%20profile%20email&...

初学者が見落としがちな点をいくつか:

  • scope 識別子は OAuth 2.0 にとって不透明。openid に特別な意味を与えるのは OIDC Core §3.1.2.1 です。それ以外の scope 名は OP とクライアントの合意や拡張仕様で定まります。
  • scope は case-sensitive。openid は scope ですが、OpenID は別物(かつ未認識の)scope です。
  • openid が OAuth を OIDC に変える魔法。openid が要求になければ ID トークンは発行されません。本ライブラリの既定は OIDC(openid scope 必須)で、op.WithOpenIDScopeOptional() で純 OAuth 2.0 に切り替えられます。
  • ユーザは一部の scope を辞退できる。grant された集合は要求された集合の部分集合になり得ます。OP は実際に grant された scope を token 応答とアクセストークンの状態に反映します。
  • クライアントは登録された scope しか要求できない。クライアントの登録 Scopes に無い scope や、AllowedClients がそのクライアントを除外している scope は、invalid_scope(RFC 6749 §5.2)で拒否されます。

claim: トークンや userinfo 応答の中の key-value ​

claim は JSON オブジェクト内の 1 フィールドです。トークンでは OP が署名し、/userinfo では既定で JSON として返ります。要求に応じて署名付きまたは暗号化された JWT になることもあります。OIDC Core §5.1 が長いカタログを提供しています。トークンの メタデータ claim とユーザ claim は分けて考える必要があります。

  • ID トークンの claim — iss、sub、aud、iat、exp は常に入ります。nonce、auth_time、acr、amr、at_hash、c_hash は、フローや要求に応じて入ります。
  • JWT アクセストークンの claim — RFC 9068 で iss、sub、aud、client_id、iat、exp、jti が必須です。scope、cnf、auth_time、acr、amr、authorization_details は条件に応じて入ります。OP 専用の private claim が 2 つあり、リソースサーバは無視してください。gid(失効に使う grant ID)と、/userinfo が読み戻す claims request の userinfo メンバである userinfo_claims です。opaque アクセストークン、リフレッシュトークン、認可コードは通信路上に claim を持ちません。
  • ユーザ claim — name、family_name、email、email_verified、phone_number、address、picture など。エンドユーザを説明する値です。grant された scope または明示的な claims request が許可し、claim ソースが値を返せる場合にだけ OP が開示します。

ユーザ claim は scope が制御し、トークンの メタデータ claim はトークンの種類とフローによって決まります。

OIDC Core の scope-to-claim 対応 ​

OIDC Core §5.4 は profile、email、address、phone に対応するユーザ claim を定めています。表には §3.1.2.1 の OIDC リクエストを示す openid と、§11 のリフレッシュトークン用同意を扱う offline_access も載せています。この 2 つは §5.4 のユーザ claim 群ではありません。

ScopeRelease される claim
openidsub
profilename、family_name、given_name、middle_name、nickname、preferred_username、profile、picture、website、gender、birthdate、zoneinfo、locale、updated_at
emailemail、email_verified
addressaddress
phonephone_number、phone_number_verified
offline_access(claim なし — リフレッシュトークン発行のためのユーザ向け同意画面と、適用されるリフレッシュトークンの TTL バケットを制御)

scope=openid email の要求は、ユーザ claim カタログから sub、email、email_verified を開示します。それ以外は何も開示しません。scope=openid profile email なら profile 群が丸ごと加わります。

この対応表だけでは、開示された claim が どこに載るか までは分かりません。通常の scope フローでは、sub と ID トークンの メタデータ claim は ID トークンに載り、profile・email・address・phone が開示するユーザ claim は /userinfo から返ります。アクセストークンには grant された scope が入り、ID トークンには標準の scope claim は入りません。

scope が claim を開示し、ID トークンと userinfo が運ぶ
各標準 scope が開示する claim 群と、その届き先を示した図。openid が開示する sub は ID トークンに載り、profile・email・address・phone の claim は userinfo エンドポイントから返ります。offline_access は claim を開示しません。要求した scope開示される claim届き先openidsubprofilename · family_name · …(14 claim)emailemail · email_verifiedaddressaddressphonephone_number · phone_number_verifiedoffline_accessclaim なし · 同意とリフレッシュの TTL を決めるID トークンiss · aud · exp · iat · subOP 自身が埋める/userinfoRP がアクセストークン付きで呼ぶ許可された scope の claim だけ返す
scope は要求であって保証ではありません。OP が許可するのは、クライアントが登録した範囲・ユーザが同意した範囲・claim ソースが実際に返せる値の重なりです。この表に載っている claim でも応答に含まれないことがあります。

ユーザ claim を ID トークンに載せたい RP は、OIDC Core §5.5 の claims パラメータで明示的に要求する必要があります。scope を許可しただけでは /userinfo 側に載ります。

op.WithIDTokenScopeClaims("profile", "email") は、指定した scope についてこの既定を変えます。それらの scope の claim が、authorization_code、refresh_token、device_code、CIBA の各 grant の ID トークンにも書き込まれます。OIDC Core §5.4 から意図的に外れる設定で、アクセストークンがリソースサーバに結び付いているために /userinfo を呼べない SPA を想定しています。custom scope も指定できますが、ID トークンが予約している claim(iss、sub、aud、iat、exp、auth_time、nonce、acr、amr、azp、at_hash、c_hash、sid)を Claims に含むものは op.New が拒否します。ユースケース: scopeを参照してください。

/userinfo が開示するのは、提示されたアクセストークンに許可された scope の claim と、そのトークンを発行した認可で claims リクエストの userinfo メンバが求めた claim です。claims リクエストはアクセストークンごとに持たれる(JWT なら userinfo_claims、opaque なら保存された行)ため、同じユーザとクライアントによる後の認可があっても、先に発行されたトークンが読める範囲は変わりません。

offline_access は claim を開示しない

offline_access は OIDC 標準 scope で、「ユーザが不在のときも動き続けたい」というクライアントの意思表明 — つまりリフレッシュトークン発行に対するユーザ向けの同意プロンプトです。ユーザ claim は何も開示しません。本ライブラリでは、結果として発行されるリフレッシュトークンを別の TTL バケット(op.WithRefreshTokenOfflineTTL)にルーティングするので、ログイン状態維持の連鎖は通常の短セッションのリフレッシュより長く保てます。詳細はリフレッシュトークン。

カスタム scope ​

組み込み側は op.WithScope で scope を追加登録できます。各 scope は op.Scope 構造体で、通信路上の識別子(Name)、同意画面に出すテキスト(Description。ユーザが拒否できない scope なら Required)、/userinfo から開示する claim 名(Claims)、discovery に公開するか(Public)、そして任意でどのクライアントが要求できるか(AllowedClients)を記述します。

go
op.WithScope(op.PublicScope("read:projects", "プロジェクトの読み取り")),
op.WithScope(op.Scope{
    Name:        "write:projects",
    Title:       "プロジェクトへの書き込み",
    Description: "あなたの代理でプロジェクトの作成・変更を行います。",
    Public:      true,
    Claims:      []string{"projects:permissions"},
    AllowedClients: []string{"trusted-client-1"},
}),

よくある用途には 2 つのヘルパが用意されています。

  • op.PublicScope(name, label) — discovery 文書の scopes_supported に公開する scope を登録します。審査済みのクライアントなら誰でも要求できる scope に使います。
  • op.InternalScope(name) — scopes_supported から 省略 する scope を登録します(RFC 8414 §2 / OIDC Discovery §3 が省略を明示的に許容)。受理判定は scope の AllowedClients リストで制御されます。discovery に載せたくないが特定クライアントには許す scope に使います。

OIDC 標準 scope は組み込みの既定で自動認識されます。標準名(例: email)に対して WithScope を呼ぶと組み込みエントリが上書きされます — 翻訳や追加の claim の対応づけを付与する目的で使うのが典型です — が、Public: false で標準 scope を登録すると op.New が失敗します。discovery 文書が「標準 scope を欠落」と公告するのを防ぐためです。

op.ParseScopeSet(raw) は組み込み側の文字列処理用ヘルパで、Unicode の空白文字の連続で分割します。通信路上の scope パースは RFC 6749 の ASCII スペース文法でより厳格なため、/auth や /par の入力で tab や改行に頼らないでください。

公開 / 内部の違い、claim の対応づけ、同意プロンプトに scope がどう表示されるかを含む詳細はユースケース: scopeを参照してください。

scope と RFC 8707 resource indicator ​

よくあるモデリングの問い: 権限を分けるとき、新しい scope を切るのか、新しい audience を切るのか?

問い答え例
「同じ RS が解釈する 異なる種類 の権限ですか?」scope を使う。同じ projects API に対する read:projects と write:projects。
「別の resource server で、独自の audience が必要ですか?」RFC 8707 を使う。projects API 用のアクセストークンと billing API 用のアクセストークン。各 RS の verifier が別 audience 向けのトークンを拒否できるよう、aud を別にしたい。
「audience ごとに寿命 / 形式 / 失効ポリシーが違いますか?」audience ごとの形式指定で RFC 8707 を使う。管理 API は opaque(即時失効)にしつつ、参照用 API は JWT(水平スケール)を維持。本ライブラリは op.WithAccessTokenFormatPerAudience で実現します。

scope は 権限の幅 を、resource indicator は 対象 audience を表現します。1 つの認可リクエストに両方を載せることもできます。例えば scope=openid read:projects&resource=https://projects.example.com と指定すると、本ライブラリは指定した resource 向けにアクセストークンを 1 つ発行します。組み込みの認可フローで resource を繰り返すと invalid_target で拒否されるため、別のリソースサーバには別のリクエストを使います。

resource パラメータを受け付けるすべてのエンドポイントは、まず同じ正規形に落としてから比較します。

ルール効果
絶対 URI が必須scheme と host を欠く値は拒否します。
scheme と host を小文字化HTTPS://API.example.com と https://api.example.com は同じ resource になります。
既定ポートを除去HTTP の :80 と HTTPS の :443 は落とし、それ以外のポートは維持します。
末尾の / を除去https://api.example.com/ は https://api.example.com になります。
query は維持query 文字列も resource の同一性の一部です。
fragment と userinfo は拒否invalid_target でリクエストを失敗させます。

許可リストとの比較や WithAccessTokenFormatPerAudience も同じ正規化を通ります。そのため、authorize、PAR、token、device authorization、CIBA、token exchange の間で同じ resource の比較結果が変わることはありません。

OIDC §5.5 の claims パラメータ(scope 単位ではなく特定の claim を細かく要求する)はユースケース: Claims リクエストパラメータに、audience ごとの形式指定のトレードオフはアクセストークン形式にあります。

次に読む ​