10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Digital Identity技術勉強会 #iddanceAdvent Calendar 2021

Day 9

FedCM(Federated Credential Management API)を実装して動かしてみた

Posted at

FedCMの現状把握

WebIDから名前が変わったFedCM(Federated Credential Management API)について、現状のステータスや実装を見ていきたいと思います。

FedCMの背景などについては以前OpenID TechNight vol.17でお話しした際の資料があるのでそちらもご参考いただけると助かります。

FedCMの参照先

FedCMの仕様や実装状況を追う上で参考にしたものは下記になります。

今回の確認内容

FedCMでは下記の3つのフローが存在しますが、まだDelegation oriented Flowは実装されていないようなため、Permission oriented FlowとMediated oriented Flowについて、正常系を動かすところまでやってみたいと思います。

  • Permission oriented Flow
  • Mediated oriented Flow
  • Delegation oriented Flow

動作確認環境

まずはこちらのドキュメントに従って、FedCMが動作する環境を準備します。

実際に動作を確認したChrome Canaryのバージョンは下記になります。
明日には仕様が変わっているかもしれませんので、現時点の実装状況であることをご了承ください。

macOS Android
98.0.4758.2 99.0.4759.0

Chrome CanaryでFedCMを有効化

Chrome Canaryのアドレスバーでchrome://flags/#fedcm と入力すると、FedCMの機能を有効化するフラグを立てることができます。
左側のボタンが「Default」となっているので、これを「Enabled」に変更します。
Screen Shot 2021-12-10 at 21.35.26.png

以上で、動作確認環境の準備は完了です。

次に実装をしていきます。

Permission oriented Flow

上記を見ながら、実装と処理の流れを追っていきます。

このフローはPCでしか動作確認できなかったのでご注意ください。

まずはRPからID Tokenのリクエストを行います。
navigator.credentials.getprovidersにはIdPの情報を、modeはPermission oriented Flowを示すpermissionを指定します。

const token = await navigator.credentials.get({
  federated: {
    providers: [{
      // IdPのURL
      url: "https://idp.example.com",
      // RPのClient ID
      clientId: "client_id",
      // nonce値
      nonce: "456"
    }],
    // Permission oriented Flowの指定
    mode: "permission"
  }
});

ID Tokenのリクエストを行うと、ユーザに対して説明のためのプロンプトが表示されます。
少しわかりづらいですが、nebula-dynamic-lynx.glitch.me はIdPのドメインで、artistic-roasted-sting.glitch.meがRPのドメインです。

Screen Shot 2021-12-13 at 7.50.32.png

ここで、「Continue」を押すと、後続の処理が実行されます。

ブラウザがprovidersで指定したIdPの.well-known/webidへリクエストをし、後続のフローで利用するエンドポイントを取得します。
ちなみにドキュメント上では.well-known/fedcmとなっていますが、現状の実装は.well-known/webidへリクエストが来ているようでした。

{
  "idp_endpoint": "https://idp.example/fedcm/idp_endpoint"
}

このあとブラウザはidp_endpointへリクエストを行い、ID Tokenの取得を試みます。
このidp_endpointはOAuthのAuthorization エンドポイントと互換性を保つように設計される想定となっているようで、scopeやclient_id、nonceなどのパラメータが付与されます。

現状はclient_idとnonceのみ指定した値が使われ、scopeはopenid profile emailが固定されています。
https://github.com/chromium/chromium/blob/016a05172b832baff78e418e51ab738257438c3c/content/browser/webid/federated_auth_request_impl.cc#L33-L47

IdPはidp_endpointへのリクエストに対して、ユーザーの状況に応じていずれかのレスポンスを返却します。

もし、ID Tokenを返却できる状況であれば、ID Tokenを返します。
これでRPに対してID Tokenが返却されID Tokenのリクエストが完結します。

{
  "id_token": "eyJ..."
}

もし、ID Tokenを返却できない状況であれば、認証などを行うためのURLをブラウザに提供します。

{
  "signin_url": "https://idp.example/fedcm/user_login"
}

ここで返却されたURLに対して、ブラウザが特別なUIと共にユーザーに明示的に同意を取得します。

Screen Shot 2021-12-13 at 8.23.12.png

Screen Shot 2021-12-13 at 8.24.44.png

最後にIdPはnavigator.id.provide()呼び出すことでID TokenをRP返却することが可能となります。

navigator.id.provide("eyJ...");

以上がPermission oriented Flowの正常系の処理となります。

Mediated oriented Flow

上記を見ながら、実装と処理の流れを追っていきます。

このフローはAndroidでしか動作確認できなかったのでご注意ください。

まずはRPからID Tokenのリクエストを行います。
navigator.credentials.getprovidersにはIdPの情報を、modeはMediated oriented Flowを示すmediatedを指定します。

デフォルト値もmediatedとなっているため、指定しなければMediated oriented Flowとなります。

const token = await navigator.credentials.get({
  federated: {
    providers: [{
      // IdPのURL
      url: "https://idp.example.com",
      // RPのClient ID
      clientId: "client_id",
      // nonce値
      nonce: "456"
    }],
    // Mediated oriented Flowの指定
    mode: "mediated"
  }
});

ブラウザがprovidersで指定したIdPの.well-known/webidへリクエストをし、後続のフローで利用するエンドポイントを取得します。
Mediated oriented Flowに関しても現状の実装は.well-known/webidへリクエストが来ているようでした。

{
  "idtoken_endpoint": "https://idp.example/fedcm/token_endpoint",
  "accounts_endpoint": "https://idp.example/fedcm/accounts_endpoint",
  "client_id_metadata_endpoint": "https://idp.example/fedcm/client_id_metadata_endpoint"
}

まずはaccounts_endpointへリクエストがされ、IdPのアカウント一覧をレスポンスで返却します。

{
 "accounts": [{
    "sub": "1234",
    "name": "John Doe",
    "given_name": "John",
    "email": "john_doe@idp",
    "picture": "https://idp.example/profile/123",
  }, {
    "sub": "5678",
    "name": "Johnny",
    "given_name": "Johnny",
    "email": "johnny@idp",
    "picture": "https://idp.example/profile/456"
  }
 ]
}

レスポンスを返すことができると、ブラウザのUIとしてアカウントチューザーが表示されます。

スクリーンショットは取れなかったため、イメージは以下のようになります。

hallowed-tattered-split.glitch.me はIdPのドメインで、artistic-roasted-sting.glitch.meがRPのドメインです。

Screen Shot 2021-12-13 at 9.09.30.png

いずれかのアカウントを選択すると、下記のような画面が表示され、ID連携をするための同意を取得します。

Screen Shot 2021-12-13 at 9.17.21.png

この際に表示する「privacy policy」や「terms of service」のリンクはclient_id_metadata_endpointのレスポンスから取得しています。

{
  "privacy_policy_url": "https://rp.example/privacy",
  "terms_of_service_url": "https://rp.example/terms"
}

ここで「Continue as John」を押すと、idtoken_endpointへリクエストが発生します。

ドキュメント上では、下記のリクエストボディとなっていますが、

{
  "account": 1234,
  "request_id": "xyz123123zyx",
  "request": {
    "client_id": "myclientid",
    "nonce": "abc987987cba"
  }
}

現状は下記のようなリクエストボディとなっていました。

{
  "request": "scope=openid profile email&client_id=client_id&nonce=456",
  "sub": "1234"
}

いずれにせよ、Authorizationエンドポイントと互換性のあるパラメータと、選択した識別子が渡ってくると思われます。

IdPはidtoken_endpointでは下記のようなレスポンスを返すことで、RPにID Tokenを返すことができます。

{
  "id_token": "eyJ..."
}

以上がMediated oriented Flowの正常系の処理となります。

実装

一応動作確認のために実装した内容は下記となっているので、とりあえず動くものという感じですが、お好きに試してみたり、コードを見てもらえればと思います。

RP

IdP(Permission oriented)

IdP(Mediated oriented)

最後に

Permission oriented FlowとMediated oriented Flowに関しては、まだドキュメントと実装が乖離している箇所もありつつ、正常系の動作は動かせるような状況となっていました。

仕様と実装が乖離している場合、chromiumのコードを参照すると最新の実装状況を把握できるため、エラー文言などを検索するなどして実装を進められました。

また、Permission oriented FlowはPC、Mediated oriented FlowはAndroid端末で動作確認可能で、デモ実装の紹介も行ったので、ぜひ手元で試してみていただければと思います。

以上、引き続き余裕があれば記事を更新していこうと思うので、引き続きよろしくお願いいたします。

最後に、記事の投稿が遅れてしまい申し訳ありませんでした:bow:

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?