【ゆるゆるとパスワードレスな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

ではまた。

文字で読みたい2分間OAuth講座 : (5) Secret of Authorization Code, (6) Actors of OAuth

こんばんは、おはようございます。 ritouです。

文字で読みたい2分間OAuth講座 : (3) Where are Sender Constrained Tokens used?, (4) Why Refresh Token? - r-weblife の続きで、今日は第5,6回の内容を紹介していきます。

動画をまだみてない人は、まずチャンネルチャンネルをサブスクライブして見てください。

www.youtube.com

では見ていきましょう。

(5) Secret of Authorization Code

www.youtube.com

これまでに、Access Token と Refresh Token を取り上げてきました。
今回はもう一つのトークン、 Authorization Code についての説明です。

Authorization Code は、ブラウザを経由して、Authorization Server から Client に渡されます。

f:id:ritou:20180925003339p:plain

仕様やフローの話をしているときに Authorization Code はトークンと呼ばれませんが、実際はトークンであり、Client の要求に対する Resource Owner(いわゆるユーザー) の許可を具体化しています。

f:id:ritou:20180925003852p:plain

そして、Authorization Code は一般的に、User Constrained Token です。(User Constrained Token とは?となったら振り出しに戻りましょ! 文字で読みたい2分間OAuth講座 : (1) The Basic Concepts (2) Bearer and Sender Constrained Tokens - r-weblife)

  • Authoization Code は Client に紐づけられている
  • Client は Client ID / Secret と共に Authorization Code を Authoization Code を送り直すことで、自分が紐づけられた Client であることを証明しつつ Access Token などを要求する

よって、Authorization Code のみを第3者に取得されても"大きな問題"にはなりません。 (モバイルアプリケーションなどで Client Secret を安全に保管できない環境で Authorization Code を取得されると Client になりすましてAccess Token などを取得されてしまうリスクはありますし、その対策もありますがここでは省略します。)

なぜこのようなステップを踏んでいるのか、その理由は Authorization Code が Client に渡される経路にあります。 Authorization Code は Resource Owner のブラウザを経由して(リダイレクトを利用して) Client に渡されます。 そのため、通信が完全には守られておらず、この経路で発行されるトークンはサーバー間の通信でやり取りされるよりも第3者に取得される可能性があります。

f:id:ritou:20180925005703p:plain

よって、このような経路で Bearer Token を送るのは BAD Idea ということです。(Implicit Grant についてはまた別途...)

f:id:ritou:20180925005901p:plain

ということで、第5回の内容は

  • Authorization Code というトークンがある
  • ブラウザ経由で送られることで盗まれる可能性があるため、User Constrained である

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

(6) Actors of OAuth

www.youtube.com

第6回は、OAuth 2.0 における登場人物について説明します。

RFC 6749 - The OAuth 2.0 Authorization Framework では4種類の登場人物が出てきます。

f:id:ritou:20180925023909p:plain

  1. Client : Protected Resource へのアクセスを行いたいエンティティ。地下鉄の例でいうと、乗客。
  2. Authorization Server : Client に各種トークンを発行するエンティティ。地下鉄の例でいうと、券売機。
  3. Resource Server : Client が Access Token を用いてアクセスする先。地下鉄の例でいうと、改札で守られた中のサービス。
  4. Resource Owner : Client にリソースアクセスを許可する人。地下鉄の例では直接出てこなかった。

Resource Owner について、地下鉄の例では、乗車料金を決める地下鉄会社の社長がその立場にあたります。 券売機では料金を払えば改札を通れる切符が買えます。そのため、実際は認可を求められるたびに出てきて許可を与える必要がありません。 SNSのユーザー情報へのアクセスを外部のサービスが要求するような場合、ユーザーが Resource Owner になって Client に許可を与えます。

ということで、第6回は OAuth の登場人物についてのお話でした。

次回予告

次回は、

  • (7) When can one use password grant?
  • (8) ROPC (Password Grant) the Migration Tool

という、パスワードを扱うフローについての回を説明します。 気になる方はチャンネルをサブスクライブして今から見ちゃいましょう。

www.youtube.com

ではまた。

FAPI : JWT Secured Authorization Response Mode for OAuth 2.0 (JARM) とは

ritou です。

みんな大好きJWT。今日もみんなで eyJ!ということで、今回はこちらの仕様について説明します。

openid.net

概要

This document defines a new JWT-based mode to encode authorization responses. Clients are enabled to request the transmission of the authorization response parameters along with additional data in JWT format. This mechanism enhances the security of the standard authorization response since it adds support for signing and encryption, sender authentication, audience restriction as well as protection from replay, credential leakage, and mix-up attacks. It can be combined with any response type.

この仕様を3行で説明しろと言われたらこんな感じでしょう。

  • JWT形式で Authorization Response を返すための仕様です
  • Response Mode を指定するための response_mode パラメータも拡張する
  • Client / Authorization Server それぞれの metadata も拡張する

まずは Response Mode の振り返りからはじめましょう。

これまでの Response Mode とは

response_mode パラメータが定義されているのは、この仕様です。

openid.net

Response Mode

The Response Mode determines how the Authorization Server returns result parameters from the Authorization Endpoint. Non-default modes are specified using the response_mode request parameter. If response_mode is not present in a request, the default Response Mode mechanism specified by the Response Type is used.

Response Mode は Authorization Server が(認可処理の)結果のパラメータを Authorization Endpoint から「どのように返すか」を決定します。
デフォルト値ではないmodeは response_mode というリクエストパラメータを用いて指定され、指定されない場合は Response Type に対応した デフォルトの Response Mode が利用されます。

OAuth 2.0 の Response Type で表現すると

  • response_type=code : いわゆる Authorization Code Grant であり、Authorization Response はクエリパラメータとして指定される -> デフォルトの Response Mode は query
  • response_type=fragment : いわゆる Implicit Grant であり、Authorization Response はフラグメント部分に含まれる -> デフォルトの Response Mode は fragment

という感じになります。

また、こちらの仕様により response_mode パラメータに form_post が追加されました。

openid.net

例を見て見ましょう。まず Authorization Request に response_mode パラメータが指定されます。

GET /authorize?
   response_type=id_token
   &response_mode=form_post
   &client_id=some_client
   &scope=openid
   &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback
   &state=DcP7csa3hMlvybERqcieLHrRzKBra
   &nonce=2T1AgaeRTGTMAJyeDMN9IJbgiUG HTTP/1.1
  Host: server.example.com

Authorization Response は HTML Form を用いた POST リクエストにより返されます。

  HTTP/1.1 200 OK
  Content-Type: text/html;charset=UTF-8
  Cache-Control: no-cache, no-store
  Pragma: no-cache

  <html>
   <head><title>Submit This Form</title></head>
   <body onload="javascript:document.forms[0].submit()">
    <form method="post" action="https://client.example.org/callback">
      <input type="hidden" name="state"
       value="DcP7csa3hMlvybERqcieLHrRzKBra"/>
      <input type="hidden" name="id_token"
       value="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiw
         iYXVkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLC
         Jpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5M
         DMxMTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1B
         SnllRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0F
         NTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2Mz
         kwMDg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqq
         wwq-Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fk
         IufbGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcq
         XVEKhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_h
         nrQ5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc
         0uVAwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA"/>
    </form>
   </body>
  </html>

最終的に、Client に送られるのは以下のようなPOSTリクエストになります。

  POST /callback HTTP/1.1
  Host: client.example.org
  Content-Type: application/x-www-form-urlencoded

  id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiwiYX
         VkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLCJpc
         3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5MDMx
         MTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1BSnl
         lRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTD
         oyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2MzkwM
         Dg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqqwwq
         -Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fkIuf
         bGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcqXVE
         KhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_hnrQ
         5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc0uV
         AwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA&
  state=DcP7csa3hMlvybERqcieLHrRzKBra

Response Mode についてなんとなくわかりましたでしょうか。 今回の仕様では、この辺りをさらに拡張するぞ!って話です。

JWT形式で Authorization Response を返すための Response Mode

仕様では Authorization Response に含まれる JWT の内容から先に定義されていますが、Response Mode のあたりから説明していきます。

この仕様で定義されている response_mode パラメータは次の4つです。

  • query.jwt : JWT形式の Authorization Response をクエリパラメータに指定する
  • fragment.jwt : JWT形式の Authorization Response をフラグメントに指定する
  • form_post.jwt : JWT形式の Authorization Response を HTTP POST で送る
  • jwt : Response Type に対応した Response Mode にて JWT形式の Authorization Response を返す
    • response_mode=jwt&response_type=code : response_mode=query.jwt
    • response_mode=jwt&response_type=(code, none以外) : response_mode=fragment.jwt

Authorization Response の例の前に、response_type パラメータ毎にどのようなJWTになるかを見ていきましょう。

JWT Response Document

JWTは以下のデータを必ず含みます。

  • iss : レスポンスを作成した Authorization Server の Issuer URL
  • aud : レスポンスの対象である Client の client_id
  • exp : JWT の有効期限

さらに、JWTはエラーレスポンスの場合でも Authorization Endpoint のレスポンスパラメータを含みます。 複数の Response Type の組み合わせに対しても適用されますが、この仕様では code, token について示します。 OIDC の Session Management のような拡張仕様で定義されているレスポンスパラメータもJWTに含まれます。

Response Type が "code" である時の Authorization Response

いわゆる Authorization Code Grant のレスポンスが JWT に含まれます。

  • code : Authorization Code
  • state : リクエストに含まれる state パラメータ

JWT の Payload は以下のようになります。

{
   "iss":"https://accounts.example.com",
   "aud":"s6BhdRkqt3",
   "exp":1311281970,
   "code":"PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA",
   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw"
}

エラーレスポンスの時は JWT に RFC6749 の sections 4.1.2.1 で定義されているエラーレスポンスパラメータを含みます。

  • error
  • error_description (OPTIONAL) - 人間が読めるようなエラー内容
  • error_uri (OPTIONAL) - エラーの内容についてのURL
  • state

JWT の Payload は以下のようになります。

{
   "error":"access_denied",
   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw"
}

エラーレスポンスについては Client に戻すべきエラーとそうでないものがあるので、その辺りは今まで通り気をつける必要がありそうです。

Response Type が "token" である時の Authorization Response

Implicit Grant の場合は次のようになります。

  • access_token - Access Tokenの値
  • token_type - Access Token の種類。 "Bearer" など
  • expires_in - Access Token の有効期限
  • scope - Access Token の Scope
  • state

RFC6749 と照らし合わせると、Scope は オプションですかね。

{
   "iss":"https://accounts.example.com",
   "aud":"s6BhdRkqt3",
   "exp":1311281970,
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw",
   "token_type":"bearer",
   "expires_in":"3600",
   "scope":"example"
}

エラーレスポンスについては code の場合と同様です。

Signing and Encryption

上記のレスポンスパラメータはJWS で署名をつける もしくは JWE にて暗号化されます。 Authorization Server は 自分自身とClient の metadata を用いてアルゴリズムを決定します。

次は Response Mode 毎の Authorization Response を見ていきましょう。

Response Mode "query.jwt"

response というクエリパラメータとしてJWTの値が指定され、HTTP リダイレクトにより Client に送られます。 フォーマットは application/x-www-form-urlencoded です。

HTTP/1.1 302 Found
Location: https://client.example.com/cb?
response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLm
V4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTMxMTI4MTk3MCwiY29kZSI6IlB5eU
ZhdXgybzdRMFlmWEJVMzJqaHcuNUZYU1FwdnI4YWt2OUNlUkRTZDBRQSIsInN0YXRlIjoiUzhOSjd1cW
s1Zlk0RWpOdlBfR19GdHlKdTZwVXN2SDlqc1luaTlkTUFKdyJ9.HkdJ_TYgwBBj10C-aWuNUiA062Amq
2b0_oyuc5P0aMTQphAqC2o9WbGSkpfuHVBowlb-zJ15tBvXDIABL_t83q6ajvjtq_pqsByiRK2dLVdUw
KhW3P_9wjvI0K20gdoTNbNlP9Z41mhart4BqraIoI8e-L_EfAHfhCG_DDDv7Yg

response_typetoken, id_token が含まれ、 JWE を"使わない"時、 URL からの漏洩を防ぐために query.jwt の値を使ってはいけません。

Response Mode "fragment.jwt"

フラグメント部に response というパラメータとしてJWTの値が指定され、HTTP リダイレクトにより Client に送られます。 フォーマットは application/x-www-form-urlencoded です。

HTTP/1.1 302 Found
Location: https://client.example.com/cb#
response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLm
V4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTMxMTI4MTk3MCwiYWNjZXNzX3Rva2
VuIjoiMllvdG5GWkZFanIxekNzaWNNV3BBQSIsInN0YXRlIjoiUzhOSjd1cWs1Zlk0RWpOdlBfR19GdH
lKdTZwVXN2SDlqc1luaTlkTUFKdyIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJleHBpcmVzX2luIjoiMz
YwMCIsInNjb3BlIjoiZXhhbXBsZSJ9.bgHLOu2dlDjtCnvTLK7hTN_JNwoZXEBnbXQx5vd9z17v1Hyzf
Mqz00Vi002T-SWf2JEs3IVSvAe1xWLIY0TeuaiegklJx_gvB59SQIhXX2ifzRmqPoDdmJGaWZ3tnRyFW
NnEogJDqGFCo2RHtk8fXkE5IEiBD0g-tN0GS_XnxlE

Response Mode "form_post.jwt"

response というHTML フォームの値としてJWTの値が指定され、User-Agent内で自動的に送信されることで HTTP POSTメソッドにより Client に送られます。 フォーマットは application/x-www-form-urlencoded です。

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-cache, no-store
Pragma: no-cache

<html>
 <head><title>Submit This Form</title></head>
 <body onload="javascript:document.forms[0].submit()">
  <form method="post" action="https://client.example.com/cb">
    <input type="hidden" name="response"
     value="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2
      FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTM
      xMTI4MTk3MCwiYWNjZXNzX3Rva2VuIjoiMllvdG5GWkZFanIxekNzaWNNV3BBQSIs
      InN0YXRlIjoiUzhOSjd1cWs1Zlk0RWpOdlBfR19GdHlKdTZwVXN2SDlqc1luaTlkT
      UFKdyIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJleHBpcmVzX2luIjoiMzYwMCIsIn
      Njb3BlIjoiZXhhbXBsZSJ9.bgHLOu2dlDjtCnvTLK7hTN_JNwoZXEBnbXQx5vd9z1
      7v1HyzfMqz00Vi002T-SWf2JEs3IVSvAe1xWLIY0TeuaiegklJx_gvB59SQIhXX2i
      fzRmqPoDdmJGaWZ3tnRyFWNnEogJDqGFCo2RHtk8fXkE5IEiBD0g-tN0GS_XnxlE"/>
    </form>
   </body>
  </html>

Client には HTTP POSTなリクエストが送られます。

POST /cb HTTP/1.1
  Host: client.example.org
  Content-Type: application/x-www-form-urlencoded

  response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2
      FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTM
      xMTI4MTk3MCwiYWNjZXNzX3Rva2VuIjoiMllvdG5GWkZFanIxekNzaWNNV3BBQSIs
      InN0YXRlIjoiUzhOSjd1cWs1Zlk0RWpOdlBfR19GdHlKdTZwVXN2SDlqc1luaTlkT
      UFKdyIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJleHBpcmVzX2luIjoiMzYwMCIsIn
      Njb3BlIjoiZXhhbXBsZSJ9.bgHLOu2dlDjtCnvTLK7hTN_JNwoZXEBnbXQx5vd9z1
      7v1HyzfMqz00Vi002T-SWf2JEs3IVSvAe1xWLIY0TeuaiegklJx_gvB59SQIhXX2i
      fzRmqPoDdmJGaWZ3tnRyFWNnEogJDqGFCo2RHtk8fXkE5IEiBD0g-tN0GS_XnxlE

Processing rules

Authorization Response を受け取った Client 側の検証処理について説明します。

  1. (OPTIONAL) JWT ヘッダパラメータの kid の値を用いてJWTをdecryptする
  2. state パラメータを取得して User-Agent との紐付けを検証(CSRF対策) state パラメータはワンタイムなCSRF対策トークンとして利用され、検証完了後破棄されます。
  3. iss の値を検証 (期待したものと一致するかどうか)
  4. aud の値を検証 (client_id と一致するかどうか)
  5. exp の値を検証
  6. iss, kid の組み合わせから署名を検証. 鍵周りの詳細はこの仕様の範囲外。

いずれの検証も失敗したらすぐにエラーを返します。 これらの検証が終わるまでは、Authorization Response の中身である codeaccess_token などの値を利用してはいけません。

Metadata

Client Metadata

Client 側で期待するJWTの署名や暗号化のアルゴリズムなどを定義できます。詳細は仕様みてください。

  • authorization_signed_response_alg
  • authorization_encrypted_response_alg
  • authorization_encrypted_response_enc

Authorization Server Metadata

Authozation Server 側でサポートしている値を定義できます。詳細は仕様みてください。

  • authorization_signing_alg_values_supported
  • authorization_encryption_alg_values_supported
  • authorization_encryption_enc_values_supported
  • response_modes_supported : サポートする response_mode パラメータ

OAuth Server Metadata についてはこちらのRFCを参照してください。

RFC 8414 - OAuth 2.0 Authorization Server Metadata

Security considerations

  • DoS using specially crafted JWTs : でかい JWK Set を処理する感じになってネットワークやCPUを圧迫するとか、JWK Set URL が DDos のターゲットみたいになるとか。鍵周りはよく考えて設計する必要がありますね。
  • Code Replay : 同一 Client, 別のユーザー向けの Authorization Code を含む攻撃は、state パラメータの検証で対応可能です。
  • Mix-Up : あるClientと複数のAuthorization Serverがやり取りする場合のMix-Up 攻撃については 図解:OAuth 2.0に潜む「5つの脆弱性」と解決法 (4/4):デジタルID最新動向(2) - @IT 見れば良いと思います。iss, aud の検証をしっかりやる必要があります。
  • Code Leakage : 暗号化すれば code の値も外から見えなくなります。

まとめ

  • Authorization Response に JWT を使う方法が定義されている
  • Response Type, Response Mode による挙動とJWTの中身の違いに注目せよ
  • JWT生成のところで鍵周りとかは仕様範囲外なので、よく考えて設計する必要がある

というところでしょうか。

もっと FAPI について知りたい?

ひたすら仕様を読む会ってのがあるみたいです。

peatix.com

今回紹介した JARM も入ってますが、既にあなたは「JARM完全に理解した」状態なので余裕ですね。 お時間とか色々と余裕がある方は是非参加してみてください。

ところで、"JARM" ってどう読むの??? ではまた。

文字で読みたい2分間OAuth講座 : (3) Where are Sender Constrained Tokens used?, (4) Why Refresh Token?

おはようございます!こんにちは!こんばんは! ritouです。

(2019/4/1 : Sender Constrained Token と表記する部分を User Constrained Token としていたので修正しました。ご指摘ありがとうございます!)

文字で読みたい2分間OAuth講座 : (1) The Basic Concepts (2) Bearer and Sender Constrained Tokens - r-weblife の続きで、第3, 4回目について説明します。

(3) Where are Sender Constrained Tokens used?

www.youtube.com

第2回で登場した Sender Constrained Token ですが、OAuth 2.0のどこで使われているのでしょうか? RFC6749には出てこない?それでは見ていきましょう。

OAuth 2.0のフローのうち、最も使われているのは Authorization Code Grant と呼ばれるものです。 その中で出てくるトークン(的なもの)に Sender Constrained Token が含まれています。 ここからじゃんじゃんスクショ貼って行くぞ!

Authorization Code Grant

ユーザーは Web アプリケーションである Client のサービスをブラウザから使おうとします。

f:id:ritou:20180920003441p:plain

ブラウザは Client にリクエストを送ります。

f:id:ritou:20180920003427p:plain

Client は認可リクエストを生成します。

f:id:ritou:20180920003606p:plain

Client はブラウザのリダイレクトを利用してユーザーを Authorization Server に送ります。

f:id:ritou:20180920003631p:plain

Authorization Server はユーザー認証をします。

f:id:ritou:20180920003650p:plain

Authorization Server は Client が要求している内容をユーザーに示して、承認を求めます

f:id:ritou:20180920003709p:plain

ユーザーが承認のアクションをとります。

f:id:ritou:20180920003729p:plain

Authorization Server は Client (のID)に紐づいた"Authorization Code"と呼ばれる "Sender Constrained Token" を生成します。

f:id:ritou:20180920003752p:plain

Authorization Server はブラウザのリダイレクトを利用して、"Authorization Code" を含む認可応答を Client に返します。

f:id:ritou:20180920003809p:plain

Client は "Authorization Code"、Client ID、Secret を含むトークン要求を Authorization Server に送ります。 Clientは自らを認証するためにClient IDとSecretを含みます。

f:id:ritou:20180920003829p:plain

Authorization Server は、トークン要求に含まれる "Authorization Code" が正しい Client から送られてきたものか確認します。

"User Constrained Token" 的には、

  • Authorization Code に紐づく Client とトークン要求を送った Client が一致すること
  • Client ID / Secret の組み合わせが正しいこと

という確認をすることで正しいClientから送られてきたとみなします。

f:id:ritou:20180920003839p:plain

Authorization Server は "Access Token" と "Refresh Token" を返します。 ここで、RFC6749(+RFC6750の組み合わせ)の"Access Token"は、 "Bearer Token"、"Refresh Token" は "Sender Constrained Token" です。

f:id:ritou:20180920003850p:plain

Client は Access Token を利用してリソースアクセスを行います。あとはよしなに。

f:id:ritou:20180920003900p:plain

まとめ

第3回のまとめとしては、

  • OAuth 2.0 の Authorization Code Grant について説明した
    • その中で出てくる、"Authorization Code" と "Refresh Token" は いわゆる "Sender Constrained Token" である
  • 他に "Sender Constrained Token" の使いどころを知りたければ、"FAPI" で検索しましょう。

となります。

ここでなぜ Refresh Token の使い方を説明しないのかと思われるでしょう。 第4回は "Refresh Token" についての内容となります。

(4) Why Refresh Token?

www.youtube.com

Refresh Token は Authorization Server に対して"のみ"利用されるトークンです。 第4回では、OAuth 2.0 の Refresh Token がなぜ必要なのかについて説明します。

OAuth 2.0 の Access Token は、(scopeの概念を入れなければ)複数の Protected Resource に対して利用できます。

f:id:ritou:20180920103130p:plain

"Access Token" が "Bearer Token" であり、Protected Resource のいずれかがハックされて攻撃者の支配下に置かれた場合を考えます。

f:id:ritou:20180920103339p:plain

攻撃者はハックした Protected Resource にて取得した "Access Token" を用いて、別の Protected Resource にアクセスできます。

f:id:ritou:20180920103441p:plain

第3者に "Bearer Token" を使われてしまう、このような被害を"軽減"させるために、"Access Token" に短い有効期限を設定する方法があります。

有効期限を短く設定することで、頑張って "Access Token" を取得できた攻撃者が別の Protected Resource に利用しようとしてもエラーとなります。

f:id:ritou:20180920104200p:plain

しかし、Client が "Access Token" のみを保存して利用する場合、ユーザーに対して頻繁にリソースアクセスの承認を求める必要があり、ユーザーがオンライン状態でなければゲームオーバーです。オンラインだったとしてもメンドクセ。

この状況を改善するため、 "Refresh Token" が利用されます。 "Refresh Token" は Authorization Server だけに送られるトークンであり、通常は長い有効期限が設定されています。 Protected Resource をハックした攻撃者に "Refresh Token" が送られることはありません。 Client はこれを Authorization Server に送り、新しい Access Token を取得します。 Access Token を Refresh するためのトークンなので "Refresh Token" と言うわけです。 ちなみに、第3回で説明した通り "Refresh Token" は”Sender Constrained Token” なので、Client ID / Secret などと共に送られ、Authorization Server は検証を行います。

と言うことで、第4回は "Bearer Token" な "Access Token" を悪用されるリスクを軽減されるために、"Refresh Token" が使われるよっていうお話でした。

今回は動画をそのまま文字にするだけじゃなくて、色々補足しようと思ったら長くなりました。これが良いのかどうかはわからん... 次回予告です。

  • (5) Secret of Authorization Code : Authorization Code についての説明!?
  • (6) Actors of OAuth : 第1回よりもちょっと詳しく登場人物を説明!?

ではまた。

この投稿をみて、動画に興味を持っていただいた方は是非、チャンネルをサブスクライブしてください!!! www.youtube.com

文字で読みたい2分間OAuth講座 : (1) The Basic Concepts (2) Bearer and Sender Constrained Tokens

おはようございます!こんにちは! ritouです。

みんな知ってる Nat Sakimura 氏が YouTube で公開されている、いわゆる2分間OAuth講座ってのがあります。

www.youtube.com

英語で話されているものの、日本語字幕もあるし、とにかくわかりやすい。 しかし、世の中には変わった人もいるものです。 文章に落として説明してくれと頼まれましたので、2回ぶんずつ書いていくぞ!

(1) The Basic Concepts of OAuth

www.youtube.com

多くの人にOAuthは難しいと言われます。 しかし、地下鉄の乗り方がわかるなら、そんなに難しいことではありません。

OAuthとは、価値のある資源「Protected Resource」を保護するメカニズムです。

地下鉄に乗車することを考えてみましょう。 誰でもタダで乗れる訳ではありません。改札があります。 改札を通るためには、券売機で切符を買う必要があります。

OAuth では、券売機のことを Authorization Server, 切符のことを Access Token と呼びます。 Access Token は紙の切符のようにワンタイムのものや、定期のように有効期限が決められているものもありますが...とりあえずこれがあれば改札を通れます。 ということで、第1回でOAuthについて覚えておく必要があるのは

  • Authorization Server : 地下鉄の券売機
  • Protected Resources : 改札を通らなければ乗れない地下鉄
  • Access Token : 切符

の3点です。 次回は切符、つまり Access Token の種類について説明します。

(2) Bearer and Sender Constrained Tokens of OAuth

www.youtube.com

今回は2種類のAccess Tokenを紹介します。

  • Bearer Token : 第1回で説明した地下鉄の切符のようなトークン。所持していれば使えるので、他の人が拾っても使えます。
  • Sender Constrained Token : 利用者を制限するトークン。飛行機の搭乗券のように、利用者が指定されている、制限されているもの。

飛行機の搭乗券は通常、

  • 搭乗券の名前とパスポートに記載されている名前が一致している
  • パスポートの顔写真と搭乗者の顔が一致している

という2点を確認することで、搭乗者と搭乗券が紐づきます。

OAuthの場合、そのような紐付け、確認のために暗号鍵を利用します。 トークンに紐付く鍵を持っている必要があることから、"Holder of Key Token" とも呼ばれます。

OAuth 2.0では、これら2種類のトークンを扱えます。

リスクの低いユースケースではBearer Tokenが使われ、銀行取引などではUser Constrained Tokenが使われます。

こんなところでしょうか。

元がシンプルなやつを落とそうとすると結構時間かかりますね。 次回は(3), (4)について書きます。もうちょっと補足を増やすのが良さそう。

ではまた。

この投稿をみて、動画に興味を持っていただいた方は是非、チャンネルをサブスクライブしてください!!! www.youtube.com

builderscon tokyo 2018 でパスワードレスについて話しました & idcon に行こう

(ふと描き忘れたなというところを追記してます)

どこで何を話したのか

builderscon tokyo 2018 にてお話しして来ました。

builderscon.io

資料もアップロード済みです。

内容は以下について緩く話して来ました。

  • パスワード認証 : そもそも人類には無理そう
  • あるある実装 : パスワード認証 + リカバリー、最近多要素認証。ソーシャルログインはパスワードとセットになってる。
  • WebAuthn : 細かい生体認証とかを頑張るのは Authenticator なので、導入するサービスは大枠の仕組みを実装すれば良い
  • パスワードレスに向かうためには : パスワード認証に依存している部分を意識すれば今ある認証方式でもいける

最初から難しい話をするつもりはなく、パスワード認証やめようってなったときに自分はこう考えて言ったよっていうのをベースに、新しい技術の話も聞きたいでしょ。でも昼過ぎに細けぇ話したらみんな寝るだろやめとくわみたいなところでした。 台本用意してたはずが飛んで時間が早く終わってしまったので、何個か質問してもらいました。WebAuthn 関連が多かった気がします。

  1. Authenticator管理などのプラクティスについて : USBのAuthenticatorを無くした時や、複数の環境でいろんなAuthenticator使いたい時のカオス感など。細かいところは色々決まってなさそう
  2. OAuth 2.0 / OpenID Connect との関係 : WebAuthn は IdPがユーザー認証するところで使われるイメージ
  3. idcon について : ガチな参加者はいるか?いる

質問の内容、特に後半は明らかに気を使ってもらってる感もありますが、1の少し実践的な運用のあたりに興味を持ってもらったということは、未来は明るい気がしました。

今回のお話を聞いて、少しでもID周りや最新の認証方式に興味を持ってもらえたらいいなというところで、idcon やってる、idconに来いというのを最後に書いときました。

ということで、idconの告知をせねばなるまいとこのブログ記事を書いたわけです。

idcon に行こう

良いタイミングで次の idcon やるってのを、聞きました(運営してる詐欺) いや、発表当日にこの募集してあったら完璧だったと思いますけれども。

fido2-workshop.connpass.com

内容も FIDO / WebAuthn で、中身も参加者もガチです。 私の一押しはやはりGooglerなあの人かなっ!

質問したら期待以上の答えが帰ってくること間違い無いでしょう。 よくみたらもう満席ですが、ぜひご参加ください。

ついでに

放置してたブログも引っ越しました。 技術的な投稿はQiitaに書く予定ですが、告知みたいなのはこっちで。 次はいつ書くのか、それは知らん。

ではまた。