Skip to content

アーキテクチャ概観

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

パッケージ構成

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 document も、実際にマウントされたエンドポイントだけを公開します。

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

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

Layerソース役割
CORSinternal/cors/token/userinfo/revoke/introspect には厳格な許可リスト。/jwks と discovery は public CORS
信頼プロキシ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 も大筋は同じ形です。上記が標準的な happy path です。

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 または React)がプロンプトを描画し、ユーザが送信します。
  3. Primary.ContinueResult(Identity がバインドされている) まで進めます。
  4. オーケストレータが LoginContext を組み立てます(subject、scope、完了したステップ、リスクスコア、ACR values)。
  5. Decider が走ります(non-nil の場合)。Pass 以外の判定は short-circuit します。
  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 の replay set揮発に置いてもよい
PARspushed authorization request揮発に置いてもよい
IATs / RATsDCR の Initial / Registration Access Token永続
EmailOTPsTOTPsPasskeysRecoveryユーザごとの MFA factor レコード永続

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

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

Discovery document の組み立て

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

  • response_types_supportedWithGrants + FAPI プロファイルから計算されます。
  • token_endpoint_auth_methods_supported は、WithProfile(profile.FAPI2Baseline) または FAPI2MessageSigning が有効なときに FAPI の許可リストと交差します。
  • scopes_supported は組み込みの scope と WithScope で登録された scope の和集合です。
  • code_challenge_methods_supported は常に ["S256"] です。plain は構造的に存在しません。
  • request_object_signing_alg_values_supported は JOSE の許可リスト(RS256PS256ES256EdDSA)です。
  • dpop_signing_alg_values_supported はそれより狭い集合 (ES256EdDSAPS256)です。理由は FAQ § DPoP discovery を参照。

次に読む