Skip to content

使い方 — カスタムアカウント選択 UI

prompt=select_account には 2 つの関心事があります。

  • セッションの意味論: ブラウザが複数の有効アカウントを含む chooser group を持ち、選択されたセッションが次の sub を決める
  • 描画面: アカウント一覧を表示し、選択された SessionID を POST するページ

マルチアカウント選択 は前者を扱います。このページは後者、つまりブランド付きのサーバ描画のアカウント選択画面を持ちつつ、state、CSRF、最後の Sessions.Switch は OP に任せるための op.WithChooserUI を扱います。

ソース: examples/12-custom-chooser-ui は、既定の HTML interaction ドライバで op.WithChooserUI を使う例です。JSON ドライバ / SPA 経路は examples/13-multi-account と対比してください。

使いどころ

目的使うもの
同梱 chooser をそのまま使うオプション不要。既定 HTML ドライバが描画
chooser の HTML / 文言 / レイアウトだけ変え、サーバ描画に留めるop.WithChooserUI(op.ChooserUI{Template: tmpl})
chooser を SPA の中で描画するop.WithSPAUI または interaction.JSONDriver
アカウントのグループ化や切替のロジックを変えるテンプレートではなく session store / authenticator 側

WithChooserUI は意図的に狭い差し込み口です。差し替えるのはテンプレートだけで、テンプレートが任意の subject を選んだり、セッションを発行したり、OP の 状態遷移 を迂回したりする経路ではありません。

テンプレートの契約

テンプレートには interaction.ChooserTemplateData が渡されます。主なフィールドは次の通りです。

フィールド用途
Accountschooser group 内の有効セッション。SessionID、subject、表示ラベル、auth time などを含む
StateRefそのまま返す不透明な interaction state 参照
CSRFTokenPOST 時に OP が検証するトークン
SessionIDField選択アカウント用に OP が期待するフォームフィールド名
SubmitMethod通常は POST
SubmitActioninteraction endpoint URL
AddAccountURL別アカウント追加のために prompt=login 経路を開始する URL

最小形は次のようになります。

go
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 {{.DisplayName}}</button>
  </form>
{{end}}
<a href="{{.AddAccountURL}}">Sign in to another account</a>
`))

provider, err := op.New(
  /* 必須オプション */
  op.WithInteractionDriver(interaction.HTMLDriver{}),
  op.WithChooserUI(op.ChooserUI{Template: tmpl}),
)

フィールド名は OP との契約です。state_refcsrf_token、動的な SessionIDField は送信フォームに残してください。

Flow

カスタム選択 UI UI のフロー: ブラウザが prompt=select_account を要求し、OP が chooser group を読み込んでテンプレートを描画し、送信を検証してからセッションを切り替え RP にリダイレクトする流れ。ブラウザOPchooser テンプレート1GET /authorize · prompt=select_account2chooser group を読み込む3ChooserTemplateData を描画4アカウント一覧 + CSRFToken + StateRef5POST SubmitAction · session_id6CSRFToken + StateRef を検証7Sessions.Switch(group, session_id)8code 付きで RP に 302

テンプレートは切替そのものを実行しません。選択されたセッション識別子を OP に返すだけです。

SPA interaction との優先関係

op.WithSPAUI を使う場合、chooser の描画は JSON の状態取得を通じて SPA が受け持ちます。WithSPAUIWithChooserUI が同時に設定されている場合、SPA 経路が優先され、chooser テンプレートは起動時の警告付きで無視されます。配備ごとに UI の所有者を 1 つに絞ってください。

UI の所有者オプション
OP によるサーバ描画 HTMLop.WithChooserUI
OP がマウントする SPA の入口op.WithSPAUI
自前ルータが SPA を配信op.WithInteractionDriver(interaction.JSONDriver{})

本番運用メモ

  • テンプレートは起動時に一度だけ parse し、リクエストごとに parse しない。
  • CSP は厳しく保つ。テンプレートデータには RP 由来の client 表示名などが入り得るため、html/template のエスケープに乗せ、インラインスクリプトを避ける。
  • SessionID は不透明な値として扱う。OP はそれが有効な chooser group に属するかを検証する。
  • 「アカウント追加」リンクは提供された AddAccountURL を使う。そうすれば次のログインが既存 chooser group に加わる。

続きはこちら