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 §3.1.2.1 では openid が OIDC リクエストを示し、§5.4 では profile、email、address、phone がユーザ claim に対応します。配備独自の scope も定義できます。このページでは、それらを 公開 scope(Discovery に掲載し、同意画面にも表示)と 内部 scope(許可したクライアントだけが使え、Discovery には掲載しない)に分ける方法を説明します。

このページで触れる仕様
用語の補足
  • Scope と claim — scope はクライアントが /authorize に付けて どんな種類のアクセスを要求するか を表す文字列(email、billing.read など)。claim は最終的に id_token や /userinfo に乗る identity の key/value(email、email_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 標準スコープ

openid、profile、email、address、phone、offline_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_tier と billing_account_id が乗ります。UserStore(または interaction driver に組み込まれた claim resolver)が当該 subject に対してその claim 名を実際に提供している場合に限ります。

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

設定済みの scope カタログは、実行時の scope → claim 名マップに反映されます。カスタム scope が付与されると、/userinfo は Scope.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
限定許可リストで段階公開中の β scopeInternalScope + scope 自体の AllowedClients を埋める
全 scope を公開し誰でも要求できる API marketplaceすべて PublicScope