Skip to content

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

Device Code(RFC 8628)CIBA(OpenID Connect Client-Initiated Backchannel Authentication 1.0)は、同じ大きな状況に対して仕様が用意している 2 つの grant です。状況とは「アクセストークンを欲しがるデバイスがまともなブラウザを持てない」こと。スマート TV、ゲーム機、CLI ツール、IoT、音声アシスタント、POS 端末、コールセンタの操作画面、ユーザの代わりに動くサーバ側プロセス、などです。

遠目に見ると 2 つのフローは同じ形に見えます — 「2 つの面が OP で合流して、ユーザはスマホで承認する」。実際には違います。両者は 誰がリクエストを起こすかユーザがどう OP に識別されるか で分かれており、その 1 点が他のほぼ全ての違い(通信路上のエンドポイント、ポーリングの主体、フィッシング対策の主軸、適用される規制プロファイル)を決めています。

本ページは選択ガイドです。仕様としての動き方はDevice CodeCIBAの各ページに置いてあります。

1 段落ずつでまとめる

Device Code(RFC 8628). ブラウザを持たないデバイスが OP に「使い切りのコードをくれ」と頼み、自分の画面に表示し、ユーザに「この URL をスマホで開いて、このコードを入力して」と告げます。ユーザは手元のブラウザで認証して承認します。その間デバイスは /token を poll し続け、承認が降りるのを待ちます。ユーザの識別は フローの途中で発覚 します — TV の前に誰がやって来るかは OP もデバイスも事前には知りません。

CIBA(Core 1.0). RP はすでにユーザが誰かを知っています — login_hint[email protected]、口座番号など)、id_token_hint(過去に発行された ID トークン)、login_hint_token(上流のシステムが発行した署名付き JWT)のいずれかで。RP は OP に「このユーザを別経路で認証してくれ」と頼みます。組み込み側の通知・認証デバイスサービスがユーザへ通知し、OP はリクエストを保持して RP の poll に応答します。本ライブラリは poll 方式だけを実装し、ping / push 配信方式は提供しません。ユーザの識別は RP が事前に与えるもの なので、配備側は承認先の認証デバイスを決められます。

比較

観点Device Code(RFC 8628)CIBA(Core 1.0)
発端ブラウザを持たないデバイス(入力デバイス → OP)RP / API クライアント(RP → OP)
ユーザの識別ユーザが別のブラウザに user_code を入力RP が login_hint / id_token_hint / login_hint_token を事前に渡す
ユーザ側の端末ユーザが手元に持っている任意のブラウザ配備側が事前登録した別経路承認用デバイス
フィッシング対策の主軸デバイスが表示する user_code + ユーザが verification URI のホストを目視ユーザの認証デバイスに表示する binding_message
ブラウザの関与あり(ユーザのスマホ側)任意 / 不要(組み込み側の認証デバイスが確認する)
ポーリング主体ブラウザを持たないデバイスRP
仕様上のエンドポイント/device_authorization/bc-authorize
token grant_typeurn:ietf:params:oauth:grant-type:device_codeurn:openid:params:grant-type:ciba
主な用途TV アプリ、CLI ツール、kiosk、音声アシスタント、入力手段が乏しい IoT強力な顧客認証(PSD2 風)、金融 / ヘルスケアでの別経路承認、画面共有なしでアクセスをリセットするカスタマーサポートのフロー
本ライブラリでの対応RFC 8628 — op.WithGrants(grant.DeviceCode)op.WithDeviceCodeGrant() で有効化OIDC CIBA Core 1.0 — poll モードのみ。Discovery も poll だけを広告し、ping / push は実装しない
ブルートフォース対策op/devicecodekit の opaque attempt-key limiter + レコード単位ロックアウト(MaxUserCodeStrikespoll-abuse ロックアウト — auth_req_id 単位で /token 再試行を rate-limit、閾値超過で AuditCIBAPollAbuseLockout
FAPI プロファイルとの関係FAPI 2.0 には組み込まれていない(RFC 8628 自体で Baseline 相当の構成は十分)FAPI-CIBA — FAPI 2.0 Baseline / Message Signing とは別プロファイル。JAR + DPoP | mTLS + access TTL 10 分上限を必須化

2 つの grant、同じ症状

両方の grant が存在するのは、正攻法の authorization_code + PKCE フローがそもそも「token を欲しがるデバイスにまともなブラウザがある」前提で組まれているからです。その前提は TV、CLI、IoT、音声アシスタント、ユーザの代わりに動くバックエンドサービス、では成り立ちません。RFC 8628 と CIBA は「ブラウザがどこか別の場所にある」という状況の、異なる 2 つの形 を解いています。

どちらを選ぶか — 判定木

順番に 4 つの問いを通ってください。最初の問いでだいたい片付きます。

CIBA か Device Code か — 4 つの問い
CIBA とデバイス認可グラントのどちらを選ぶかを、4 つの問いを順にたどって決める判定木。RP がユーザを既に知っているか、デバイスに画面があるか、ユーザが認証デバイスを事前登録しているか、別経路での承認が規制要件かを順に見ます。RP はどのユーザかを既に知っているかフロー開始前に login_hint を渡せるはいCIBAリダイレクトなしで OP がユーザに到達できるいいえトークンを求めるデバイスに画面があるか短いコードと URL を表示できる程度でよいはいDevice Codeユーザがコードを別画面へ持っていくいいえユーザは認証デバイスを事前登録しているかOP が到達方法を既に知っているスマホなどいいえDevice Code通知先が無いので、ユーザ側から来てもらうはい規制下の金融・ヘルスケアで別経路承認が要件か取引に使う端末から離れた場所で承認する必要があるはいCIBAbinding_message が 2 つの端末を結び付けるいいえ既定は Device Code要求するものが少なくて済みます。hint resolver も通知チャネルの運用も要りません
分かれ目は、どちらから話しかけるかです。Device Code はユーザ側に OP を探してもらう方式で、CIBA は事前に伝えられた端末へ OP から働きかける方式です。後者はその通知チャネルを自分で運用している場合にだけ成立します。

1. RP はフロー開始 にユーザが誰かを知っているか?

  • はい → CIBA。RP はすでに login_hint(または ID トークン、hint token)を持っており、/bc-authorize に乗せて送れます。ユーザは自分を識別するための入力をしません。
  • いいえ → Device Code。ユーザは verification ページでサインインすることでフローの中で自分を識別し、OP はそこでユーザが誰かを知ります。

2. token を欲しがるデバイスに画面はあるか?

  • ある → Device Code がそのまま user_codeverification_uri を表示できます。TV / ゲーム機 / CLI の典型ケース。
  • ない(音声アシスタント、ヘッドレス IoT 等) → どちらでも動きます。Device Code は TTS で user_code を読み上げる、verification_uri_complete を QR で出す等の方法が取れます。CIBA は別の認証デバイスを使い、組み込み側の通知サービスが承認 prompt を届けるため、利用デバイス自体の表示は不要です。

3. ユーザは認証デバイスを事前登録しているか?

  • CIBA は前提とします。 事前登録された認証デバイスがないと、組み込み側の承認サービスは届け先を持ちません。そのデバイスを事前登録する工程(銀行アプリ、スタッフのスマホ、規制当局が発行する authenticator、等)は配備の一部です。
  • Device Code は前提としません。 ユーザがサインインできる任意のブラウザセッションで動きます。本人のスマホ、同僚のラップトップ、店舗の kiosk、いずれでも。

4. これは規制下の金融 / ヘルスケアで、別経路承認が要件になっているか?

  • CIBA は元々その目的で設計されています。 FAPI-CIBA プロファイルは CIBA Core の上に JAR、送信者制約付きトークン(DPoP または mTLS)、access TTL 10 分上限を上乗せします。binding_message は規制当局が見る audit の根拠(「ユーザは何を承認したのかを正確に見ていた」)です。
  • Device Code は汎用的な仕組みです。 適切に組めば十分機能しますが、規制当局が SCA で名指ししてくる形にはなっていません。

判定木を抜けてもまだ迷うなら、既定としては「画面はあるがブラウザがない」一般消費者向けには Device Code、「RP がユーザを把握済みで、別経路で承認だけしてほしい」業務向けには CIBA を選んでください。

シーケンス図

Device Code(RFC 8628)

Device Code(RFC 8628)
デバイス認可グラント。ブラウザを持たないデバイスが device_code と短い user_code を要求し、コードと URL を画面に表示し、ユーザが別のブラウザで承認する間トークンエンドポイントを poll します。ブラウザの無いデバイスTV · CLI · IoTOPgo-oidc-providerユーザのブラウザスマホでもラップトップでもよい1POST /device_authorization2200 · { device_code, user_code, verification_uri }3デバイスが user_code と開く URL を表示するparloop · interval 秒ごと4POST /token · grant_type=…:device_code5400 · authorization_pending6verification_uri を開き、user_code を入力し、ログインして承認する7POST /token — 次の poll8200 · { access_token, id_token? }
デバイスとブラウザは互いに接続されていません。両者の間を移動するのは user_code だけで、それを運ぶのは片方の画面を読んでもう片方に入力する人間です。

CIBA(Core 1.0、poll モード)

CIBA(OIDC Core 1.0、poll モード)
CIBA の poll モード。RP が backchannel authentication エンドポイントで login hint を渡し、OP がそれを subject に解決し、組み込み側の通知サービスがユーザの事前登録デバイスへ届け、RP は承認が届くまで poll します。RP / API クライアントPOS · コールセンタ操作画面OPgo-oidc-providerユーザの認証デバイス事前登録済み1POST /bc-authorizelogin_hint=alice · binding_message2HintResolver → subject3組み込み側の通知サービスが届ける「Acme Coffee で 800 円を承認?」4200 · { auth_req_id, expires_in, interval }parloop · interval 秒ごと5POST /token · grant_type=…:ciba6400 · authorization_pending7ユーザが認証デバイス上で承認するApprove(auth_req_id)8POST /token — 次の poll9200 · { access_token, id_token, refresh_token? }
上の図との違いは手順 3 です。ここでは OP からユーザに働きかけるため、解決できる hint と送信できるチャネルの両方が必要になります。手順 5 から 9 の poll ループはどちらの方式でも同じです。

形は頭の中でマッピングできる程度には似ています。決定的な違いは斜めに伸びる矢印です。Device Code ではユーザが コードを手に OP まで歩いてくる のに対し、CIBA では OP が ユーザがすでに信頼しているデバイスに手を伸ばす

脅威モデルの並列比較

Phishing — 攻撃者がユーザを騙して 攻撃者の リクエストを承認させる.

  • Device Code: ユーザは入力する URL のホストを目視で確認します。user_code 自体に session 単位の秘密値はありません(エントロピは意図的に控えめ)。ユーザがホストを打ち間違えたり phishing メールのリンクを踏んだりすれば、同じ user_code が攻撃者のサイトでも通ってしまいます。
  • CIBA: binding_message/bc-authorize に同梱され、ユーザの認証デバイスに表示されます。ユーザは「Acme POS 端末 #14: Acme Coffee で 800 円を承認?」を見てから承認します。文脈のない別経路確認画面(「異常なアクティビティを検知しました、承認しますか?」)が失敗モードです。

user_code への replay / brute-force.

  • Device Code: user_code はユーザが手で入力できるよう短く(BDWP-HQPK 等)作られています。原理的には総当たり可能です。本ライブラリは op/devicecodekit を同梱しており、最初の手入力は raw user_code ではない opaque なサーバ側 ceremony key と VerifyUserCodeByAttemptKey で検証し、その後 ApproveUserCode または DenyUserCode を呼びます。レコード単位の helper は MaxUserCodeStrikes(既定 5)も適用します。組み込み側が用意する verification ページは 必ず このヘルパを経由してください。
  • CIBA: ユーザが手で打つコードはありません。auth_req_id は不透明値で OP が発行します。

/token ポーリングへの replay / abuse.

  • 両フローとも、ユーザが判断中は authorization_pending を、interval より速く poll してきたら slow_down を返します。RFC 8628 §3.5 は新しい interval の遵守を必須としており、OP は LastPolledAt と原子的に値を永続化するため、複数レプリカ構成で値をリセットされる経路はありません。
  • CIBA はさらに poll-abuse ロックアウトを持ちます。auth_req_id 単位の違反カウンタが閾値を超えると、リクエストは reason="poll_abuse" で拒否され、audit catalogue に AuditCIBAPollAbuseLockout が記録されます。

本ライブラリの現状の対応

Device Code(RFC 8628). フル対応、op.WithGrants(grant.DeviceCode)op.WithDeviceCodeGrant() で有効化します。verification ページ(ユーザが user_code を入力する画面)は 組み込み側がホスト します。最初の手入力は raw user_code ではない opaque なサーバ側 ceremony key と devicecodekit.VerifyUserCodeByAttemptKey で検証し、その後 ApproveUserCode / DenyUserCode を呼びます。devicecodekit.Deps は pointer のまま保持・受け渡しし、既定 limiter はプロセス内限定です。複数インスタンスでは共有 atomic AttemptLimiter を注入します。Audit は Deps.AuditLogger 経由です。Audit catalogue:

  • AuditDeviceAuthorizationIssuedAuditDeviceAuthorizationRejectedAuditDeviceAuthorizationUnboundRejected
  • AuditDeviceCodeVerificationApprovedAuditDeviceCodeVerificationDeniedAuditDeviceCodeUserCodeBruteForce
  • AuditDeviceCodeTokenIssuedAuditDeviceCodeTokenRejectedAuditDeviceCodeTokenSlowDown
  • AuditDeviceCodeRevoked(公開 Revoke ヘルパから発火)

CIBA(Core 1.0). poll モードのみで、Discovery も poll だけを広告します。ping / push 配信方式は提供しません。op.WithGrants(grant.CIBA)op.WithCIBA(op.WithCIBAHintResolver(...)) で組み込みます。HintResolver は組み込み側のフックで、受信した hint(login_hintid_token_hintlogin_hint_token)を subject に解決します。id_token_hint は OP が署名・issuer・認証済みクライアント audience を検証し、検証済み sub だけを resolver に渡します。exp は意図的に検証せず、pairwise client は resolver 前に拒否します。Audit catalogue:

  • AuditCIBAAuthorizationIssuedAuditCIBAAuthorizationRejectedAuditCIBAAuthorizationUnboundRejected
  • AuditCIBAPollAbuseLockout
  • AuditCIBATokenIssuedAuditCIBATokenRejectedAuditCIBATokenSlowDown
  • AuditCIBAPollObservationFailed(token endpoint がきれいに反映できない状態遷移を観測したとき)

認証デバイスの callback は組み込み側が CIBA substore を直接呼ぶ経路です。その呼び出しに対して本ライブラリは AuditCIBAAuthDeviceApproved / AuditCIBAAuthDeviceDenied を発火しません。これらの名前は配備側 telemetry 用のカタログです。

続きはこちら