Skip to content
go-oidc-provider
Main Navigation go-oidc-provider とは
ここから始める
構成を選ぶ
インストール
最小構成 OP
必須オプション
ルータへのマウント

概要

読む順番

設定の基礎

OAuth 2.0 / OIDC 入門
issuer / 発行者
Redirect URI
クライアント種別
scope と claim
Discovery
JOSE 入門(JWS / JWE / JWK / JWKS / kid)

基本フロー

認可コードフロー + PKCE
リフレッシュトークン
Client Credentials

トークン

ID トークン / アクセストークン
アクセストークンの形式(JWT と opaque)

セッションと同意

セッションとログアウト
同意

送信者制約トークン

送信者制約(DPoP / mTLS) — 選び方
DPoP
mTLS

発展トピック

Device Code(RFC 8628)
CIBA
ブラウザを使わないフロー(CIBA と Device Code)
Token Exchange(RFC 8693)
JARM(署名付き認可レスポンス)
FAPI 2.0 入門

ヘルプ

用語集
FAQ
一覧

基本構成

最小構成の OP と RP のペア
総合バンドル

プロファイル / フロー

セキュリティプロファイルの宣言
FAPI 2.0 Baseline
FAPI 2.0 Message Signing
サービス間通信(client_credentials)
OAuth 2.0(openid なし)
DPoP nonce フロー
Protected resource メタデータ(RFC 9728)
API を守る(リソースサーバ)

認可グラント

Device Code(RFC 8628)
CIBA ポーリング方式
トークン交換(RFC 8693)
JWT bearer(RFC 7523)
ID-JAG(実験的)
カスタム grant

暗号化 / subject

Pairwise subject
JWE 暗号化

UI

SPA / 対話画面のカスタマイズ
カスタムログイン UI
カスタム同意 UI
カスタムアカウント選択 UI
マルチアカウント選択
SPA 向け CORS
i18n / ロケール

ストレージ

ストレージ構成の選び方
SQL ストア
DynamoDB ストア
Hot / Cold 分離(Redis 揮発)
ストアを自前実装

スコープ / claim

Public / Internal スコープ
Claims リクエスト
Rich authorization requests(RFC 9396)
Grant management
MCP 認可サーバ

認証

MFA / ステップアップ
アカウントページ
パスワードレス passkey
既存ユーザストアの投影
bcrypt パスワードの移行
カスタム authenticator

ガバナンス

ファーストパーティ同意スキップ
クライアントオンボーディング
動的クライアント登録
バックチャネルログアウト

運用

Prometheus メトリクス
概要
本番投入チェックリスト
鍵ローテーション
JWKS エンドポイント
マルチインスタンス展開
可観測性
バックアップ / DR
ストレージの保守
Shared Signals

標準

RFC 対応一覧
OFCS 適合状況
OFCS 再現レシピ

セキュリティ

セキュリティ方針
設計判断
CVE 回帰マトリクス
時刻ずれとリプレイ猶予
脆弱性報告
オプション一覧
エラーカタログ
監査イベントカタログ
アーキテクチャ概観
リリースノート
変更履歴

日本語

English

日本語

English

Appearance

Sidebar Navigation

概要

読む順番

設定の基礎

OAuth 2.0 / OIDC 入門

issuer / 発行者

Redirect URI

クライアント種別

scope と claim

Discovery

JOSE 入門(JWS / JWE / JWK / JWKS / kid)

基本フロー

認可コードフロー + PKCE

リフレッシュトークン

Client Credentials

トークン

ID トークン / アクセストークン

アクセストークンの形式(JWT と opaque)

セッションと同意

セッションとログアウト

同意

送信者制約トークン

送信者制約(DPoP / mTLS) — 選び方

DPoP

mTLS

発展トピック

Device Code(RFC 8628)

CIBA

ブラウザを使わないフロー(CIBA と Device Code)

Token Exchange(RFC 8693)

JARM(署名付き認可レスポンス)

FAPI 2.0 入門

ヘルプ

用語集

FAQ

On this page

同意 ​

同意(consent) とは、ユーザが「このアプリにこの scope を渡してよい」と明示する意思表示です。OIDC Core 1.0 §3.1.2.4 はタイミングを定めておらず、情報を渡す前に OP が認可の判断を得ることだけを要求しています。本ライブラリは、クライアントが scope 集合を初めて要求したときに同意画面を出し、その回答を覚えます。

このページで触れる仕様
  • OpenID Connect Core 1.0 — §3.1.2.4(同意)、§3.1.2.1(prompt)
  • RFC 6749 — §3.3(scope)、§6(refresh)
  • RFC 7009 — トークン失効(§2.1 のカスケード)

メンタルモデル

  • grant とは「ユーザ U がクライアント C に scope 集合 S を認めた」という記録で、(subject, client_id) の組で引く。
  • 既存の grant でカバーされない scope を要する authorize フローの最初の 1 回で 同意画面 が表示される。
  • 同じ scope 集合内に収まる以降のフローは、プロンプトを スキップ する。
  • 新しい scope の追加(scope の差分)は、その差分だけを尋ねるプロンプトを再度呼び出す。
  • grant を失効させると記録が削除され、次の authorize フローでは再びプロンプトが出る。

同意画面が表示されるタイミング ​

OP は authorize フローのたびに、図の順で次の 4 つのケースを評価します。

同意画面は 3 つの条件のどれかで表示される
同意画面を出すかどうかを 3 つの判定で決める図。prompt=consent の明示、この subject と client_id に対する grant が無い、要求 scope が既存 grant の範囲外、のいずれかで表示します。どれにも当たらなければ既存 grant が要求をカバーしているため、プロンプトなしで認可コードを発行します。リクエストが次を含むprompt=consentはい強制的な再確認grant があっても同意画面を出すいいえ次の組に対する grant が存在しない(subject, client_id)はい初回の grant同意画面を表示し、同意範囲を記録するいいえ要求 scope が既存 grant の範囲外既存 grant ではカバーできないはいscope の差分全体ではなく差分だけ同意を求めるいいえ既存 grant が要求をすでにカバーしているプロンプトなしで認可コードを発行し、そのままリダイレクトで返す
scope を見るのは 3 つ目の判定だけです。運用中の統合に scope をひとつ足すと全ユーザに同意画面が再表示されるのはこのためで、リクエストが既存の grant に収まらなくなり、OP は同意を得ていない範囲へ grant を広げないからです。
ケース挙動
初回 grant — その (subject, client_id) ペアに対する Grant が存在しない。プロンプト。ユーザは scope の全リストを目にします。送信時、本ライブラリは承認された scope を含む Grant 行を書き込みます。
scope の差分 — Grant は存在するが、要求された scope 集合に「過去に承認されていない scope」が含まれる。プロンプト。差分が強調表示されます。ユーザは差分のみ承認、差分を拒否、全体を拒否のいずれかを選べます。承認時に行が更新されます。
強制再プロンプト — リクエストが prompt=consent を持つ。grant の状態に関係なくプロンプト。OIDC §3.1.2.1 が要求する挙動です。
既存 grant が要求をカバー — Grant 行がすべての要求 scope と、要求されたすべての authorization_details 要素をカバーしており、prompt が省略、none、または login(consent ではない)。スキップ。本ライブラリは認可コード発行に直接進みます。スキップ専用の監査イベントはありません。

grant は同意画面から作られるものに限りません。device code、CIBA、token exchange の引き換えでも、対象クライアントに対する store.Grant が記録されます。/auth はこの grant も同意として扱うため、同じ scope を求める後続の要求では同意画面が出ません。

同意をスキップしても、認証ルールはスキップされません。既存セッションからコードを発行する前に、/auth は LoginFlow のルールと Decider を、チェーンが Primary を完了した直後であるかのように評価します。RuleClient や RuleScope による step-up が該当すれば、その step を先に実行します。prompt=none の場合は代わりに interaction_required を返します。この判定は、grant がカバーしている経路と、後述のファーストパーティのスキップの両方に適用されます。

prompt=none、prompt=login、prompt=consent の違い
  • prompt=none — 「ユーザがログイン済みで同意も整っているなら、フローを静かに完了させる。さもなくばエラー(login_required、consent_required、interaction_required)を返す」。SPA の静かな再認証で使う。
  • prompt=login — 「有効なセッションがあっても、ユーザに再認証させる」。これだけでは同意の再プロンプトは強制されない。
  • prompt=consent — 「grant が scope をカバーしていても、同意画面を強制する」。RP が機微な操作の前にユーザに再確認させたいときに有用。

本ライブラリは OIDC Core 1.0 §3.1.2.1 に沿ってこれらを適用します。prompt=none と prompt=login の併用は invalid_request で拒否します。

終端 subject に対するカバー判定の再確認 ​

最初のカバー判定は authorize 開始時点で参照できる subject を使います。prompt=login、max_age の期限切れ、RFC 9470 の step-up では、その後の認証で別の subject に結び付くことがあります。アカウント選択画面によるアカウント切替も同様です。認可コードを発行する前に、本ライブラリは要求された scope と authorization_details を終端 subject 自身の grant に対してもう一度判定します。その grant が要求をカバーしなければ、最初の subject の grant で認可することはなく、終端 subject に対する同意を取得するか、コードを発行せずにフローを終了します。

同意のみで発行する場合の認証コンテキスト ​

認証要素を実行せず、有効なセッションが残っている場合、認可コードと grant はそのセッションの auth_time、acr、amr を保持します。同意画面への回答時刻で auth_time を置き換えたり、空の認証要素の集合で acr や amr を消したりしません。セッションの期限切れ、別タブでのログアウト、アカウント切替などで有効な認証コンテキストが残っていない場合は、存在しない認証コンテキストを作らず login_required で拒否します。

ファーストパーティクライアント ​

組み込み側によっては、主力 web クライアント、社内管理コンソール、自社モバイルアプリのような信頼された少数のアプリを運用しており、同意画面がユーザの手間にしかならないことがあります。op.WithFirstPartyClients(ids ...string) は、指定した client_id について、Grant の有無にかかわらず同意画面を スキップ します。列挙した ID は op.WithStaticClients でも登録されている必要があります。スキップの対象は静的シードで登録したクライアントと管理者が作成したクライアントだけで、動的登録されたクライアントには適用されません。

スキップが働くのは、次の条件がすべて満たされるときだけです。

  • ユーザにすでにセッションがある、または要求が先にサインインを必要とする(セッションなし、prompt=login、max_age、ACR の step-up)。後者では、grant はそのサインインで確定した subject に紐づきます。スキップが subject を新たに作ることはありません。アカウント選択(prompt=select_account)と新規登録(prompt=create)では画面を出します。
  • 要求が prompt=consent を持たない。prompt=consent は常に画面を出します。
  • scope に offline_access を含まず、authorization_details も Grant Management の操作も含まない。これらは常に明示的な画面を出します。
  • ブラウザが Sec-Fetch-Site: same-origin を送る、または same-site で Origin か Referer が redirect_uri の origin と一致する。cross-site のナビゲーションではスキップしません。

あわせて 2 点あります。

  1. スキップは記録される。 本ライブラリは Grant 行を通常通り書き込み、audit.ConsentGrantedFirstParty を発火します。「このユーザは結局どの scope を承認したのか」という監査の問いには答えられます。変わるのは、ユーザが画面を見たかどうかだけです。
  2. FAPI 2.0 プロファイルでは使えない。 WithFirstPartyClients を profile.FAPI2Baseline や profile.FAPI2MessageSigning と併用すると、op.New が失敗します。

完全な設定(操作者側の scope カタログを含む)は ファーストパーティ同意スキップ を参照。

ファーストパーティは操作者が宣言するもので、ユーザが宣言するものではない

ユーザは同意フローで「これはファーストパーティアプリです」とは見せられません。信頼の根拠は、操作者が op.WithFirstPartyClients(...) に client_id を列挙していることです。コードパスをエンドツーエンドで管理していないクライアントを列挙してはなりません — そうするとスキップが「告知のない scope 付与」に変わってしまいます。

同意 UI の呼び出し方 ​

プロンプトが必要になると、本ライブラリは組み込み側の同意テンプレート(op.HTMLUI の Consent フィールドを op.WithUI に渡して登録)に制御を渡します。テンプレートが HTML をレンダリングし、本ライブラリが state、CSRF、__Host-oidc_csrf の double-submit cookie、永続化を担当します。

go
op.WithUI(op.HTMLUI{
    Consent: op.PageTemplate{
        Template: myConsentTemplate, // *template.Template
    },
})

テンプレートには、クライアント(ID、表示名、ロゴなどのクライアントメタデータ)、要求 scope(既存 grant に対する差分があればそれも)、フォームに埋め込む CSRF トークンが渡されます。Required が付いた scope はユーザが外せません。PageTemplate.ContentSecurityPolicy は同意ページの CSP を指定します。既定ではすべてのサブリソースを禁止するため、スタイルシート、ロゴ、フォントを読み込むテンプレートは、その origin を宣言してください。フォームの POST を受けて、本ライブラリは CSRF を検証し、承認された scope 集合を解析し、Grant 行を書き込み、authorize フローを継続します。改竄された送信や古い送信には 4xx を返し、画面をキャンセルすると access_denied でリダイレクトします。

RFC 9396 の authorization_details は要素の完全一致でカバー判定します。新しい要素やメンバの変更があれば、scope 集合が既にカバーされていても同意を求めます。組み込みの同意契約が扱うのは scope の承認で、detail ごとの承認・絞り込み UI はありません。通常の同意が完了すると、要求された detail は grant と一緒に永続化されます。

クライアント側で同意を描画する SPA(ログインフォームをすでに扱っている React アプリなど)では、SPA の入口と静的アセットも OP にマウントさせたい場合に op.WithUI(op.SPAUI{...}) を使います。このモードではブラウザは LoginMount/{uid} に着地し、SPA はプロンプトの状態を LoginMount/state/{uid} から取得します。SPA 本体を自前のルータで配信する場合は、低レベルな op.WithUI(op.DriverUI{Driver: interaction.JSONDriver{}}) 経路を使います。この場合、状態取得エンドポイントは /interaction/{uid} のままです。

WithUI が受け取るモードは 1 つなので、SPA と同意テンプレートは組み合わせではなく択一です。route 形と使い分けは SPA のカスタム対話画面 を参照してください。

同意の失効 ​

ユーザまたは運用者は、いつでも同意を失効させられます。経路は 3 つあり、影響の範囲が異なります。

経路失効させるものgrant の記録
トークン 1 つを指定した /revoke(RFC 7009)そのトークン。リフレッシュトークンなら、そのローテーション連鎖全体と、grant に紐づくアクセストークン。grant の別のリフレッシュ連鎖と未引き換えのコードは有効なまま。残る。次の authorize フローは引き続き同意をスキップする。
Grant Management の失効(grant に対する DELETE)grant に紐づくすべてのアクセストークンとリフレッシュトークン。削除される。次の authorize フローはカバーする grant を見つけられず、再び同意を求める。
Provider.RevokeSubject(ctx, subject)subject が持つすべての grant について、上と同じ。ユーザの無効化やプロビジョニング解除向け。削除される。

3 つは同じ失効処理を共有するため、JWT アクセストークン、opaque アクセストークン、リフレッシュトークンは入口によらず同じ方法で失効します。device code、CIBA、token exchange の引き換えで発行されたトークンは grant の配下にあるため、これらの経路でも失効します。カスタム grant が発行した認証情報は、ハンドラが grant を記録しない限り対象外です。

Grant Management による失効が成功すると grant_management.revoked を発火します。一般的な grant / トークン失効に対して専用の consent.revoked イベントは発火しません。end-session カスケード(セッションとログアウト)も同じ失効処理を使いますが、grant の記録は残します。

監査イベント ​

同意フローは、op.WithAuditLogger 経由で 2 つの同意関連監査イベントを発火します。

イベント発火タイミング
audit.ConsentGrantedユーザが同意フォームを送信し、Grant 行が書き込まれた。
audit.ConsentGrantedFirstPartyファーストパーティの自動同意が適用された(プロンプトは出していない)。

audit.ConsentGranted は scope や authorization_details の要素を追加する通常の再同意も表します。本ライブラリは audit.ConsentGrantedDelta、audit.ConsentSkippedExisting、audit.ConsentRevoked を発火しません。Grant Management による失効の成功は audit.GrantManagementRevoked で報告されます。

発火する同意イベントを組み合わせると、SOC ダッシュボードで「ユーザが明示的に同意画面をクリックした」と「操作者が事前承認した静かな経路でこのクライアントが同意を得た」を区別できます — 特定の Grant 行がなぜ存在するのかを振り返る際に有用です。

次に読む ​

  • ユースケース: ファーストパーティクライアント — 操作者側の scope カタログ、op.WithFirstPartyClients、監査の方針。
  • ユースケース: カスタム同意 UI — op.HTMLUI{Consent: ...} の設定、テンプレート契約、CSRF の扱い。
  • リファレンス: 監査イベント — 本ライブラリが発火するすべての監査イベントと付随する追加フィールド。
Pager
Previous pageセッションとログアウト
Next page送信者制約(DPoP / mTLS) — 選び方

a personal project by libraz