Skip to content

ユースケース — Public / Internal スコープ

そもそも scope とは

OAuth 2.0(RFC 6749 §3.3)における scope は、クライアントが /authorize 要求に付けて どんな種類のアクセスを要求するか を表す文字列です(例: email / profile / billing.read)。OP が許可する scope を決め、アクセストークンに許可された scope が乗り、リソースサーバが API 認可判断に使います。

OIDC Core 1.0 §5.4 は特殊な scope として openid(リクエストを純 OAuth から OIDC に切り替えるスイッチ)と、ユーザ claim 用の profile / email / address / phone を予約しています。それ以外は OP 独自のカタログ であり、本ページではこのカタログを Public(discovery で広告 + 同意画面に描画)と Internal(許可リスト経由でだけ発行・discovery には出さない)に分割する方法を扱います。

このページで触れる仕様
用語の補足
  • Scope と claimscope はクライアントが /authorize に付けて どんな種類のアクセスを要求するか を表す文字列(emailbilling.read など)。claim は最終的に id_token/userinfo に乗る identity の key/value(emailemail_verified など)。scope は粗い単位で、1 scope は claim の に対応します。claim 単位の細かい制御は Claims リクエストパラメータ を参照。
  • scopes_supported — OP が公開する scope を列挙する discovery フィールド。RP はこれを見て要求可能な scope を知ります。掲載されない scope でも発行は可能です — scopes_supported公開リスト であって 認可 ではありません。

ソース: examples/60-scopes-public-private

分離の宣言

op.PublicScope(name, label)op.InternalScope(name)Scope 値を返すコンストラクタです。op.WithScope に 1 つずつ渡します:

go
op.WithScope(op.PublicScope("billing.read",  "請求履歴の閲覧")),
op.WithScope(op.PublicScope("billing.write", "請求書の管理")),
op.WithScope(op.InternalScope("internal:audit")),

PublicScope:

  • scopes_supported に掲載されます。
  • 同意画面にラベルが表示されます。
  • 自身の seed の Scopes に列挙した任意の登録 RP に発行できます。

InternalScope:

  • scopes_supported には 掲載されません
  • 同意画面にも 表示されません(OP は同意フェーズをスキップします)。
  • 受理可否は Scope.AllowedClients で制御されます。空のリストはどの RP からも要求可能、要素を持つリストはそのリストに掲載されたクライアントのみ受理し、それ以外は RFC 6749 §5.2 の invalid_scope で拒否されます。
  • OIDC 標準の scope 名で InternalScope を作ろうとすると op.New が拒否します。discovery 文書が OIDC Discovery 1.0 §3 に違反しないようにするためです。

OIDC 標準スコープ

openidprofileemailaddressphoneoffline_access は組み込みデフォルトで自動登録されます。明示宣言は不要 — 例は OP 独自の scope カタログに焦点を当てています。

カスタム claim を Scope.Claims で開示する

カスタム scope は、Scope.Claims スライスに claim 名を列挙することで、その scope が /userinfo で開示する claim を宣言できます。scope が granted されると、OP は標準 OIDC マッピングと並んで、宣言された claim 名を /userinfo 応答に乗せます:

go
op.WithScope(op.Scope{
    Name:        "billing.read",
    Title:       "請求情報",
    Description: "請求履歴の閲覧",
    Claims:      []string{"billing_tier", "billing_account_id"},
    Public:      true,
}),

クライアントに billing.read が付与されると、/userinfo 応答には profile / email 等が開示するものに加えて billing_tierbilling_account_id が乗ります。UserStore(または interaction driver に組み込まれた claim resolver)が当該 subject に対してその claim 名を実際に提供している場合に限ります。

カスタム scope は宣言済 claim を開示します

設定済みの scope カタログは、実行時の scope → claim 名マップに反映されます。カスタム scope が付与されると、/userinfoScope.Claims に宣言された claim 名を自動で開示します。ただし、その subject に該当 claim の値が存在する場合に限ります。

OIDC 標準 scope は影響を受けません

profile / email / address / phone は OIDC Core §5.4 で定義されている claim マッピングをそのまま保ちます。Scope.Claims はカスタム scope のための hook です — 通常運用で標準 scope の claim 集合を上書きする必要はありません。標準 scope の Public: true を外すと op.New が拒否します。

クライアント単位の許可リスト

クライアントの Scopes に scope を追加します:

go
op.WithStaticClients(
  op.PublicClient{
    ID:           "billing-app",
    RedirectURIs: []string{"https://billing.example.com/callback"},
    Scopes:       []string{"openid", "billing.read"},
  },
  op.PublicClient{
    ID:           "audit-dashboard",
    RedirectURIs: []string{"https://audit.example.com/callback"},
    Scopes:       []string{"openid", "internal:audit"},
  },
)

クライアントが許可リストに無い scope を要求すると invalid_scope で拒否されます — カタログとクライアント許可は AND で評価されます。Internal scope は加えて Scope.AllowedClients との AND が掛かるため、その allow list に無いクライアントは scope を自身に列挙していても拒否されます。

確認

sh
curl -s http://localhost:8080/.well-known/openid-configuration | jq .scopes_supported
# [
#   "openid", "profile", "email", "address", "phone", "offline_access",
#   "billing.read", "billing.write"
# ]

internal:audit が含まれない — それが目的です。

いつ使うか

シナリオ選択
同じ OP に社内管理アプリを乗せ、ユーザ向け discovery には隠したいInternalScope
限定 allow-list でロールアウト中の β scopeInternalScope + scope 自体の AllowedClients を埋める
全 scope を公開し誰でも要求できる API marketplaceすべて PublicScope