OIDC Client Initiated Backchannel Authentication Flow (CIBA)とは - 概要編

ritouです。

今回ご紹介する仕様は、OpenID Connect の Client Initiated Backchannel Authentication Flow(CIBA) でございます。

openid.net

OpenIDファウンデーションの MODRNA WG でただいま絶賛 Public Review 中の一品であります。

サクッと終わるだろと思ってましたが書いてるうちに細かいリクエストのところで盛り盛りになってしまったので、分割して今回は概要編みたいな感じにします。

概要

既存の OIDC の認証フローはユーザーのブラウザを介して RP -> OP -> RP といういわゆる OAuth Dance のために HTTP リダイレクションを利用します。 3-legged な OAuth Dance の感じを示すとこんな感じです。

f:id:ritou:20181229000232p:plain

  1. RP上で「OPのアカウントでログイン」を選択
  2. RPからOPにリダイレクト
  3. OP上で認証、データアクセスの許可
  4. RPにリダイレクト
  5. RPはOPから各種トークン取得

シーケンスでいうとこんなです。

f:id:ritou:20181229000342p:plain

いつものやつですね。

それに対して、今回紹介する CIBA の認証フローでは次のようになります。

  • RP はなんらかの方法(仕様の対象外)でユーザーの識別子を知る
  • ユーザーの識別子を知っている RP が OP と直接通信してトークンを要求、取得する
  • 認証結果の受け渡しに関するユーザーの許可などについては、スマートフォンなどで OP とやり取りする

これだけではわからんと思いますが、なーんとなく、CIBAの認証フローに近くてイメージしやすそうなのは、Google に自分のスマートフォンを利用してログインする機能でしょう。 実際は CIBA とは異なりますが、Google 内部で RP=Youtube, OP=Google みたいな立ち位置にすると、近くなるかもしれません。

f:id:ritou:20181229004954p:plain

  1. RP上でユーザー識別(例:メアド入力)
  2. RPはOPに対してこのユーザーの認証を要求
  3. 手元のスマートフォン + OP上でユーザー認証、データアクセスの許可
  4. RPはOPから各種トークン取得

今のは仕様に書いてなくて私が説明のために考えた例ですが、仕様に記載されているユースケースを見てみましょう。

ユースケースと用語

例として、次の3つが記載されています。後の説明のためにちょっと順番を変えています。

  • ユーザーのスマートフォンで POS 端末の支払い
  • 銀行窓口での顧客認証
  • コールセンターにおける発信者認証

ソーシャルログインなどに代表される OIDC の認証フローの説明では UserAgent(エンドユーザー), RP, OP の3者が登場人物となるユースケースが一般的です。 しかし、このユースケースにおける

  • POS 端末を操作している人
  • 銀行窓口の担当者
  • コールセンターの担当者

という人たちは、実際に認証とアクセス許可を行う UserAgent(エンドユーザー) と同一人物ではなさそうです。

CIBA ではこの人たちが利用しているのを RP 、操作している端末などを Consumption Device (CD)スマートフォンなどユーザーが認証/認可処理を行う端末を Authentication Device (AD) と呼びます。

OIDC では CD = AD = UserAgent と言えるでしょう。 CIBA では CDAD が別でも大丈夫です。 ということは、システム的/物理的に離れていてもユーザー識別がうまくてきればユーザー認証結果を取得して利用できて、「これは良いのではないか」って感じになってきたんじゃないでしょうか?

おまけで、現在のなんたらPayブームに乗って、仮に Ponta の ID と決済できるとこのIDが紐づいてたら

コンビニ店員「Pontaカードカモン」
客「はい」
店員「XXX円です」
客「CIBA Pay(ダッサ)」
店員「はい(ポチ)」
客のスマホ「(XXX円をローソンに支払います。よろしいですか?)」
客「おk」
店員「支払いおわた。レシートどぞ」

ぐらいは出来そうです。

OAuth 2.0 Device Flow との違い

そういえば、OAuth 2.0 には Device Flow ってのがあります。 Device Flow はテレビやスマートスピーカーのような、エンドユーザーとの対話機能に制限があるデバイス上で動作する Client(RP) の 認可処理を別の端末で行うための仕様です。

qiita.com

CIBA とあえて比較するとしたら、

  • CD , AD の分離
    • Device Flow は既存の OAuth Dance の RP -> OP にリダイレクトするタイミングでQRコードやURL表示などで手元のスマートフォンなどに誘導し、 CDAD が分離
    • CIBA は最初から CDAD が分離していて、 OP が AD を用いてユーザーと対話
  • ユーザー識別
    • Device Flow では OP の所でユーザー認証するので事前のユーザー識別は不要
    • CIBA では Client が OP に「このユーザーを認証してください」と要求し、OP は紐づく AD を特定 するため、事前のユーザー識別が必要

という感じで、完全に CIBA が Device Flow を置き換えるような感じではなさそうです。 互いに適切な用途があると思うのでこの辺りの導入を検討する際は気をつけましょう。

概要編の最後に、ざっくりと CIBA の認証フローを見ていきます。

認証フロー概要

CIBA では次のような認証フローが定義されています。

  1. Client は OP の Backchannel Authentication Endpoint にユーザー識別子などを含む HTTP POST を送ってエンドユーザーの認証を要求
  2. OP はバックグラウンドで認証を試みる間、その認証に一意にひもづく識別子を返す
  3. ClientID Token, Access Token, Refresh Token(optional) を受け取る。その方法について、Poll, Ping, Push というモードがある

モードによって最後のトークン取得方法が異なります。

  • Poll : Client は OP の Token Endpoint をポーリングして応答を受け取る
  • Ping : 事前に Client が登録しておいた Callback URI に対して OP が認証の識別子と共に通知用のリクエストを送る。ClientToken Endpoint からトークンを取得する。
  • Push : 事前に Client が登録しておいた Callback URI に対して OP がトークンを含むリクエストを送る。

Poll モードについては Device Flow と似ています。

Ping / Push モードでは OP から RP へのリクエストも考慮されています。 ということで、このモードを使う場合、RP にも新たなエンドポイントが必要となりますね。

次回予告

ここからの詳細の説明、ちょっと長くなるので次回にします。 また、Device Flow の時もそうだったんですが、こういう飛び道具系の仕組みはセキュリティ面で色々考えるところがあるので仕様の Security Considerations, Privacy Considerations のあたりも見ていきましょう(今回のに含めたかったけどパラメータについての言及があったのでやめます)。

CIBAについてもっと詳しく聞きたい?

誰が詳しいの?と CIBA OIDC あたりでググった結果...

f:id:ritou:20181229192822p:plain

1個めが仕様、その外はこれ全部くどーさんですね。 ということで、CIBAについてすでに情報を発信しており、私よりも熱い思いを持つ人、くどーさんに聞けばいいじゃない。ソレガイイ、ソレガイイ。

私も行きます。行けたら。

参考になりそうなスライド

www.slideshare.net

www.slideshare.net

おまけ : 発音

そういえば、今回初めて CIBA を知った方、ここまで脳内でどう発音してきましたか? 親切な仕様だと発音についての記載があったりしますが、まずは文章やスライドだけ読むマンにとって、OAuth、JOSE、CBORなど、発音に困ることが多々あります。

さすがくどーさん親切ですね。

ではまた!

OAuth 2.0 の Implicit grant 終了のお知らせ

f:id:ritou:20181112110600j:plain

おはようございます。 月曜です。 ritouです。

先週、こんな記事出てました。

Why you should stop using the OAuth implicit grant!

ということらしいです。

Implicit grantといえば Token Replace Attack や Covert Redirect など、OAuth 2.0の脆弱性を語る上で欠かせない唯一無二の存在であります。

www.atmarkit.co.jp

私の中では最初から非推奨ですが、公式にとなると気になりますね。

策定中のドキュメントではどう書いているかというと

The implicit grant (response type “token”) and other response types causing the authorization server to issue access tokens in the authorization response are vulnerable to access token leakage and access token replay …

In order to avoid these issues, Clients SHOULD NOT use the implicit grant and any other response type causing the authorization server to issue an access token in the authorization response.

OAuth 2.0 の Implicit Grant では Access Token そのものの値を fragment の値に指定して Authorization Server から Client に戻されます。 その部分で漏洩 / リプレイアタックみたいなやつのリスクがありそうなので、Implicit 使うべきではないみたいな感じですね。

この記事における理由としては

  • Implicit Grant のセキュリティは修理不可能なほどぶっ壊れている
    • トークン漏洩に脆弱で、攻撃者は取得した Access Token で 色々できる
  • Access Token の悪意ある再利用を防ぐためには、sender constrained access token が必要
    • このトークンタイプをサポートするために Implicit grant を拡張することは容易でない

となっていて、なるほど詰んでる。って感じです。

続いて、これはOAuth 2.0にとって致命傷となるか?については

  • もともと Implicit = Browser-based JavaScript App onlyではなかった
    • Authorization Code Grant はいいぞ

と、AuthZ Code 推しとなっています。これもわかる。いわゆる、わかりみがある。

ではなぜ、Implicit grant があるのかってとこで...ちょっと引っかかりました。原文のまま見ていきます。

Well, in the old days (back in 2010) browser-based apps were restricted to sending requests to their server’s origin only.

むかーしむかし、ブラウザベースのアプリはリクエストの送信を自分たちのサーバーの origin に制限されていました。

So there was no way to call an authorization server’s token endpoint at a different host.

なので、異なる host から AuthZ Server のトークンエンドポイントを呼び出す方法がなかった。(?)

That’s why the short cut was introduced to deliver the access token in the authorization response through the browser.

それがショートカットで AuthZ Response 内に Access Token を含んで渡す理由だ(??)

The risks associated with this approach should be mitigated by limiting the privileges and lifetime of such tokens. The implicit grant has always been a compromise!

Access Token の権限やライフタイムを制限することでリスクを緩和するしかなかった。みたいな。

この説明について、そもそも AuthZ Server のエンドポイントを叩けないならば、Implicit grant で Access Token をわたしたって Resource Server のエンドポイント叩けるんですか? AuthZ Server だけ制限しているケースってあるの?みたいな気持ちになりました。

そんなのよりも、Access Token をフラグメントで渡す理由は

  • ブラウザベースのアプリケーションは Public Client であり、 Client Secret を安全に管理できない
  • Authorization Code Grant でもユーザー/攻撃者から簡単に取得できる Client IDだけで Access Token に交換できる
  • 最初から Access Token 渡せばいいじゃん。アプリをホストしているサーバーに値が渡されたくないならフラグメントで

ぐらいな感じじゃないのかと思っておりました。今も思っています。

記事ではここから CORS 万歳!みたいになります。

Thanks to Cross-Origin Resource Sharing (CORS) this compromise now ends. CORS allows browser-based apps to send requests to hosts outside of their origin given the destination permits it.

And what’s very important, it’s broadly adopted! This evolution allows the OAuth working group now to recommend use of the authorization code grant for browser-based apps, that way leveraging the aforementioned security benefits to those apps as well.

CORS 使って AuthZ Code Grant でやるのを推してく!みたいに書いてあります。

まだ残る CORS 推しの違和感を解消するため、別の記事で、こんなの見つけました。

What is the OAuth 2.0 Implicit Grant Type?

この記事では、Implicitが今の仕様になった経緯が "When to use the Implicit Grant Type" のところで説明されています。

One of the historical reasons that the Implicit flow used the URL fragment is that browsers could manipulate the fragment part of the URL without triggering a page reload. However, the History API now means that browsers can update the full path and query string of the URL without a page reload, so this is no longer an advantage of the Implicit flow.

ページのリロードなしでフラグメントの値を操作できるので便利たったけど History API で戻ったりできるようになったのでそんなにアドバンテージではないってのと、

The one remaining reason to use the Implicit flow is if the authorization server doesn’t or can’t support cross-origin requests (CORS). The Authorization Code grant requires that the JavaScript app make a POST request to the authorization server, so the authorization server will need to support the appropriate CORS headers in order to allow the browser to make that request. This is a relatively easy change to make if you’re building your own authorization server, but if you are using an existing server then you may be stuck using the Implicit grant to get around the CORS limitation.

AuthZ Server が CORS 対応してない or できない場合には既存の Implicit Grant で回避するしかなかったみたいなのが書いてあります。 この説明であれば少し親切かもしれません。が、これが決め手とはどうしても思えないですね。

では今後どうすれば良いかについて、Draftが出ています。

draft-parecki-oauth-browser-based-apps-00 - OAuth 2.0 for Browser-Based Apps

00なので今はじっくり見てませんが、落とし所としては

  • PKCE : Public Client でも sender constrained な仕組みにできる
  • Response Mode : AuthZ Code Grant でもフラグメントに載せられる

の併用あたりになるのではと予想しています。

違和感を覚えても安易に「いい加減なこと書いてるとぬっCORSぞ」みたいなことは言わずに、ベストプラクティスを追求していきたいですね。

ではまた。

まんぷくになった YubiKey を YubiKey Manager でリセットしたメモ

f:id:ritou:20181102013325p:plain

こんばんはこんばんは ritou です。

それは突然の出来事でした。

光らない YubiKey

諸事情により ResidentKey を使った navigator.credentials.create()何回も何回も何回も何回もしていたら、ある時から

  • ResidentKey を使った navigator.credentials.create() : 動かない(光らない)
  • ResidentKey を使わない navigator.credentials.create() : 動く(光る)
  • navigator.credentials.get() : 動く(光る)

みたいな状態になりました。

色々なところで挙動を確認していたら、Windows + Edge な環境でこんな表示が。

f:id:ritou:20181102012910p:plain

なるほどお腹がいっぱいなのかな?と思いつつもそれ以上の確認方法がわかりません。

どうせこの YubiKey さんはまだ各所のデモサイトで使ったぐらいの開発用というかどうなっても影響のないものだったので、中身をリセットすることにしました。

YubiKey Manager との出会い

誰かが YubiKey Manager というワードを使っていたのを思い出して、調べたところ、CLIでもGUIでもできそうな感じでした。

実際は恐る恐る CLI でやっちゃったのですが、あとからGUIでも試したら最初からこっちでやれば良かったと思いました。

ドキュメント : yubikey-manager-qt

* Reset the FIDO Applications

これです。インストール方法なども書いてあります。

f:id:ritou:20181102015029p:plain

手元にある青いやつをを入れると...デバイスの画像とFirmwareの番号が表示されます。

f:id:ritou:20181102015409p:plain

こいつに対してできる機能は限られており、Applications -> FIDO2 と進んでいくと...

f:id:ritou:20181102015451p:plain

PIN設定とリセットができそう。

PIN設定はまた後で取り上げようと思っているので、リセットの方を選択します。

f:id:ritou:20181102015655p:plain

f:id:ritou:20181102015836p:plain

念入りに確認たあと、YubiKey を一回さし直せって言われます。

f:id:ritou:20181102015859p:plain

んで、タッチすると成功します。

f:id:ritou:20181102015946p:plain

f:id:ritou:20181102020022p:plain

ここまでで作業は終了です。

光を取り戻した YubiKey

再び ResidentKey を使った navigator.credentials.create() を試みると、光るようになってました。

めでたしめでたし

まとめ

  • YubiKey がまんぷく宣言!
  • YubiKey Manager におまかせ!!

容量についてもうちょっと調べ方ないかなーと思っています。知ってたら教えて詳しい人!

とりあえず、YubiKey Manager のご利用は自己責任で!というところでしょうか。

ちなみに、夜中なので「WebAuthn で出会った YubiKey がお腹いっぱいで眠いって言い出したので YubiKey Manager でリセットしたったwww」みたいなひどいタイトルをつけそうになりましたが書いてるうちに落ち着きました。

ではまた。

【ゆるゆるとパスワードレスなUXを検討】(3) 新規アカウント作成時に WebAuthn Authenticator の登録

f:id:ritou:20181022235144p:plain

どーもどーもどーも。ritou です。 下書きのまま放置してました。

WebAuthnを実サービスに導入しようとしたらどこでつまづくんやってのを考えて来ましたが、ここまではわりと無難な設計に落ち着いている気がします。

今回はアカウントを新規登録したタイミングで WebAuthn の Authenticator を登録するあたりを整理します。 と言っても、アカウント作りつつの (1) WebAuthn Authenticator の登録でしかないです。

前提

登場人物を振り返っておきます。

  • あるサービス = WebAuthn の RP
  • ユーザー = あるサービスに登録完了した時点でこうなっていて欲しいと言う状態
    • 1つ以上の User Verification 可能な Authenticator と紐づけられている(MUST)
    • 既に確認済のメアド / SMS番号を持っている(SHOULD)
  • Authenticator : 生体とかPIN(UserVerification)で単体でユーザー認証が達成できるやつ

新規登録フローってのは、この前提条件に引っ張られてしまうのであまり一般化できなくなってしまいそうなのが悩みどころです。が、仕方ないですね。 そこそこのユーザーデータを扱い、あわよくば決済などを提供できるぐらいのサービスを想定すると、パスワードレスになったとしてもメールやSMSの確認の要件は残るだろうと言うことでこの辺りにしていますが、メアドやSMSの確認を後から行うところもあるよねってとこでその辺りで2パターンを考えていきます。

メアド/SMSの確認を最初に行うフロー

大したことない話ですがサクッと書くとすぐ終わるのでちょっと引っ張ります。

新規アカウント登録のフローとして

  1. メール/SMSの確認を行う
  2. 表示名などの必要最低限な設定を行う

みたいなのが一般的かと思います。

今回はそこに

  • WebAuthn の Authenticator 登録

というのを追加したいわけですが、入力フォーム一つで済むパスワード設定と異なり、Authenticator 登録はユーザーのアクションを伴うのでタイミングが重要ですね。

  1. メール/SMSの確認を行う
  2. 表示名などの必要最低限な設定を行う
  3. WebAuthn の Authenticator 登録

この順番であれば、ユーザー情報が存在してからの Authenticator 登録みたいな感じですが、今回の前提としては登録完了時に Authenticator が紐づいていて欲しい。 ということは

  • 1, 2, 3まで終わったら登録完了!
  • 1 が済んだら内部的に仮登録でユーザーID発行、2, 3まで終わったら本登録完了

みたいな作りになるかもしれません。

ResidentKey を用いるログインフローまで考えると、ユーザーIDが振られ、表示名などを登録した後に Authenticator の登録になるのが望ましい気がします。 内部で扱うユーザーIDと Authenticator に渡すユーザーIDを分けるようにしても実現できそうですが、細けぇ話は次回にまとめたいと思います。

次に、ちょっと順番変えてみます。

  1. メール/SMSの確認を行う
  2. WebAuthn の Authenticator 登録
  3. 表示名などの必要最低限な設定を行う

こっちの方がしっくり来る気もします。

これを書きながら思ったことですが、一度 Authenticator に渡してやった user.name ってメアドやニックネーム的な「後から変更可能な値」が使われることが多そうです。 当然、サービス側で更新した時に Authenticator 側との不整合が起こることは容易に想像できますね。 なんとなく、navigator.credentials.update() みたいな関数が欲しくない?と思いました。

とりあえず、IDの振り方あたりを気をつければ WebAuthn の Authenticator 登録処理を含む新規登録フローもなんとなく実現できそうです。

後からメアド/SMSの確認を行うフロー

ここでは、メール/SMSの確認処理による "最初の離脱" を抑えるために、確認処理を後から行うサービスを想定します。

この場合、

  • 表示名などの必要最低限な設定を行う

  • WebAuthn の Authenticator 登録

の組み合わせの話になります。 上記、ResidentKey を利用する可能性まで考えると、name or displayName あたりが欲しいので

  1. 表示名などの必要最低限な設定を行う
  2. WebAuthn の Authenticator 登録

という順番が無難そうです。

ユーザーがもつ "認証方式の種類" について

builderscon の時も少し話しましたが、メール/SMSを確認して、パスワード認証のリカバリーに使ったりする時点で、もうご立派な認証方式じゃねーかと個人的には捉えています。

今回はその確認を先にやる、後回しにするという2パターンを考えましたが、登録完了時にユーザーが持つ認証方式の数が変わることになります。

特に WebAuthn が使える環境が限られている、ユーザー側の事情により生体認証が不可能な場合などを考慮すると、複数の認証方式が利用できる状態(セキュリティ的観点で言うと利用する認証方式を細かく管理できている状態)になっているのが好ましいでしょう。

「認証する手段がなくなって詰んだ」状態を避けるために、離脱のリスクはありつつも最初にメール/SMSの確認を済ませておくのが無難かな〜と思います。

外部サービスのアカウントを利用した登録フロー

アカウントの新規登録で言うと、いわゆるソーシャルログインを利用した登録フローへの WebAuthn の導入も考えられます。

ソーシャルログインによりSNSなどのいわゆる Identity Provider から確認済みメールアドレスなどを取得できた場合、それをリカバリーに利用できます。

また、ニックネームなどを属性情報の初期値として利用するような実装も一般的でしょう。

WebAuthn の Authenticator 登録との組み合わせ方としては

  1. ソーシャルログイン
  2. 表示名などの必要最低限な設定を行う
  3. WebAuthn の Authenticator 登録

とするのが無難かなと思います。

上述の自前の登録フローと組み合わせる場合は、順番を揃えておくことで実装/UX共に統一感が出せそうですね。

まとめ

今回も無難な内容になったかもしれませんが

  • Email / SMS 確認を先にするサービスと後にするサービスがあり、それぞれで WebAuthn Authenticator 登録するタイミングを整理した
  • Email / SMS 確認のタイミングはサービスの内容だけではなく、 WebAuthn の認証ができない場合のリカバリー手段としても考慮が必要そう
  • ソーシャルログインによる登録に対応している場合も、処理の順番は考える必要がある

これで既存ユーザーへの Authenticator 登録、ログイン、新規登録のUXを整理しました。 次回は今回ちょっとだけ触れた、Authenticator に渡す情報とサービス内のアカウント情報の関係、悩ましい点などを整理したいなと思います。

ではまた!

「Webauthn における ResidentKey について」 について

f:id:ritou:20181020210218p:plain

ritouです。

今回はアメブロで言うところのリブログ的な何かです。

おっさん、昨日の夜にこれ読みました。

blog.haniyama.com

  • ResidentKeyの使い所
  • userVerification オプションの指定に "UserVerificationイラネ" はない
  • platform な Authenticator で UserPresent な場合って?
  • そのままで UserPresent として動作し、PIN設定したら UserVerification になる Authenticator の挙動

あたりがガーッと書かれており、熱い想いがつたわってきますね(?)。

こいつら、組み合わせ方によってイレギュラーな動きをするパターンがあるなら別だけど、これらはそれぞれ独立している話っぽい。

なので、個別で考えるのが良いんじゃないかなと思います。

ResidentKey の使い所

自分の中では、

  • 対象のユーザーが特定できている状態からの "WebAutnn 認証" リクエストのみを利用する : ResidentKey 使わなくてもなんとかなる
  • 対象のユーザーが特定できていない、もしくは事前に特定する必要がない状態からの "WebAutnn 認証" リクエストを利用する : ResidentKey 使う必要がある

ぐらいの認識でいて、WebAutnn を用いたログインフローを整理したりしています。

ritou.hatenablog.com

前者は

  • ログイン中のユーザーを WebAuthn で再認証(例:アカウント設定変更時の "パスワード確認" みたいなの)
  • Googleのログインフォームのように先にメアドなどでユーザー識別をした後に認証処理に入る

みたいなところで、ResidentKeyの機能を使わなくてもなんとかなりそうかなと言う感じです。 一方、後者は”生体認証もしくはセキュリティキーを用いてログイン” ボタンから始まるログインフローみたいなので ResidentKey の機能が必要になりそうです。

自分の検討では UserVerification 使うぞって決め打ちなので今の所そんなに悩むとこはないかなーという印象ですが、Authenticator がサポートする認証方式、つまり UserPresent vs UserVerification の話を ResidentKey の話に混ぜちゃうと、Authenticator の挙動に引っ張られて話が発散しそうな予感はします。

User Present な Platform Authenticator?

だが、スマホや PC で UV がない認証がないけど、これってどうなるの?

スマホやPCでUVがない認証、つまり生体認証もPINも不要でAndroidの初期設定みたいな画面タッチだけで通すようなイメージだろうか。 セキュリティカードみたいな使い所とかが platform(internal) な方であるのだろうか?と言うあたりでしょうか。

んで、どっかの仕様でこれを実装/提供してはいけないとか書いてあるのでしょうかね? と言うのが要確認なところ。 それがあれば別だけど、そうでなければこれを用意するかどうかは需要と言うかユースケースがあるかだけな気はします。

UserVerification 任意 はあるけど UserPresent のみでおkを指定できない

これも、要件があるのかどうかってとこですかね。

これは外部 Authenticator も一緒で、たとえば一旦 PIN を設定した YubiKey は、 User Verification オプションをオフにしても PIN の入力が求められることになる。

つまり、サービスA でタッチだけでログインしていたとして(discourage)、他のサービスBで UV が必須(required)だったとする。そこで、サービスB に登録するために新しく PIN を設定すると、サービスA でも PIN が必要になるということ。

そんなんどこに書いてあんねんってのは、CTAP2 の仕様に書いてあったように思うけど、探しても見つからない。

パッと思うのは「こんなの Authenticator(今回はYubiKey) 依存の問題で、細かいとこまで作り込めばいくらでも素直なUXにできるのでは 」ってところだけど、仕様で定義されてるものがあるのか確認が必要ですね。

日本的な言い方で言うところの「コンシューマ領域のユースケースを考慮した WebAuthn Authenticator の設計/実装ガイドライン」みたいなのがあってそれに書いてるなら従うべきだろうし、なかったら Authenticator 作ってる人たちはちゃんと考えなくちゃいけなくて大変そうですね。

まとめ

とりあえずは

  • Authenticator の挙動が仕様に沿ったものなのか、メーカー依存なのかを確認する必要がある
  • Authenticator 周りの事情がはっきりしてから ResidentKey のユースケースを考え直すと良さそう

ってとこでしょうか。 仕様読むの大変なので、もくもく会以外でも継続してオンラインで何かできると良いですね。

【ゆるゆるとパスワードレスなUXを検討】(2) WebAuthn Authenticator でログイン

f:id:ritou:20181013233507j:plain

どーもどーも、 ritou です。

この記事は 【ゆるゆるとパスワードレスなUXを検討】(1) WebAuthn Authenticator の登録 - r-weblife の続きです。

ということで、しばらく続ける予定です。

WebAuthn そのもののログイン方法については詳しい方々の資料やデモなどがあるので、把握されている方も多いでしょう。

今回はレガシーなサービスのパスワード認証など、複数のログイン方法がある場合なども考慮しつつ、ユーザーが WebAuthn を用いたログインにどう出会うかみたいなとこを考えます。

前提

登場人物を振り返っておきます。

  • あるサービス = WebAuthn の RP
  • ユーザー = あるサービスに登録済みのユーザー
    • パスワードを設定しているかもしれない
    • 既に確認済のメアド / SMS番号を持っているかもしれない
    • 1つ以上の User Verification 可能な Authenticator と紐づけられている(←new!!!)
  • Authenticator : 生体とかPIN(UserVerification)で単体でユーザー認証が達成できるやつ
    • 注意 : 今回の記事のスクリーンショットでは都合により YubiKey(User Present) を用いたものを載せています。ご了承ください。

既に Authenticator との紐付けは済んでおり、その後のログインで WebAuthn を使って行くあたりを考えていきます。

WebAuthn を絡めたログインフロー

WebAuthn のみのログインフロー

シンプルなUXでいうと、最初は

  • WebAuthn でしかログインする方法がない

みたいなケースから見ていきましょう。

一番シンプルなのは、"Login" ボタンとかリンクを押すといわゆる navigator.credentials.get() が呼ばれて WebAuthn のログインが始まるパターンかと思います。

Yubico の WebAuthn デモサイトで言うところの "Login without username" ですね。

f:id:ritou:20181014022300p:plain

これを使うには、Authenticator の登録で navigator.credentials.create() を利用する時、requireResidentKeytrue にセットする必要があります。(前の記事に追加しなければ...done)

2018/10/14 時点で、Chrome のバージョン: 71.0.3578.5(Official Build)canary (64 ビット) を使うと動作確認もできます。 最新版のGhrome Canary だと NotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported at this time. というエラーが返されます。Official Build だとエラーにはなりません。

f:id:ritou:20181014022830p:plain

ユーザー名を入力しなくてもちゃっちゃと進みます。

現在の Chrome では単一のユーザーの情報が保存されている場合のみこれが使えると聞いた記憶がありますが、複数のアカウントからの選択、いわゆる Account Chooser みたいなUIをサポートしていれば、Authenticator に登録されているユーザーから選択する処理がブラウザ上で行われることになりそうです。 (Windowsでのアカウント選択画面については IdM実験室: Windows 10 October 2018 UpdateのEdgeでWebAuthnを試す で説明されてますね)

Windows Edge + Google 製のデモサイト + コンソールでちょちょちょっと作業すると、Account Chooser 的な UI が見えます。

f:id:ritou:20181022135030p:plain

  • メリット : RP実装、ユーザーアクションが少ない
  • デメリット : 他のUXに比べてエラーハンドリングは多少増えるかもしれない

こんなエラーの場合は新規登録に流すみたいなのも整理しないといけないんですが一通り揃ってから考えましょう。

ユーザーの設定を見て WebAuthn のログインフローに誘導

次は一度ユーザーの識別(特定?)を行ってから WebAuthn のログインが始まるという UX です。

  • WebAuthn 以外にもログイン可能だけど、Authenticator と紐づいてるユーザーが優先度をあげている、もしくはデフォルトで WebAuthn でログインする
  • WebAuthn のログインにて Resident Key を使わない

というあたりでしょうか。

現状で近い UX としては、Google へのログインでしょうかね。この前気づきましたがヤフーも同じ感じです。

ログインの際はまず、最初にメールアドレスを入力します。

f:id:ritou:20181014093824p:plain

サービス側はメールアドレスなどの識別子に紐づくユーザーが WebAuthn の対象であった場合に、WebAuthn のログインフローに誘導します。 この場合、いわゆる ResidentKey の機能に頼らずとも、navigator.credentials.get() を呼び出すときに allowCredentials -> id として CredentialId を指定してやると実現できそうです。

と、実際はこの誘導についても

  • そのまま navigator.credentials.get() を呼び出してエラーハンドリングしっかりやる
  • 環境により WebAuthn が利用できない可能性も考え、"WebAuthn でログイン" ボタンみたいなのを表示して WebAuthn 優先のUIにしつつも他の認証方式も利用可能にする

などの細かいやり方はありそうです。

先にユーザー識別を行うこの UX に対してはその識別子が有効かどうかわかってしまうのであれこれ、みたいな話は以前からあります。

WebAuthn を絡めても同じ話は残るので、そのあたりの考慮は必要そうですね。

  • メリット : 他の認証方式からの移行時、複数の認証方式をサポートしている場合も利用可能
  • デメリット : RPの作り込みがけっこう必要そう

ソーシャルログイン的に認証方式を選択

残ってるのは、最初からユーザーに認証方式を選ばせる UX です。

と言っても、"WebAuthn のみのログインフロー" と他の認証方式が並ぶイメージですね。

最初に実装する人が何も考えないで実装しちゃうと、文言が "Login with WebAuthn" とかになっちゃいそう。

その文字を見たときにユーザーの理解、大丈夫でしょうかね。大丈夫じゃなさそう。

f:id:ritou:20181014095833p:plain

ここまで行くためには FIDO 2.0/WebAuthn が、かなり普及されてないときつそうではあります。

そしてまたボタンが増える... Nascar Problem って知ってますか?懐かしいですね。

その他細かいこと

直接UXには関係ないところで気をつけるべき物をメモっときます。

  • ユーザー認証ログを残そう : これは認証方式問わずやるべきですね
  • 環境判定(初めての環境ならどうこうみたいな処理)を入れているならその辺りはパスワード認証と同様に続けるべき
  • Authenticator紛失なども考慮して、ログインできるかどうかを制御できるようにするべき

まとめ

ログインの UX について考えてみました。

  • Resident Key
  • 他の認証方式やリカバリーフローへのフォールバック

あたりを考える必要がありそうです。

次回は新規登録やりますかね。

ではまた!

【ゆるゆるとパスワードレスなUXを検討】(1) WebAuthn Authenticator の登録

f:id:ritou:20181011025933p:plain

どーも、ritou です。

先日、こんなイベントに参加しました。

fido2-workshop.connpass.com

ブラウザの対応状況などがわかってきたところで、そろそろ実サービスに導入する際に引っかかりそうなところを整理しておきたいというお気持ちです。

いきなり全部書くのは大変なので、最初は割とシンプルにできそうな既存ユーザーへの Authenticator 登録のあたりから考えます。

追記:Yahoo! JAPAN が WebAuthn + Android Chrome による指紋認証を用いたログインを導入したのでそれを意識しつつ更新してます。

about.yahoo.co.jp

前提

登場人物はこんな感じです。

  • あるサービス = WebAuthn の RP
  • ユーザー = あるサービスに登録済みのユーザー
    • パスワードを設定しているかもしれない
    • 既に確認済のメアド / SMS番号を持っているかもしれない
  • 登録できる Authenticator : 複数登録可能. 生体とかPIN(UserVerification)で単体でユーザー認証が達成できるやつ
    • 今だと Chrome + 指紋認証, Touch ID / Edge + Windows Hello みたいなあたりを想像
    • (追記)platform, cross-platform どちらも対象にしたい

汎用的な話にしたいので、ユーザーの前提を少し緩めにしてます。 が、パスワードレスをうたいたいので Authenticator は UserVerification 必須にしてます。 では、あるサービスのユーザーが Authenticator を登録するまでの流れを整理しておきましょう。

Authenticator 登録の流れ

サービスが行う一連の流れをざっくり書くと、以下のようになりそうです。

  1. 既存の認証方式を用いて一度ユーザーを認証する : パスワード確認、メール / SNS + 認証コードなど
  2. WebAuthn の isUserVerifyingPlatformAuthenticatorAvailable() を用いて対応している Authenticator を持っているかを判定
  3. WebAuthn の navigator.credentials.create() を呼び出し、ユーザーは Authenticator を操作
  4. WebAuthn の navigator.credentials.create() のレスポンスを検証し、ユーザーに紐付ける
  5. Authenticator 追加完了時にメールなどで通知を送る

この一連のフローは割と一般的な "デバイス紐づけフロー" と似ていると思います。

1 について、ログイン方法が1つ増えることはサービス内で重要な設定変更であるため、この処理に入る前にはパスワード認証における「パスワード確認」相当の、ユーザーを再確認する方法が必要でしょう。

特徴的なのは、紐づけられる Authenticator を持ってるかどうかを確認する必要があるだろうと思い、2を入れてます。 が、その Authenticator がユーザーに紐づいているかまではこの関数ではわからないことには気をつけないといけないですね。

(追記)↑ここで

というコメントをもらいました。 isUserVerifyingPlatformAuthenticatorAvailable()platformな Authenticator の判定となるので cross-platform な Authenticator も対象としたい場合は使ってはいけませんね。

WebAuthn Relying Parties use this method to determine whether they can create a new credential using a user-verifying platform authenticator. Upon invocation, the client employs a client platform-specific procedure to discover available user-verifying platform authenticators.

ということでこの処理はなかったことで。

navigator.credentials.create() の呼び出しのところでは、

  • 今回想定しているのは MFA ではないので UserVerificationrequired にする
  • ユーザーIDなども Authenticator に保存する Resident Key の機能を用いてユーザー識別子の入力をスキップしてログインさせたい気持ちがあるので、 requireResidentKeytrue にする

とかが必要そうです。詳細は省略します。

navigator.credentials.create() のレスポンス検証してユーザーと紐づけるところでは、

  • Attestation に含まれる Authenticator 情報の一部を使って判別できるようにする
  • ユーザーに名前をつけさせ、登録した日時や環境などと一緒に表示して判別できるようにする

などの工夫をしておかないと、後から Authenticator の無効化などを考える時にユーザーに識別させるのが面倒になりそうです。 個人的には上記の2つの案を組み合わせたらバランス取れるかなと思いますが、最初に載せたイベントでは Google の方が "エンプラをのぞいて Attestation 使うな" とお達しがありました。

Yahoo! JAPANではユーザーに名前をつけさせて管理しているようです。 C向けは Attestation を使わず、ユーザーに名前をつけさせるのがスタンダードになるか注目です。

ちなみに、yubico のデモサイトでは、登録完了時に Attestation に含まれる値を表示に用いています。

f:id:ritou:20181011034418p:plain

          "deviceProperties": {
            "deviceId": "(略)",
            "displayName": "Security Key by Yubico",
            "deviceUrl": "https://www.yubico.com/products/yubikey-hardware/fido-u2f-security-key/",
            "imageUrl": "https://developers.yubico.com/U2F/Images/SKY.png"
          },

ラッキングに使えてしまうのよくないのはその通りですが、実際にユーザーに紐づけるとなるとちょっとだけ情報が欲しくなるので、悩ましいですね。

5として、メールなどの登録完了通知を入れています。 アカウントに対する認証方式の追加はとても重要な処理なので、万が一の場合でも本人に気づきを与える仕組みは必要でしょう。

この流れを使う具体的なフローは2種類ぐらいあるかなと思っています。

1. ユーザーが自発的に設定管理機能から登録

ユーザーが "設定 -> セキュリティ -> 新しいログイン方法" みたいなところで自発的に登録するケースです。

2で対応している Authenticator がない時にヘルプへの誘導をしたり、現在の設定状況をみて紐づけられている Authenticator の上限チェックを行うことになるでしょう。

3の前に現在の設定状況をみて紐づけられている Authenticator の上限チェックを行うなど、サービス側で紐付け可能かの判断が入ることになるでしょう。 3, 4 の処理を経て対応する Authenticator がなかった場合もヘルプへの誘導をしたり、という形式になりそうです。

2. ユーザーがログインしたタイミングで環境などを判定して登録フローに誘導

ユーザーがその時点のログイン方法でログイン完了のタイミングでフックして、Authenticator の登録フローを強制 / 任意で誘導 するようなユースケースもありえるかなーと思っています。

2では Authenticator が利用できる状態であるだけではなく、ユーザーに Authenticator が紐づいていないとか、過去にユーザーがこの誘導を拒否した履歴がないみたいな条件にマッチした時、Authenticator の登録へと誘導するようなイメージです。

ここでも3の前にユーザーに Authenticator が紐づいていないとか、過去にユーザーがこの誘導を拒否した履歴がないみたいな条件にマッチするかどうかを判断して Authenticator の登録へと誘導するようなイメージです。

どうしたってユーザーは自発的に設定しないものだと思いますので、普及させたかったらこれぐらいやらないと「新しい認証方式、誰も使ってくれないわ...」みたいな状況になりそうです。

まとめ

最初なので既存ユーザーに Authenticator を紐づけるところから UX を考えてみましたが、そんなに新しい感じはなかったですね。 次回はログインフローを取り上げる予定です。こちらは Resident Key を用いた Account Chooser とか、もう少し考えることがありそうです。

ではまた!

Authlete の OAuth 2.0 / OIDC 実装ナレッジ 完全に理解した

f:id:ritou:20151012214551j:plain

お疲れ様です。ritouです。

OAuth 2.0 / OIDC 実装の刺激が欲しくなったので(?)、Authlete 社が公開しているナレッジサイトの OIDC / OAuth 2.0 に関する部分を読むことにしました。 kb.authlete.com

この記事は、OAuth 2.0 / OIDC を完全に理解した上で Authlete というプロダクトについてなんとなくイメージがついていないとニヤニヤできないかもしれません。 Authlete は "サードパーティーアプリケーションから Web APIへのアクセス制御を行うために必要とされる『認可』の仕組みを提供するクラウドサービス" ですとどこかに書いてありました。

これは個人的な考えですが、RFCなどで定義された仕様を利用するプロダクトというのは、仕様で定義されている機能を設計/実装と、仕様で定義されていない部分や複数の仕様で定義されていて選択が必要な部分の設計/実装によって作られていると思います。

f:id:ritou:20181001013008p:plain

この、仕様で定義されていない部分の設計/実装というところが、そのプロダクトの立ち位置などにより与えられた要件をどのように解決しているのか、つまりナレッジであると考えています。 Authlete というプロダクトのナレッジから何か新しい発見があることを期待しながら、上から見ていきます。 なお、9/30頃に読んだものなので、それから更新されたりなんなりで差分が発生しても追従しない予定ですが、追記ぐらいはするかもしれません。

トークン管理

ユーザー単位での発行済トークン管理 — Authlete ナレッジベース (Beta)

実行例が紹介されていますが、APIリファレンスと重なるので、「 Authlete では発行済みの Access Token を操作する API があり、こんな用途に使えます」ってのを紹介する方が良い気がしますが、まぁそれは個人的見解です。

簡単に説明すると

  • ユーザーIDを指定して、有効な Access Token が発行済みの Client 一覧が取れる : 管理画面やユーザー向けの設定画面で「連携しているアプリケーション一覧」みたいな機能をつける時に使えそう
  • ユーザーID, Client ID, Scopeの値を指定して更新 : Scope定義の変更時などに整合性を合わせる時に使えそう(下の方に書いてある)
  • ユーザーID, Client ID を指定して、発行済みの Access Token 一覧を削除 : 「連携しているアプリケーション一覧」からの解除に使えそう

って感じでしょうか。

Authlete を利用するサービスが Access Token をハンドリングできるAPIを用意していますよということなので、例えばライブラリ開発者とかならこれを参考に似たようなメソッドを内部で用意しておくと便利かもしれません。

ハイブリッド・フロー: スコープを制限したアクセス・トークンの発行 — Authlete ナレッジベース (Beta)

  • 発行済みの Access Token の Scope を変更(狭められる)できるAPIがある
  • ハイブリッドフローで Client のフロントエンド / バックエンド向けに2つの Access Token を発行することができる
  • Authlete 利用サービスの認可サーバー・フロントエンドがScope更新APIを使ってフロントエンドむけのスコープを制限することができる

ちなみに ”Authlete 自身はスコープのサブセットを管理しません” とある、サブセット管理の例を上げておくと

  • Authorization Request で指定可能な Scope を管理する
  • そのうち、Implicit 的に渡す Access Token に付与していいかどうかをフラグで持つ
  • Authorization Endpoint から発行される Access Token にはフラグが立ってるやつだけ指定される

とかになって複雑になりそう。やらない選択に +1

一瞬、クライアントのフロントエンドが更新API叩くのか?って思ってしまったので、図があると良いのかも。 "Authlete 利用サービスの認可サーバー・フロントエンドが" と言うことは、Scope 広いアクセストークンが Client に戻される前に Scope を制限できるってことなので、リスクもないですね。

使用されていないトークンに関する Authlete の削除ポリシー — Authlete ナレッジベース (Beta)

  • 90日間利用されなかったトークンは消す
  • Access Token だけじゃなく Refresh Token も使われてなかったら消す

だそうです。

期限切れのアクセス・トークンに関するイントロスペクションの挙動 — Authlete ナレッジベース (Beta)

有効期限切れのAccess TokenをトークンインストロペクションAPIに投げたとき、

  • 初回は有効期限切れと言うレスポンスを返し、削除処理が走る
  • 2回目以降は存在しないレスポンスになる

トークンインストロペクションAPIがわからない? って人...つ RFC7662として発行されたOAuth Token Introspectionとは - r-weblife

リフレッシュトークンを継続利用する/しない — Authlete ナレッジベース (Beta)

例えばWebアプリケーションのログインセッションのポリシーは結構バラバラであり、

  • 一度ログインしたらずっとログイン状態
  • 最終アクセス日時から一定期間たつとログアウト
  • ログイン日時から一定期間たつとログアウト

みたいなのとかがある。 Authlete では OAuth / OIDC の Refresh Token も

  • 利用時に有効期限を伸ばすことで 「定期的にアクセスしてくるエンドユーザー(=リソースオーナー)は再度認証・認可をする必要がなくなり、一方、長期間使わないエンドユーザーは再アクセス時に認証・認可が必要となります。」
  • 有効期限は発行時のままにして 「アクセス頻度によらず、一定期間毎(リフレッシュトークンの有効期限が切れる毎)に、エンドユーザーは再度、認証・認可が必要となります。」

のどちらかを選べます。自由度高いですね。

トークン情報の更新 — Authlete ナレッジベース (Beta)

  • 発行済み Access Token の有効期限やScopeの値を更新するためのAPIがある
  • Access Token単位の指定、Client - ユーザーに関するトークンを指定できる

指定方法によって更新機能が異なり、発行済み Access Token を指定する場合は

  • 有効期限とスコープの更新ができる。
  • 任意の Key / Value の値をプロパティとして追加して、Authlete のDBに格納できる

ということで、任意の値を Access Token に紐づけられるとなれば、サーバレスというかBaaS的な使い方が広がりそうな気はします。

Client - ユーザーに関するトークンを指定については、"ユーザー単位での発行済トークン管理" に書いてあったやつですね。実行例は...と記載してありますが、この辺りはもうちょっと整理してあると読みやすそう。

そして、補足事項としてアクセストークンの単一化について記載してあります。 これは、ある Client / User に対して有効な Access Token を制限できるってことなので、リスクもないですね。

  • 発行済みで有効期限内であり、無効化されていない Access Token 全て
  • 最後に発行された Access Token のみ有効とし、過去に発行された Access Token は無効化される

という2種類の要件に対応できるというお話ですね。 これだけで1つのトピックとして成り立つぐらいの内容な気がしています。なんで補足なんだ。

認可タイプ

最適な OAuth 2.0 フローの選び方 — Authlete ナレッジベース (Beta)

Grant Type どれ使うか問題へのアドバイスでしょうかね。

基本的に、認可コードフロー(+ PKCE)の利用をご検討ください。どうしても認可コードフローが使えない場合、各種フローの性質をよく理解した上で、他の方法をご検討ください。

無難ですな。

スコープ

カスタムスコープの言語別説明文の登録 — Authlete ナレッジベース (Beta)

カスタムスコープの説明文を言語別に設定できるAPIがあるそうです。

クライアント管理

サービス管理者による開発者コンソールへのログイン — Authlete ナレッジベース (Beta)

開発者コンソールの話なので省略したいところだけど、API Key/Secret を API アクセス以外に使うのって...と思ったり思わなかったり。

シンプルにできるならそれでいいのかもしれないけど。

クライアント ID の別名化 — Authlete ナレッジベース (Beta)

  • Authlete には Client ID を払い出す機能がある
  • 既存の認可システムから Authlete に移行したい場合、既存の Client ID どうするの問題が発生する
  • Authlete では、Client ID のエイリアス機能を有効にすることで既存の Client ID を指定したままの移行が可能

ということですね。これはけっこう親切ですね。

ユーザーが認可したクライアントの管理に関する Authlete のポリシー — Authlete ナレッジベース (Beta)

  • (上の方でも出てきたけど)ユーザーが認可済み Client 一覧を取得できる
  • これは、"Access Token / Refresh Token が有効" な Client ではなく、"Access Token / Refresh Token がDBに存在する" Client だぞーん
  • なので、DBから消えたら出てこなくなる

というポリシーの宣言のようです。きっと裏ではユーザーIDでSELECTしてClientでまとめて...みたいな処理なのでしょう。

認証

OAuth 認証と OpenID Connect — Authlete ナレッジベース (Beta)

  • Access Token からプロフィールなどを引いた結果を用いて認証処理を行うみたいな、いわゆる OAuth 認証を Authlete を使って実現はできる
  • 様々な視点から考える必要があるので、SSOやID連携をするときは、OIDC の ID Token 使え

2012年の記事が参考文献として紹介されていますが、あれから今まで OAuth 認証だめ、OIDC 使えっていう啓蒙をするにふさわしい文献的なものはなかったっけなと振り返り中です。もう2018年も残り3ヶ月だぞ。

エラー処理

"fail" API を用いたエラーレスポンスの生成 — Authlete ナレッジベース (Beta)

  • Authorization Endpoint のエラーレスポンスの実装、サポートするパラメータが増えたりすると結構大変
  • Authlete にはそれやってくれるAPI がある
  • ROPC 使うとこ向けに、 Token Endpoint 用のものもある

確かに Response Code / Response Mode の値をサポートしようとしたらレスポンス生成部分だけで結構複雑な実装になりそうなので、このAPIは便利ですね。

クライアント認証

client_secret_jwt によるクライアント認証 — Authlete ナレッジベース (Beta)

ちなみに、例示のセクションを "テスト" と表現するのは微妙な気がしますね。

private_key_jwt によるクライアント認証 — Authlete ナレッジベース (Beta)

JWK用意するのだけちょっとめんどくさいかも?ライブラリに自動生成のスクリプトなりメソッドなりがあるともっと楽そうですね。 ちなみに、例示のセクショ(ry

UserInfo エンドポイント

Userinfo API におけるアクセストークン検証 — Authlete ナレッジベース (Beta)

Authlete の UserInfo APIAccess Token が有効かどうかを検証するので Introspection 叩かなくていいですよと。これ書くってことは、API仕様に書いといても見てくれないんでしょうかね。

あと、APIリファレンスだと /auth/userinfo (こっちがUserInfo取得?)と /auth/userinfo/issue (ID Token発行?)があるのでリンクは https://docs.authlete.com/#userinfo にでも貼っとくのが良さそう。

感想

Authlete というプロダクトが様々なところで使われるにあたり、仕様の範囲外の部分をどう設計/実装したのか、顧客もしくは内部から出てきた要件に対応するために考えたと思われる部分が詰め込まれていて興味深かったです。

この手のナレッジの表現方法はなかなか難しい印象がありますが、個人的には先に「このような要件があり」「自分たちはこう対応した」みたいな書き方だと読みやすいのかなと思いました。

そして、できれば「このような要件があり」の部分が 「OIDC Provider / OAuth 2.0 Server 実装の悩みどころ、実装あるある」 のように一般化されてどこかパブリックな所にまとめられていると良いのかも?と思ったりしました。

上で取り上げられているものでいうと

  • 使われていない Access Token を削除しちゃっていいのか
  • ハイブリッドフローで Implicit な認可レスポンスで返す Access Token と Authorization Code を受け取って返す Access Token で Scope 変えたい

とかが公開されていた場合、OIDC / OAuth の実装時に「これ考えないといけない」リストとしても使えますし、出来上がったプラットフォームや今回の Authlete のようなプロダクト以外にも、ライブラリの開発者などが後から「自分はこう対応した」というまさにナレッジの比較を行うこともできそうです。

この辺りはどこかで Authlete や他のプロダクトの方とお話ししてみたい所ですね(←何かへのフリ)。 ではまた。

OpenID Connect のあれが WebAuthn のこれになったらどうなるかって話

f:id:ritou:20180928025259j:plain

おはようございます。ritou です。

builderscon tokyo 2018 にて WebAuthn のさわりの話をした時に、「OAuth / OpenID Connect」 との関係について質問がありました。 この質問に限った話ではありませんが、"認証のための技術" なんていうと、認証方式、認証状態のセッション管理、ID連携まで広範囲に渡るため、話が混乱してしまうものです。 ここでは OIDC で言うところの誰が WebAuthn で言う所の誰になるとどうなるのか、みたいな話を書きます。

前提

FIDO と Identity な標準化技術は "補完関係"

結構前ですが、もっと詳しい方が書いたものを紹介します。 https://www.jstage.jst.go.jp/article/itej/70/5/70_481/_pdf

上記FIDOの技術仕様は、認証の領域にのみ注力しています. 近年, 認証や認可などの技術を含むアイデンティティ(Identity, 本人性)管理と呼ばれる技術領域において標準化が進められてきました. それらの標準的技術(例えば, SAMLOpenID Connectのようなアイデンティティ連携技術仕様)と協調的で互いに補完関係になるように配慮しています.

"補完関係" です。 いつだったかの idcon のタイトルもこんな感じでしたね。 使っていきましょう。

OIDC

OpenID Connect は、(OAuth 2.0のAPIアクセスの仕組みの上で)異なるサービス間であるユーザーの認証情報を渡す仕組みです。

OpenID Connectの登場人物で言うと

  • OpenID Provider (OP) : あるユーザーの認証情報をRPに提供
  • Relying Party (RP) : あるユーザーの認証情報をOPから取得

があるので、これらを OIDC OP, OIDC RPと呼びます。 ソーシャルログインの普及により、数でいうと OIDC OP < OIDC RP というようになります。 懐かしのSNSのように、OIDC OP かつ OIDC RP なやつとかいますが、一旦置いておきます。

WebAuthn

登場人物として

  • Authenticator : 生体認証などを提供
  • Relying Party : 生体認証などを利用

がいます。

Authenticator が OIDC... ってなるとちょっとイメージが難しくなりそうなので、今回は Relying Party を WebAuthn RP と呼び、これがOIDC OP/RPのどちらになったらどうなるかって話をします。

組み合わせ方

OIDC OP == WebAuthn RP

OpenID Connect の OPである GoogleYahoo! JAPAN とかが WebAuthn の RP になって生体認証とかでログインできるようになるケースです。 一般的に、ユーザー規模が比較的大きく、2段階認証的なのを早い段階から実装していることが多い印象がある OpenID OP なので、実現しそうな予感がプンプンします。 メリットを考えてみましょう。

  1. OIDC OP のサービス単体として割と多くのユーザーが WebAuthn の恩恵を受けられ、パスワード認証からの移行を進めることで安全性を高められる
  2. OpenID OP を利用している OpenID RP も、OIDC OP で WebAuthn を利用するユーザーについて追加実装なしで安全性を高められる
  3. ユーザーに紐づく Authenticator の管理等を比較的数の少ない OpenID OP 内で完結できるので、ユーザーの負担は抑えられる

全体で見た時のデメリットと言うか迷うところでいうと、"OpenID OP が WebAuthn で認証したユーザーだと言うことを OpenID RP に教えてもらえないと辛い"というのはありそうです。
これはソーシャルログインを流行らせようとしてた頃からずっと言ってますが、OpenID OPから WebAuthn 使ってる人とパスワード使ってる人が一緒に OIDC RP に流れてくる可能性があるわけです。
OIDC RP がユーザーの認証レベルを意識して必要ならば追加の認証をさせようなどと思った時に、ハンドリングするための情報が欲しくなるでしょう。

例えば Google の OIDC ID Token を見てみましょう。 OpenID Connect  |  Google Identity Platform  |  Google Developers

ID Token の claims のところを見てみると...

f:id:ritou:20180928022444p:plain

おきまりの "iss" とかに加えて、"email" や "profile" とかがあって...

f:id:ritou:20180928022454p:plain

認証方式とか、そう言うのはなさそうです。 これだと、OIDC RP 側でこのユーザーはどんな認証をしてきたんや、どんな人生を歩んできたんやと言うのがわかりません。 OIDC RP側がきっちりポリシー決めて、追加認証とか入れようと思っても辛いです。

では、念のため Yahoo! JAPAN の ID 連携の方も見てみましょう。 Yahoo! ID連携:ID Token - Yahoo!デベロッパーネットワーク

Payloadのところです。

f:id:ritou:20180928023211p:plain

「本当に世の中の文字は小さすぎて読めなーい!でしょう?」

f:id:ritou:20180928023331p:plain

"amr" claim があれば、ユーザーの認証方式がわかります。これは便利。 いずれはここに FIDO 2.0 / WebAuthn 関連で追加されることでしょう。 さらに、認証した日時までもらえます。

f:id:ritou:20180928024109p:plain

これがあれば、OpenID RPも認証強度が適切かどうかを自分たちのポリシーに照らし合わせて判断し、場合によっては追加認証を求めたりもできそうです。 素晴らしいですね。
あとはYahoo! JAPAN が FIDO 2.0 / WebAuthn に対応すれば良いわけです。と言うところで

about.yahoo.co.jp

ヤフー株式会社(以下、ヤフー)は、生体認証などの次世代認証の標準化を提唱する業界団体FIDO(ファイド)アライアンスの新たな規格「FIDO2」の認定を、2018年8月に世界で初めて開催された「FIDO2」認定テストにおいて、このたび取得したことをお知らせします。国内企業では唯一の認定取得となります。 ...
ヤフーは、近日中にFIDO認証に対応し、ますますパスワードレスで安全なログインの普及に努めてまいります。

Yahoo! JAPAN FIDO 2.0 Ready 状態っぽいので、上記の組み合わせが実現する日も近そうです。(褒めまくったので何かくれ)

OIDC RP == WebAuthn RP

続いて、OpenID Connect の RP が WebAuthn RP になるパターンです。 現在、世の中の OpenID Connect の RP は OP のアカウントと連携しつつも、独自にパスワードを設定させる事例が多くありますが、これではパスワードレスな世の中は実現できません。 また、OIDC OPに障害が発生している時など、OIDC RP 側で身動きが取れなくなる可能性もあります。 そのような場合に、やはり独自で認証方式を持たなければならない、となった時に、WebAuthn も候補に上がるでしょう。

メリットとしては"OIDC OP が WebAuthn に対応していなくても、全てのユーザーに WebAuthn の利用を強制できる"と言うあたりでしょうか。 逆にいうと、作りによってはOIDC OP/RP 双方で WebAuthn の認証を求められて辛い、ということにもなりそうです。 なので、やはり OIDC OP 側の方がしっかりしないといけない雰囲気ですね。

あとは、数の観点からユーザーがいろんなサービスに Authenticator を登録する必要がでてくる可能性があるところでしょう。 パスワード認証よりも認証方式そのものがセキュアであるとはいえ、今までのクレデンシャル管理の歴史を思い返すとなかなか辛いですね。

これは差別かもしれませんが、OIDC OP/RP が WebAuthn を追加するのにどっちが安全な実装ができるだろうかと考えたときに、やはり OIDC RP 側は結構頑張らないといけなそうな雰囲気がします。 信頼できるライブラリを使う、Auth0 のように認証機能を代行してくれる外部サービスを使うのも良いでしょう。

あとは、これからだんだん見えてくるであろう、WebAuthn の登録/認証フローにおけるベストプラクティスを実装していくことが重要になるでしょう。

どちらの組み合わせでも必要なベストプラクティス

OIDC OP/RP いずれが WebAuthn を実装することになっても、やることは一緒です。

  • 新規/既存ユーザーと Authenticator の Public Key の紐付け
  • 認証
  • Authenticator の管理(追加、削除)

この辺を詰めていく必要があります。 もっと言うならば、あるユーザーに対して Authenticator が追加/変更/削除 されたことをサービス間で伝搬させるような仕組みが重要になるかもしれません。 セキュリティイベントの通知みたいな感じですかね。これは別途考えたいところです。

まとめ

  • OIDC の OP/RP のそれぞれが WebAuthn の RP となる未来を想像した
  • どっちもアリ
  • ベストプラクティスは詰めていかねばならん

といったところでしょうか。 そういえば、来週は WebAuthn 関連のイベントがありますね。

idcon.connpass.com

fido2-workshop.connpass.com

私も参加予定です。 ではまた!

文字で読みたい2分間OAuth講座 : (7) When can one use password grant?, (8) ROPC (Password Grant) the Migration Tool

こんばんは、ritouです。

まだNatさんのチャンネルをサブスクライブしていないの?

www.youtube.com

前の記事 : 文字で読みたい2分間OAuth講座 : (5) Secret of Authorization Code, (6) Actors of OAuth - r-weblife

では、今回も見ていきましょう。

(7) When can one use password grant?

www.youtube.com

今回は、Resource Owner Password Credentials Grant Type (ROPC) の使い所について説明します。 一言で言うと「使ってはいけません」なのですが、それだと2分間にならないのでもう少し説明します。

ROPCは、ユーザー(Resource Owner) が自らのIdentifier, パスワードを Client に渡し、 Client はそれを Authorization Server に渡して Access Token を取得するフローです。

f:id:ritou:20180926025809p:plain

UIで言うと、これらはユーザー認証にあたりバックエンドのLDAPやDBサーバーに問い合わせる、(OAuthが登場する前からある)レガシーなアプリケーションと同様です。

f:id:ritou:20180926030511p:plain

仕組みとしてレガシーアプリケーションよりもROPCで改善されている点としては、Client が Resource Owner のパスワードを保存することなく、タスクを実行できることです。

f:id:ritou:20180926030812p:plain

しかし、ここでClient は"中間者"としていわゆる"中間者攻撃"を行なっていることに注意すべきです。

f:id:ritou:20180926031506p:plain

Client は Resource Owner のパスワードを取得しているので、Resource Owner ができることは Resource Owner に成り代わって全てできます。 (Authorization Server が Resource Owner に対して Client からの要求に対する許可を問い合わせる Authorization Code Grant とは異なり、) Resource Owner が実際に許可を与えたかどうかを Authorization Server はわかりません。

Authorization Server が Client に全幅の信頼を寄せていても、これはしても良い事ではありません。 なぜなら、Resource Owner をフィッシング攻撃に脆弱になるように訓練しているようなものだからです。 ある意味では、これはインターネットに公害を垂れ流し、他人にコストを押し付けていることになります。

悪いことは他にもいくつかありますが... Scott Brady さんの記事は、この件に関する良い概論となっています。

www.scottbrady91.com

せっかくなので目次だけでもざっと載せると、

  • ROPCがなぜ存在するのか
  • ROPCを使うべきではないたったn個の理由
  • ROPCを使う前の自分自身への4つの質問

が書いてあり、まとめとしては

  • (著者が)唯一 ROPC の利用を認めたのは、2000年前半のレガシーアプリケーションからの移行の時だけ
  • ブラウザベースのクライアントアプリケーションなら Implicit Grant使え
  • WebアプリケーションならAuthorization Code Grant使え
  • IoTデバイスなら Device Flow使え

とのことです。

If you use this grant type despite the above, I will be nasty to you and say mean things about you both behind your back and to your face.

"say mean things about" は意地悪なことを言うって意味ですな。 say mean things a...の意味・用例|英辞郎 on the WEB:アルク 何やら穏やかではない。

んで、最後にこの動画が貼り付けられています。循環参照的な...

話を戻します。 もしモバイルアプリケーションを書いているならば、IETF BCP212 が参考になります。

BCP 212 - OAuth 2.0 for Native Apps

まとめると、

  • ROPC は Resource Owner のパスワードを扱うフローであり、推奨されていない
  • RFC6749に記載されているのも、レガシーアプリケーションからの移行が理由である。もう7年前だぞ。
  • 要するに、使うな

そして、第8回はそれでも食い下がって来る人向けのお話のようです。

(8) ROPC (Password Grant) the Migration Tool

www.youtube.com

前回、ROPC使うなって言ったら、仕様に書いてるから使っていいだろって言われました。 ほんとかどうか見ていきましょう。

ROPC は RFC6749 に記載されていますが、こうも書かれています。

The authorization server and client SHOULD minimize use of this grant type and utilize other grant types whenever possible. Authorization Server と Client はこのフローの利用を最小限とするべきで、可能な限り他のフローを使うべき

なので、一言で言うと、「使うな」と言うことです。 では、なぜ記載されているのか。ROPC の使い所が一つだけあります。 それは、「パスワードを扱うレガシーな Client の OAuth 2.0移行」のためです。

今回は、その移行方法について説明します。

まず、パスワードを保存しているレガシーなアプリケーションがあります。

f:id:ritou:20180926112530p:plain

まず、ROPCのフローを実装してアプリケーションを更新します。

f:id:ritou:20180926112620p:plain

Client は保存してあるパスワードを Authorization Server に送り、Access Tokenを取得します。

f:id:ritou:20180926112729p:plain

保存してあるパスワードは削除。

f:id:ritou:20180926112816p:plain

代わりにAccess Tokenを保存します。

f:id:ritou:20180926112837p:plain

これで、裏側はAccess Tokenをベースでデータをやり取りする、OAuth 2.0 の Client に移行できました。 これだけであれば、ユーザーにパスワード入れなおしてもらわなくてもできます。"Zero Friction Migration!!!"

f:id:ritou:20180926113057p:plain

裏側はAccess Tokenベースになっているので、後はアプリケーションの特性に合わせて Access Token 取得までのフローを改修してやれば良いです。

f:id:ritou:20180926112057p:plain

まとめると、

  • ROPCがRFC6749に記載されている理由は、レガシーアプリケーションからの移行のためである
  • パスワードを保存しているレガシーアプリケーションは、ROPCで Access Token に変換して保存することで
  • 保存してあるパスワードでごにょごにょやるのでユーザーにパスワード聞き直す必要もない
  • このケースじゃない場合、別のフローを使うこと考えなさい

と言う感じですね。

これで9/26時点で最新の第8回に追いつきました。 これからできることは、チャンネルをサブスクリプションして動画を再生し、Natさんのモチベーションを上げることです。

www.youtube.com

ではまた。