ユースケース — カスタムアカウント選択 UI
prompt=select_account には 2 つの関心事があります。
- セッションの意味論: ブラウザが chooser group(サインイン済みの複数アカウント)を持ち、ユーザが選んだアカウントが次の
subを決める。 - 描画面: アカウントの一覧を表示し、選ばれた
SessionIDを POST するページ。
マルチアカウント選択 は前者を扱います。このページは後者を扱います。ブランド付きでサーバ描画のアカウント選択画面を持ちつつ、state、CSRF、セッションの切り替えは OP に任せるには、op.HTMLUI{Chooser: ...} を使います。
ソース:
examples/12-custom-chooser-uiは、組み込みの HTML ページでop.HTMLUI{Chooser: ...}を使う例です。JSON ドライバ / SPA 経路はexamples/13-multi-accountと対比してください。
使いどころ
| 目的 | 使うもの |
|---|---|
| 同梱 chooser をそのまま使う | オプション不要。組み込みの HTML ページが描画 |
| chooser の HTML / 文言 / レイアウトだけ変え、サーバ描画に留める | op.WithUI(op.HTMLUI{Chooser: op.PageTemplate{Template: tmpl}}) |
| chooser を SPA の中で描画する | op.WithUI(op.SPAUI{...}) または op.WithUI(op.DriverUI{Driver: interaction.JSONDriver{}}) |
| アカウントのグループ化や切替のロジックを変える | テンプレートではなく session store / authenticator 側 |
Chooser テンプレートは意図的に狭い差し込み口です。差し替えるのはテンプレートだけで、テンプレートが任意の subject を選んだり、セッションを発行したり、OP の状態遷移を迂回したりすることはできません。自前の driver の上にテンプレートを重ねたい場合は、op.DriverUI へ、その driver を Inner にした interaction.TemplateOverlayDriver を渡します。
テンプレートの契約
テンプレートには interaction.ChooserTemplateData が渡されます。
| フィールド | 用途 |
|---|---|
Accounts | chooser group 内の有効なセッション(最後に使った順)。各要素は SessionID、Subject、DisplayName(ユーザの name claim。不明なら空)、AuthTime を持つ |
StateRef | そのまま返す不透明な interaction state 参照 |
CSRFToken | POST 時に OP が検証するトークン |
SessionIDField | 選択アカウント用に OP が期待するフォームフィールド名 |
SubmitMethod | 通常は POST |
SubmitAction | interaction エンドポイントの URL |
AddAccountURL | 同じ group に別のアカウントをサインインさせる /auth の URL。PAR が必須の配備では空 |
最小形は次のようになります。
tmpl := template.Must(template.New("chooser").Parse(`
{{range .Accounts}}
<form method="{{$.SubmitMethod}}" action="{{$.SubmitAction}}">
<input type="hidden" name="state_ref" value="{{$.StateRef}}">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<input type="hidden" name="{{$.SessionIDField}}" value="{{.SessionID}}">
<button type="submit">Continue as {{if .DisplayName}}{{.DisplayName}}{{else}}{{.Subject}}{{end}}</button>
</form>
{{end}}
{{if .AddAccountURL}}<a href="{{.AddAccountURL}}">Sign in to another account</a>{{end}}
`))
provider, err := op.New(
/* 必須オプション */
op.WithUI(op.HTMLUI{
Chooser: op.PageTemplate{Template: tmpl},
}),
)フィールド名は OP との契約です。state_ref、csrf_token、SessionIDField が指す名前(session_id)のフィールドは、送信フォームに残してください。「アカウント追加」のリンクは、AddAccountURL が空でないときだけ表示します。RequirePAR(たとえば FAPI 2.0)の配備では、クエリ文字列だけの /auth URL は拒否されるため、OP はこのフィールドを空にします。
op.PageTemplate ごとに ContentSecurityPolicy フィールドがあるので、chooser のポリシーは同意画面のものとは別に宣言します。2 つのテンプレートが別々の origin から読み込む場合があるためです。空文字は厳格な既定値(default-src 'none'; style-src 'none'; frame-ancestors 'none'; base-uri 'none')を使い、スタイルシートや画像も読み込めません。独自のポリシーは op.New の実行時に interaction.NormalizeCSP で正規化されます。form-action は拒否され、frame-ancestors と base-uri は指定する場合は厳密に 'none' でなければなりません。欠けている保護の directive は自動で追加されます。拒否されたポリシーは、interaction.ErrCSPNotPermitted で op.New を失敗させます。テンプレートに必要なアセットの directive だけを追加し、script は無効のままにしてください。
流れ
session_id を「このブラウザに今提示されたもの」だけに限定しています。テンプレートは切り替えそのものを実行しません。選ばれたセッションの識別子を返すだけです。OP はその識別子が有効な chooser group に属することを確認し、他の group のものや期限切れの session_id は 4xx で拒否します。選ばれたセッションが、その間に期限切れになったりサインアウトされたりしていた場合、RP は login_required を受け取ります。
選ばれたセッションでリクエストを処理できない場合(リクエストに prompt=login がある、セッションが max_age より古い、acr が acr_values を満たさない)、OP はリクエストを失敗させません。そのアカウントのログイン認証要素を、もう一度実行します。
選ばれたアカウントは、戻ってきたセッションと同じ LoginFlow の rule、Decider、リスクに応じたステップアップを通ります。そのため、rule や拒否の判定は、ほかのサインインと同様に選択にも適用されます。選択によって再認証が走った場合、選ばれたセッションのレコードは新しい auth_time、acr、amr でローテーションされます。
chooser の所有者
op.SPAUI を使う場合、chooser の描画は JSON の状態エンドポイントを通じて SPA が受け持ちます。op.WithUI が受け取るモードは配備ごとに 1 つなので、chooser の所有者は渡すモードで決まります。
| UI の所有者 | op.WithUI に渡す値 |
|---|---|
| OP によるサーバ描画 HTML | op.HTMLUI{Chooser: ...} |
| OP がマウントする SPA の入口 | op.SPAUI{...} |
| 自前ルータが SPA を配信 | op.DriverUI{Driver: interaction.JSONDriver{}} |
本番運用メモ
- テンプレートは、リクエストごとではなく起動時に一度だけ parse する。
- CSP は厳しく保つ。データには攻撃者が影響し得る表示名が入るので、
html/templateのエスケープに任せ、inline script を避ける。 SessionIDは不透明な値として扱う。表示したり、他のフィールドから組み立てたりしない。AddAccountURLは渡されたとおりに使う。次のサインインを既存の group に加えるための内部マーカーが付いており、手で組み立てたprompt=loginの URL は、アカウントを追加せず現在のアカウントを置き換える。
続きはこちら
- マルチアカウント選択 — chooser group の意味論と
Sessions.Switch。 - SPA / 対話画面のカスタマイズ — 同じ prompt を JSON ドライバで扱う経路。
- カスタム同意 UI — consent 向けの同等のサーバ描画テンプレート差し込み口。