Skip to content

アーキテクチャ概観

op.New(...)http.ServeMux を内部に持つ http.Handler を返します。本ページでは、リクエスト到着からレスポンスまでの間に OP が何を実行するか、関わるパッケージ、検証の順序、組み込み側が制御するストレージの差し込み口を整理します。

パッケージ構成

op/                         ← 公開 API 表面(組み込み側はここを import)
op/profile/                 ← FAPI 2.0 / 将来のプロファイル
op/feature/                 ← PAR / DPoP / mTLS / introspect / revoke / DCR / JAR
op/grant/                   ← authorization_code、refresh_token、client_credentials
op/store/                   ← Store interface(サブストアの集合)+ contract test suite
op/storeadapter/{inmem,sql,redis,composite}
op/interaction/             ← ログイン UI 用 HTML / JSON ドライバの差し込み口

internal/                   ← 外部からは import 不可(Go の可視性)
  authn/                    ← LoginFlow オーケストレータ、Authenticator runtime
  authorize、parendpoint、tokenendpoint、userinfo、
  introspectendpoint、revokeendpoint、registrationendpoint、
  endsession、backchannel
  jose、jwks、keys          ← 署名 / 検証 / 鍵セット
  jar、dpop、mtls、pkce、sessions
  cookie、csrf、cors、httpx、redact、log、metrics
  discovery、scoperegistry、timex、i18n

境界は構造的に強制されています。外部コードは internal/ に届きません。組み込み側が制御する差し込み口(オプション、store interface、authenticator、audit subscriber)はすべて op/ 配下にあります。

ハンドラグラフ

op.New*http.ServeMux を構築し、設定されたパスにハンドラをマウントします(下図はデフォルト):

feature.*PARIntrospectRevokeDynamicRegistrationBackChannelLogout)で制御されるエンドポイントは、対応する feature が有効になっているか、対応するオプション(WithDynamicRegistration など)が渡されたときだけマウントされます。discovery 文書も、実際にマウントされたエンドポイントだけを公開します。

クロスカットなミドルウェア

すべてのハンドラは以下にラップされます:

Layerソース役割
CORSinternal/corsdiscovery と /jwks は public CORS。/userinfo/token、interaction / session の JSON 面、マウント済み protocol endpoint(/par/revoke/introspect/register/bc-authorize/device_authorization/end_session など)は厳格な許可リスト
信頼プロキシinternal/httpxWithTrustedProxies を元に、X-Forwarded-* / Forwarded から実クライアント IP を解決
Cookieinternal/cookie__Host- プリフィックス、AES-256-GCM、session は SameSite=Lax、互換可能なところは Strict
CSRFinternal/csrfconsent / logout の POST に対して double-submit + Origin / Referer チェック

これらはオプションではありません。組み込み側のオプション設定に関係なく構造的に適用されます。

Authorize → token のライフサイクル

最も流量の多いパスです。概略は次のとおりです。

/par/end_session も大筋は同じ形です。上記が標準的な成功経路です。

LoginFlow の内部

WithLoginFlow(LoginFlow{...}) は構築時に内部のパイプラインへコンパイルされます:

LoginFlow {Primary, Rules[], Decider, Risk}

    ▼ (compile)
internal/authn/CompiledLoginFlow
    ├── Primary  → Authenticator(Step descriptor → runtime 実装の解決)
    ├── Rules[]  → 順序付き (When, Then) ペア
    ├── Decider  → 任意の short-circuit
    └── Risk     → 評価パスごとに 1 回呼ばれる

各 authorize リクエストでは:

  1. Primary.Begininteraction.Step(Prompt または Result)を返します。
  2. UI ドライバ(HTML または SPA)がプロンプトを描画し、ユーザが送信します。
  3. Primary.ContinueResultIdentity がバインドされている)まで進めます。
  4. オーケストレータが LoginContext を組み立てます(subject、scope、完了したステップ、リスクスコア、ACR values)。
  5. Decider が動きます(nil 以外の場合)。Pass 以外の判定はそこで短絡します。
  6. それ以外は Rules を順に評価します。最初にマッチしたルールの Step.Kind()CompletedSteps にまだ含まれていなければ発火します。
  7. 発火するルールが無くなるまで繰り返し、その後にセッションを発行します。

ExternalStep 経由で自前の factor を差し込む手順は、ユースケース: カスタム authenticator を参照してください。

ストレージの差し込み口

ライブラリは、組み込み側の users テーブルを直接読み書きしません。op.Store interface(小さなサブストアの和集合)越しに会話します:

サブストア何が入るか配置の目安
ClientsOAuth クライアントレジストリ通常は永続
Userssubject + claim組み込み側の実装。既存の users テーブルにマッピングすることが多い
AuthorizationCodesone-shot な code(PKCE challenge、scope)永続
RefreshTokensrefresh chain、ローテーション履歴永続
AccessTokensJWT id 側 / opaque token永続
OpaqueAccessTokensopaque AT lookup永続
Grants(user, client) ごとの consent scope永続
GrantRevocations失効した grant の tombstone永続
Sessionsブラウザセッションのレコード揮発に置いてもよい
Interactions試行ごとの interaction 状態揮発に置いてもよい
ConsumedJTIsJAR / DPoP jti のリプレイ検出集合揮発に置いてもよい
PARspushed authorization request揮発に置いてもよい
IATs / RATsDCR の Initial / Registration Access Token永続
EmailOTPsTOTPsPasskeysRecoveryユーザごとの MFA factor レコード永続

「揮発に置いてもよい」サブストアは composite アダプタ越しに Redis 層へ配置できます。composite は構築時に「永続バックエンドは 1 つ」を強制するので、トランザクショナルクラスタが 2 つのストアにまたがって分裂することはありません。

詳細は hot/cold ストレージ を参照してください。

Discovery 文書の組み立て

/.well-known/openid-configuration は OP の実効設定から discovery 文書を組み立てます。広告されるフィールドはそのまま OP の実挙動を表します。discovery と挙動の間に乖離はありません。理由は以下のとおりです。

  • response_types_supportedWithGrants + FAPI プロファイルから計算されます。
  • token_endpoint_auth_methods_supported は、WithProfile(profile.FAPI2Baseline) または FAPI2MessageSigning が有効なときに FAPI の許可リストと交差します。
  • scopes_supported は組み込みの scope と WithScope で登録された scope の和集合です。
  • ui_locales_supported は runtime locale resolver(seed bundle + WithLocale 追加分)から自動導出されます。WithDiscoveryMetadata(...).UILocalesSupported に非空の明示リストを渡した場合だけ、それが優先されます。
  • code_challenge_methods_supported は常に ["S256"] です。plain は構造的に存在しません。
  • request_object_signing_alg_values_supported は JOSE の許可リスト(RS256PS256ES256EdDSA)です。
  • dpop_signing_alg_values_supported はそれより狭い集合 (ES256EdDSAPS256)です。理由は FAQ § DPoP discovery を参照。

次に読む