Quantcast
Channel: IdM実験室
Viewing all 775 articles
Browse latest View live

Worldcoinによる金融包摂とデジタルアイデンティティ

$
0
0

こんにちは、富士榮です。

2023年も終盤に近づいてきたということで、今年もDigital Identity技術勉強会 #iddance Advent Calendar 2023に参加しています。


今回のネタはWorldcoinで話題のWorld IDを探っていきたいと思います。

ということでこんな内容でお届けします。

  • Worldcoinとは
  • World IDの取得と確認
  • Identity ProviderとしてのWorld ID
  • Incognito Actions(おまけ)

なお、お約束ですが本記事ではWorldcoinを含む特定の仮想通貨の購入や投資などを推奨するものではありません。あくまでDigital Identity関連技術の観点からWorldcoin/IDを見ていきたいと思います。


Worldcoinとは

Worldcoinというキーワードを検索するとOpenAIの創業者であるSam Altman氏が共同創業者した仮想通貨のプロジェクトという切り口で紹介されている記事が多くヒットしますが、本家Worldcoinのサイトを見るとこのように記載されています。

Worldcoin is an open-source protocol, supported by a global community of developers, individuals, economists and technologists committed to expanding participation in, and access to, the global economy. The Worldcoin Foundation is the steward, and will support and grow the Worldcoin community until it becomes self-sufficient. Tools for Humanity helped launch Worldcoin, and currently serve as advisors to the Foundation and operators of the World App.(原文)

ワールドコインはオープンソースのプロトコルであり、開発者、個人、経済学者、技術者からなるグローバルコミュニティによって支えられている。Worldcoin財団はスチュワードであり、Worldcoinコミュニティが自立するまでサポートし、成長させる。Tools for Humanityはワールドコインの立ち上げを支援し、現在は財団のアドバイザーとワールドアプリの運営者を務めている。(Deeplで機械翻訳) 

また、同サイトにはWorldcoinに関するホワイトペーパーも公開されており、その中に端的にWorldcoinはこのように解説されています。
Worldcoin was founded with the mission of creating a globally-inclusive identity and financial network, owned by the majority of humanity. If successful, Worldcoin could considerably increase economic opportunity, scale a reliable solution for distinguishing humans from AI online while preserving privacy, enable global democratic processes, and show a potential path to AI-funded UBI.(原文)

Worldcoinは、人類の大多数が所有する、グローバルに包括的なアイデンティティと金融ネットワークを構築することを使命として設立された。成功すれば、ワールドコインは経済機会を大幅に拡大し、プライバシーを守りながらオンライン上で人間とAIを区別するための信頼できるソリューションを拡大し、グローバルな民主的プロセスを可能にし、AIが資金を提供するUBIへの潜在的な道を示すことができる。(Deeplで機械翻訳)

要するに金融包摂の文脈で設立されたデジタルアイデンティティと金融ネットワークで、UBI(ユニバーサル・ベーシック・インカム)を実現することも一つのゴールとして定めている様です。

また、同時にWorldcoinを構成する要素についても以下のように説明が記載されています。

Worldcoin consists of a privacy-preserving digital identity network (World ID) built on proof of personhood and, where laws allow, a digital currency (WLD). Every human is eligible for a share of WLD simply for being human. World ID and WLD are currently complemented by World App, the first frontend to World ID and the Worldcoin Protocol, developed by the contributor team at Tools for Humanity (TFH).(原文)

ワールドコインは、個人であることの証明の上に構築されたプライバシーを保護するデジタルIDネットワーク(ワールドID)と、法律が認めるデジタル通貨(WLD)で構成されている。すべての人間は、人間であるというだけでWLDの分け前を得る資格がある。World IDとWLDは現在、Tools for Humanity (TFH)の貢献者チームによって開発された、World IDとWorldcoinプロトコルの最初のフロントエンドであるWorld Appによって補完されている。(Deeplで機械翻訳) 

なるほど、Worldcoinのサイトのトップに記載されていたWorld ID、WLD、World Appはそれぞれ以下のような関係性だということがわかります。(トップページからリンクされるそれぞれのページの解説文からも補完)

  • World ID:デジタルIDネットワーク
    • A more human passport for the internet. More powerful. More integrations. More human.(原文)
    • インターネットのより人間的なパスポート。よりパワフル。より多くの統合。より人間的に。(Deeplで機械翻訳)
  • WLD:デジタル通貨(Worldcoin Token)
    • A more human token freely, equally, and globally distributed to unique humans.(原文)
    • より人間的なトークンは、自由に、平等に、そしてグローバルに、ユニークな人間に配布される。(Deeplで機械翻訳)
  • World App:フロントエンドアプリケーション
    • More human access to the global economy with identity and finance for all. Privacy‑first. No fees. 24 hour support.(原文)
    • すべての人のためのアイデンティティと金融で、より人間らしいグローバル経済へのアクセスを。プライバシー第一。手数料無料。24時間サポート。(Deeplで機械翻訳)
なんとなくわかってきました。
要するに金融包摂(世の中には銀行等の金融システムにアクセスできない人たち、要するに銀行口座が持てず社会経済に参加できない人たちが多く存在する。その人たちの経済参加を促進すること。この辺りはThe World Bankのページが参考になります。)を実現するには、人類が区別されることなくアクセスできるアイデンティティ・ネットワークに支えられたユニバーサルなベーシック・インカムを含む金融システムが必要であり、Worldcoinはその理想を実現するためのプロジェクト、という位置付けということです。


World IDの取得と確認

では、早速World IDを取得してみたいと思います。
World IDアカウントを作成するのに必要となるのは、以下のものです。
  • スマートフォン(iOS/Android)にインストールするWorld App
  • SMSの受信できる携帯電話番号(なくてもアカウントは作成できます)
スマホを持っていない人は金融包摂的にどうなるのか?という疑問は抱きつつ、World Appをインストールしセットアップを続けます。

もちろんこの状態でもWorld IDは作成されているのですが、World IDのもう一つの特徴は本当に人(Human)であることをOrbというデバイスを使って確認(物理的にOrbが置いてある場所まで行って対面で確認を行う)を行うことにあります。

まだID確認を行なっていない状態だとアプリの上部にVerify your World IDというリンクが表示されるのでタップすると近所に存在するOrbの場所が表示されますので、場所によっては予約をした上で現地を訪問して存在確認を行います。なお、この際に虹彩登録を行う必要があることから謎のシステムやデバイスに生体情報を提供するのはどうなのか?という議論も存在します。確かに私も渋谷の指定の場所へ行ってOrbでID確認をしてきましたが、かなりカジュアルな感じで登録ができてしまうので、偽Orbを作って生体情報だけを搾取する人たちが出てきても不思議ではありません(Orbを操作してくれる人も、Orbそのものについても登録しようとしている人から見て正当性を確認する手段がない)。この辺りは今後の課題なんだと思います。

なお、ID確認時には法的なアイデンティティ・ドキュメント(免許証などの身分証明書)の提示は求められないので、この辺りは金融包摂のため、という考え方がベースにあるのだと思います。ただ、一人のユーザが複数のWorld IDを取得できないようにするための虹彩情報の突合・重複確認がどこまで本当に実施されているのか?などはよくわかりません。

こんな感じのデバイスが置いてあるのでアプリのQRを読み込ませた後で虹彩登録を行います。登録自体は5分もかからずサクッと終わります。


なお、このID確認を行わないとベーシック・インカムとなるWLDを受け取ったり取引することができません。ちなみにWorld Appをインストールすると定期的(週一回くらい?)にGrantといってベーシック・インカムとしての3〜10程度のWLDが付与されます。現時点で1WLDは日本円で368〜9円程度なので、隔週で1,000円もらえる感じです。実際に利用する際は暗号資産取引所などで日本円へ換金することもできるんじゃないかと思います。(やったことありませんが)※追記:12/18の朝にみたら爆上がりしてました。624円とかになってました。

いずれにしても確認が終わるとVerifiedとして記録がされます。



Identity ProviderとしてのWorld ID

さて、デジタルIDの話なので、あまり仮想通貨の話に行っても仕方がないのでWorld IDの使い方の話をしたいと思います。

非常にざっくりいうと、World IDもOpenID Connectに対応したIdentity Providerとしての機能を持っています。

開発者ポータルへアクセスし、サインアップすると一般のIdentity Providerと同じようにクライアント登録などができる仕組みが提供されています。

ちなみにAuth0(Okta CIC)ではすでにマーケットプレイスにSign in with Worldcoinの機能が提供されていますので、クライアントID/シークレットの登録とRedirectURIを設定すると簡単にWorld IDによるサインインが実装できます。

開発者ポータルでClient ID/Secret/RedirectURIの設定を行います。

こちらはAuth0(Okta CIC)の画面。Social Connectionsとして定義します。

Sign in with Worldcoinを起動するとQRコードが出てくるので、World AppでQRコードを読み込み、サインインします。

World App側でQRコードを読み込むとVerify with World IDの画面が表示される、確認に成功するとサインインされます。


World IDのOpenID Connectの実装に関してはドキュメントが公開されているので、こちらを見ると自作のWebアプリケーションや他のIDaaSへの接続も容易です。(最近World IDのバージョンが2.0に更新され、PKCEのサポートなどが追加されました)

簡単なRelying Partyを書いて実際のid_tokenの中身などを確認していきましょう。細かいコードは省略しますが、結果的にこんなid_tokenが返ってきます。


ポイントはこの辺りです。ドキュメントをみるとhttps://id.worldcoin.org/betaのエレメントはdepricateっぽいですが、verification_levelとしてOrbで確認済みのWorld IDかどうかがわかるような仕組みになっています。

  "https://id.worldcoin.org/beta": {

    "likely_human": "strong",

    "credential_type": "orb"

  },

  "https://id.worldcoin.org/v1": {

    "verification_level": "orb"

  },

ちなみに、Orbで確認を行なっていないWorld IDでもアクセスしてみるとverification_levelがdeviceとなっています。

  "https://id.worldcoin.org/beta": {

    "likely_human": "weak",

    "credential_type": "device"

  },

  "https://id.worldcoin.org/v1": {

    "verification_level": "device"

  },


ところで、Auth0でも簡単なRelying Partyでも実装できたので、ついでにAzure AD B2Cでも実装してみようと思います。Discoveryもサポートしているし、openid-configurationを見るといつもの鬼門のresponse_mode=form_postもサポートしているようなので試してみます。

こんな感じでIDプロバイダーを設定します。

ところが、response_modeをform_postにするとWorld IDのAuthorizationエンドポイントでWorld Appでサインインしたところjsのエラーが出て止まってしまいます。どうもCSPの設定がうまくできていないみたいですね。。。


ということで、response_modeをqueryに変更して再度試してみるとTokenエンドポイントへのアクセスでエラーが出ているようです。
ここは結構ハマったのですが、Postmanなどで順番にアクセスするとちゃんとid_tokenが取得できるので、Azure AD B2CがTokenエンドポイントへアクセスする際に何か変なヘッダがついていたりするんだろうな、、(あるいは逆にUser-Agentがないと返事をしないTokenエンドポイントがいたりするのでそういうケースかも、、)と思いダミーのTokenエンドポイントを作りPostman経由の場合とヘッダを比較。

結果、

expect: '100-continue'

が犯人でした。

PostmanでもexceptヘッダをつけるとWorld IDのTokenエンドポイントがエラーを出しました。どうやらContent-Lengthの事前通知には対応していないようです。

とはいえ、APIクライアントとなるAzure AD B2C側のリクエストをここまで細かいレベルで調整するのはカスタムポリシーを使っても無理っぽいので、現時点では諦めます。(どうしても実装する場合はTokenエンドポイントとの間にゲートウェイモジュールを立ててProxyさせる形になると思います)


Incognito Actions(おまけ)

ここからはおまけですが、World IDにはIncognito Actionsという機能があります。これはユーザがSign in with World IDを含むVerifyをする対象のアプリケーションを簡単に作るための機能です。(例えば、投票などが想定されています)
開発者ポータルからIncognito Actionsメニューから定義体を作っていくことができますが、その際にVerifyの回数の制限などをつけることができます。
例えば、Uniqueだと1つのWorld IDにつき1回しかVerifyができないようなアプリケーションが作れますし、2 Verificationsや3 Verificationsのように2回・3回まで許可する設定やUnlimitedのように制限なしの設定もできます。
本来はIDKitというSDKやAPIを使ってアプリケーションを構築していくことになるのですが、管理者ポータルからテストをするためのKioskモードが用意されているのでこちらから確認していきます。
Enable Kiosk〜Open Kioskでテスト用の画面が開きます。

先ほどのSign in with World IDと同じようにWorld AppでQRコードを読み込みます。今回はUniqueモードを設定したので最初の1回はちゃんとVerifiedとなります。
しかし、再度同じようにKioskを起動して同じWorld IDで確認をするとAlready verifiedとエラーが出ます。
簡単な投票や特典の配布などのアプリを作るのに良さそうですね。



ということで、Worldcoin / World IDを触ってみました。
金融包摂という社会課題への対応をしていくという方向性については共感できるものがありますし、うまく社会に浸透していくことができればベーシック・インカムはおいておいて金融システムからネグレクトされた人々にとっては救いになり得るのかもしれません。
ただ、先に書いた通りOrbの真正性などまだまだ情報の非対称性など粗いところも多く、まだまだポリシーやガバナンス面を含めやるべきことはたくさんあるんじゃないかと思います。Sam Altman氏もOpenAIで得られた知見や利益を、打ち上げ花火だけではなく、うまく実際の社会のために役立てられるといいですね。






ShibbolethとGriffinそして単一デーモンで複数のIdentity ProviderとFederation

$
0
0

こんにちは、富士榮です。

ここしばらくポスト数が減っていたので、今年は小ネタを中心に色々と書いていこうかと思います。


ということでみなさん大好きなShibbolethです。スペルがずっと覚えられずShobbolethと某所に書いて伝説のワード「しょぼれす」を産んでしまったのは遠い過去の記憶です。


Shibbolethってなんだ?というかなぜShibboleth

そういえばShibbolethコンソーシアムのWebサイトを見てもあんまりその辺は触れられていないんですよね。
A world leader in identity management technology, Shibboleth is an open-source project with a strong community of users. With a dedicated team of developers and vital support from Consortium members, Shibboleth has grown over the years to offer a variety of products alongside its world-renowned Identity Provider.(原文:What is Shibboleth?)

アイデンティティ管理技術の世界的リーダーであるShibbolethは、ユーザーの強力なコミュニティを持つオープンソースプロジェクトです。献身的な開発者チームとコンソーシアムメンバーからの重要なサポートにより、Shibbolethは世界的に有名なIdentity Providerに加え、様々な製品を提供するまでに成長しました。(Deepl翻訳) 


なお、Shibbolethという言葉自体は麦の穂を示す言葉で、アッカド語、ウガリト語、アラム語、古代シリア語、旧南アラブ語、アラビア語あたりが語源みたいです。

Cognate with Akkadian 𒋗𒁍𒌌𒌈 (šubultum, “ear of wheat”), Ugaritic 𐎌𐎁𐎍𐎚 (šblt, “ear of grain”), Aramaic שׁוּבַּלְתָּא/שִׁבַּלְתָּא‎ (šubbaltā/šibbaltā), Classical Syriac ܫܒܠܬܐ‎ (šeb(b)eltāʼ, “ear of grain; flow of a river”), Old South Arabian 𐩪𐩨𐩡𐩩‎ (s¹blt, “ear of grain”), and Arabic سُنْبُلَة‎ (sunbula), سَبَلَة‎ (sabala).

- Wikidisctionary「שיבולת」より

同じくWikipediaによるとこのShibbolethのsh(ʃ)の発音ができる・できないで民族の違いを見極めていたことから「ある社会集団の構成員と非構成員を見分けるための文化的指標を表す用語」になってきたとのことです。

そしてギレアデびとはエフライムに渡るヨルダンの渡し場を押えたので、エフライムの落人が「渡らせてください」と言うとき、ギレアデの人々は「あなたはエフライムびとですか」と問い、その人がもし「そうではありません」と言うならば、 またその人に「では『シボレテ』と言ってごらんなさい」と言い、その人がそれを正しく発音することができないで「セボレテ」と言うときは、その人を捕えて、ヨルダンの渡し場で殺した。その時エフライムびとの倒れたものは四万二千人であった。

多分「しょぼれす」って言ったら瞬殺されてますね・・・

この辺りからセキュリティドメイン(フェデレーション)の境界線を区分するためのゲートキーパーとしての認証システムにShibbolethという名称が用いられているのかな、という推測もあながち間違ってないのかもしれません。また、Shibbolethのロゴマークがグリフィンなのも「黄金を守る」という役目を持つ神話上の生物だから、というところに起源があるのかもしれません。(いずれも真偽は不明)

Shibbolethのロゴ

単一デーモンで複数のIdentity ProviderとFederation

さてさて、そんなShibbolethですがSAMLが喋れるIdentity Provider(IdP)としての役割を持つサーバーとApacheやnginxなどのWebサーバに組み込んでService Provider(SP)として構成するためのモジュールから構成されます。

ということで小ネタですが、単一のデーモン/shibdで動作しているSPがVirtual Directoryの単位で別のIdPとFederationさせたいケースが時たまありますので、どう設定するのが良いのかをちょっとだけ。(なお、セッション管理などSPとなるアプリ側でどう扱うかはアプリ側の設計次第なのでスコープアウトします)

やり方としては単純でRequestMapperに追加のApplicationIdを定義してMetadataProviderの設定をオーバライドしてあげるだけです。

こちらがshibboleth2.xmlの設定。additionalAppというapplicationIdを設定し、SP側のEntityID、HandlerURL、IdPのEntityIDとMetadataを別のものを設定してあげます。

<RequestMapper type="Native">

  <RequestMap applicationId="default">

    <Host name="sp.example.org" authType="shibboleth" requireSession="true" applicationId="additionalApp"/>

  </RequestMap>

</RequestMapper>


<ApplicationOverride id="additionalApp" entityID="https://sp.example.org/additionalApp/shibboleth-sp">

  <Sessions lifetime="28800" timeout="7200" checkAddress="false" handlerURL="/additionalApp/Shibboleth.sso">

    <SSO entityID="https://otheridp.example.jp/idp">

      SAML2

    </SSO>

  </Sessions>

  <MetadataProvider type="XML" validate="true" path="otheridp-metadata.xml"/>

</ApplicationOverride>


その上で、httpd.confのLocationにVirtual Directoryを設定し、ShibRequestSettingで追加したApplicationIdを参照するように指定してあげます。

<Location /additionalLocation>

  AuthType shibboleth

  ShibRequestSetting requireSession 1

  require shib-session

  ShibRequestSetting applicationId additionalApp

</Location>


これでshibdとhttpdを再起動すれば完了です。

従来のパスへのアクセス時はshibboleth2.xmlに元々指定してあったデフォルトのIdPへ、追加したパスへのアクセス時には追加で指定したIdPへリダイレクトされるようになります。

なお、REMOTE_USERはそれぞれのパス単位で別のもの(リダイレクト先のIdPのもの)となりますので、この辺りのハンドリングをアプリ側でコントロールしてあげる必要はあります。


ということで今年もよろしくお願いします。 







 

did:webの課題をカバーするdid:websがパブリックレビューへ

$
0
0

こんにちは、富士榮です。

みなさん大好きなdidメソッドの話です。

結局did:webが結構使われてるけどdid:webって既存のwebサイトの管理方法(DNSとかCA)に依存しちゃうので分散型じゃないよね、という課題(?)をカバーするためにdid:websっていうのをToIP(Trust Over IP) Foundationのdid:webs Method Task Forceで検討して仕様策定している、ということのようです。

ToIP Foundationからのアナウンス

https://trustoverip.org/news/2023/12/15/announcing-public-review-of-the-didwebs-method-specification/


個人的には結局インターネット上でDIDを使うユースケースに限っていえば既存のインターネットの仕組みであるDNSのレジストラやHTTPSのCA局のポリシーやガバナンスを信頼する必要があるはずなので、そこまで分散型に拘る必要性は感じない派なのですが・・・

did:webにおける課題

did:webに限らずdid全般において言えることですが、結局did、Decentralized Identifiersはつまるところ識別子なのでどうやってメソッド内の名前空間におけるユニークネスを担保するのかがポイントになります。ユニークネスを担保するためには多くの場合においてレジストリ(Trusted Data Registry)を構築し重複レコードがないことを管理することが必要となります。その際の登録〜管理プロセスを特定の管理主体(群)を通して行うモデルが従来のDNSやCA局などが取ってきたモデルです。一方で衆人環視の元、あらかじめ合意されたコンセンサスアルゴリズムに則ってレジストリへの登録〜管理を行うモデルがいわゆるブロックチェーンベースのレジストリ管理のモデルです。
一方でOpenID ConnectにおけるSIOPv1(v2と区別する意味であえてv1とつけます)における自己発行の証明書のThumbprintを識別子として用いる、つまり証明書のエントロピーに全振りし特定のレジストリを用いないモデルも存在します。
この辺りは上記のToIPからのアナウンスにも記載されていますが、整理するとdidメソッドにおける識別子の管理方法は、
  1. 権威ある管理主体を利用する方式
  2. ブロックチェーンを利用する方式
  3. 自己発行の証明書を利用する方式
に分類されるわけです。
前述の通りdid:webはdid:web:example.jp:users:taroの様にドメイン名を識別子に利用する上にDID Documentを取得するレジストリ(did.json)の正当性についてはHTTPSを用いるので1番めの権威ある管理主体を利用する方式に該当します。
しかしながら前述の通り、Decentralized Identifierというからには分散している方が良いのである、という主張も存在するわけで2番めのブロックチェーンを利用する方式なども依然として利用されている状態にあります。
この様なカオスがdidメソッドの乱立を招いてしまっているわけで、本ポストを書いている時点で186個もメソッドが登録されいる状態にありますし、もちろんW3Cのdidレジストリに登録せずにプライベートでdidを利用しているシステムも多数存在している状態となっています。

話を戻すと、分散型の必要性を主張するシナリオに対応するためにdid:webを拡張するのが今回のToIPのタスクフォースの対応だということになります。

did:websにおける対応

では、具体的にどの様な対応をしているのかを見ていきましょう。非常に単純です。
要するに従来のdid:webのDNS名前空間はそのまま利用し、最後に3番めの自己発行の識別子をくっつけることで対応しようという試みです。こんな感じの識別子ですね。
did:webs:example.jp:users(ここまではdid:web):xxxxxxxx(自己発行の識別子)

この最後の自己発行識別子の部分については既にToIP FoundationのACDC(Authenticated Chained Data Container) Task Forceで議論しているので、こちらを使おうという話の様です。いわゆるGLEIFがvLEIで採用しているKERI(Key Event Receipt Infrastructure)の話ですね。この辺りはIIW(Internet Identity Workshop)でも毎回活発に議論されています。

私もACDCやKERIについてはあまり知らないのでこれ以上は突っ込みませんが、did:web+ACDC=did:websって考えておけば良いのかな、、というぐらいの理解です。

まぁ、冷静に考えると結局のところDNSやHTTPSへの依存は一定以上残るわけなので、そこまで分散型に拘るなら素直にブロックチェーンベースのdidメソッドを使えば良いのでは?と思いましたが。。いずれにしてもユースケース次第ですね(という逃げ口上)。

ログインさせたいユーザを指定してIdPへFederationする(SAML/OpenID Connect)

$
0
0
こんにちは、富士榮です。

SAMLでもOpenID ConnectでもFederation構成をしているとService Provider(SP)やRelying Party(RP)側であらかじめIdentity Provider(IdP)側で認証させたいユーザ名を指定したいケースがしばしばあります。
典型的なケースの一つは多要素認証をオンデマンドで実行させた場合で、普通のページには第1要素であるユーザ名とパスワードで認証、決済ページには追加要素で認証を要求する、というシナリオは結構あり得るシナリオです。この場合、第1要素で認証されたユーザと追加要素で認証されたユーザをSP/RP側で比較することで同じ人であることを検証することももちろん可能なのですが、できればあらかじめ追加要素で認証させたいユーザの識別子をSP/RPからIdPに渡してあげることで利用者が再度ユーザIDをIdP側で入力する必要がなくなるのでUXも向上します。
この様なシナリオは当然のことながらSAMLでもOpenID Connectでもサポートされています。

SAMLの場合

SAMLを使う場合、SPからのSAML Requestの中にSubjectを指定することができます。
OASISのSAML2.0 coreの仕様の3.4.1のAuthnRequestのエレメントの章に記載があるSubjectが該当します。
SAML 2.0 core仕様

https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf

 こんな感じのリクエストになります。

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                    ForceAuthn="false"
                    ID="a133c62aafc8dcee7a69481de5af763c4ee370494"
                    IssueInstant="2024-01-03T03:28:40Z"
                    Destination="https://idp.example.jp/sso/login"
                    AssertionConsumerServiceURL="https://sp.example.com/acs"
                    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                    Version="2.0"
                    >
    <saml:Issuer>https://sp.example.com/sp</saml:Issuer>
    <saml:Subject>
        <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">test@example.jp</saml:NameID>
    </saml:Subject>
</samlp:AuthnRequest>
実際の動作は上記リクエストを受け取ったIdP側の実装に依存するので利用するIdPの仕様を確認してださい。ちなみにEntra ID(Azure AD)の場合はこの方法ではなくクエリパラメータにlogin_hint=test@example.jpという形で付加してリクエストを投げる形になります。(この辺りを参照:https://learn.microsoft.com/ja-jp/entra/identity-platform/single-sign-on-saml-protocol#subject)ただし、Entra IDが外部IdPとFederationしている時はlogin_hintではなくusername=test@example.jpという形でパラメータ名が異なるので要注意です。
また、Azure AD B2Cでは要求リゾルバという仕組みでSubjectの情報を取得することができるので、このサブジェクトに応じた処理を書けば割と自由に実装ができます。
Okta CIC(旧Auth0)はSAML RequestのSubjectをちゃんと判別してくれそうです。(この辺りを参照:https://community.auth0.com/t/pass-login-hint-to-saml-provider/92546

OpenID Connectの場合

こちらはシンプルにlogin_hint属性をクエリパラメータに付加することで実現できます。(仕様はこちらを参照;https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html)こんな感じでAuthentication Requestにパラメータをつけるだけです。
HTTP/1.1 302 Found
  Location: https://server.example.com/authorize?
    response_type=code
    &scope=openid%20profile%20email
    &client_id=s6BhdRkqt3
    &state=af0ifjsldkj
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &login_hint=test@example.jp
こちらもSAMLと同様にIdPの実装に依存するので実際の動作はIdPの仕様を確認してみてください。ちなみにAzure AD B2Cの場合はSAMLと同じく要求リゾルバで属性の取得ができます。

ちなみに他にもヒント系のパラメータは色々とあります。
また、SP/RPから認証コンテキストを指定したい場合もあるのですが、現在SAMLについてはAuthnContextClassRefというパラメータがありますが、OpenID Connectについてはacr/amrをclaimsパラメータで渡す方法はありますが、レベルの指定方法の共通化をどうするべきか?についてはPam Dingleさんを中心にIntenet Identity Workshopで議論が続けられているので今後の注目ポイントかもしれません。

IDトークン発行のタイミングで認証済みユーザの情報をどこまで保存するか

$
0
0

こんにちは、富士榮です。

OpenID Providerを自前で実装する際の悩みポイントの一つが認可コード、アクセストークンをサーバ側でどの様に保存するか、という問題です。

どういうことかというと、認可コードを発行するタイミングで通常はユーザの認証を行うわけですが、そのタイミングで認証済みユーザの属性情報を取得しておいて認可コード、アクセストークンと紐づけてテーブルに保存をしておく方が実装は簡単になる一方で、最新のユーザ情報をuserInfoから取得したい場合の対応が結局必要になったり、ユーザの削除の検知のメカニズムの実装が必要になる、という話です。

ちなみに、認可コードやアクセストークンをサーバ側で一定期間保持をし続けるための実装がOpenID Providerの可用性やスケーラビリティを左右する最大のポイントの一つだと思うので、この辺りは各サービス事業者の考え方で分かれるところです。(この辺りは別ポストで詳しくお話しようと思いますが、最後までステートレスにこだわったMicrosoftは認可コードをJWTにして認証済みユーザの属性情報を暗号化して入れることでトークン要求時に認可コードからセッションをLookupしてid_tokenを生成する必要をなくしていたり、VittorioがAuthorのRFC9068/JWT Profile for OAuth 2.0 Access Tokenの様にIntrospectionエンドポイントが無くてもトークンの有効性検証ができる仕組みを実装したり、Hybrid Flowでもないのにフロントで取得できるid_tokenにはほとんど情報を入れず、userInfoやGraph APIへのアクセスをしないとユーザ属性が全く取れなかったり、、、とグローバルスケールの認証基盤を運営する上での苦労が滲み出ていたりします)

横道にそれましたが、一番簡単な実装だと認可エンドポイントへアクセスし認証されたタイミングで以下のレコードを保存しておき、


トークンエンドポイントへアクセスしアクセストークンやIDトークンを発行する際は必要なカラムを追加するなどを含めこのテーブルだけで処理がクローズできるので実装はシンプル、という話です。こんな感じです。



しかし、この実装のメリットはこのレコードだけで全ての処理が完結するため処理がシンプルになること、そしてid_token発行時とuserInfoアクセス時の間に仮にユーザの属性の更新をされても認証時点のユーザの属性情報をクライアントへ返却できる、という点があります。一方でユーザの最新の属性をuserInfoエンドポイントから返却したいケースやユーザの削除されたことを検知するメカニズムの実装が結局必要になる、など考慮点も存在します。

そのため、実際にはユーザの識別子だけをレコード状には記録しておき、トークンエンドポイントやuserInfoエンドポイントへのアクセス時は対応するユーザの属性をデータベースから取得する、という実装になるはずです。


ということで実際どういう実装になっていそうなのか確認してみます。

Microsoft Entra ID

いわゆるAzure Active Directoryです。
そもそもIDトークンに識別子以外の属性情報が入らないので自然と上記の実装になっていると考えて良いと思います。


LINE Login

こちらはid_tokenにも認証されたユーザの属性情報が入ります。

userInfoにアクセスする前にLINEアプリで名前を「開発用」から変更してみました。

この状態でuserInfoにアクセスすると、、

はい、更新後の値が取得されます。


まぁ、普通に考えたらこういう実装になるだろうな、という話でした。

バックエンドが充実していない環境でOpenID Providerを作る

$
0
0

こんにちは、富士榮です。


昨日こんなことを書きましたので、ちょっと深掘りしてみようと思います。

この辺りは別ポストで詳しくお話しようと思いますが、最後までステートレスにこだわったMicrosoftは認可コードをJWTにして認証済みユーザの属性情報を暗号化して入れることでトークン要求時に認可コードからセッションをLookupしてid_tokenを生成する必要をなくしていたり、VittorioがAuthorのRFC9068/JWT Profile for OAuth 2.0 Access Tokenの様にIntrospectionエンドポイントが無くてもトークンの有効性検証ができる仕組みを実装したり、Hybrid Flowでもないのにフロントで取得できるid_tokenにはほとんど情報を入れず、userInfoやGraph APIへのアクセスをしないとユーザ属性が全く取れなかったり、、、とグローバルスケールの認証基盤を運営する上での苦労が滲み出ていたりします


要するにEntra IDの話なんですが、良いか悪いかは置いておいてOpenID Providerを作るときにバックエンドにDBをなるべく持たずに認可コード、アクセストークン、IDトークンのやりとりを一貫性を持って実行するための工夫の話です。


そもそも何が課題なのか?

ご存知の通りOpenID ConnectやOAuthはIDトークンやアクセストークンを取得するためにユーザエージェント(ブラウザ)とRelying Party(クライアント)が認可エンドポイントとトークンエンドポイントとの間のアクセスを行ったり来たりする必要があります。(いわゆるOAuth Dance)

これを実装しようとすると認可エンドポイントへのアクセスとトークンエンドポイントへのアクセスが本当に同一トランザクションの中でのやりとりなのかを確認しないといけませんし、サーバ(OpenID Provider)側としてはトランザクション内での情報の保持をしないといけなくなります。ただHTTPは本来ステートレスな仕組みなので、通常のWebアプリケーションを作る場合と同じ様にサーバ側でのセッション管理をどうするか、しかも認可エンドポイントはユーザエージェントから直接アクセス、トークンエンドポイントはクライアントからのバックエンドアクセスという形なのでcookieで、というわけにも行きません。

通常の実装では何の疑問もなくバックエンドにデータベースを置いてステート管理をサーバ側で実装することになるのですが、グローバルでスケールしている認証サービスの様に極めて高い可用性が必要とされるサービスでその様なインフラを作るのは非常にコストが高い行為といえます。(Entra IDの様に基本無償で提供される様なサービスなら難しい判断になると思います)


そうだ、クライアントにステート情報のハンドリングを任せよう!

そこで考えられたのが全てのやりとりの中で最終的にIDトークンやアクセストークンを発行するのに必要となる情報をもと回れば良いではないか、という考え方だと思われます。(Entra ID以外で見たことはありませんが)

簡単に流れを説明するとこんな感じになっているんだと思います。

認可エンドポイント

  1. ユーザを認証する→最終的にIDトークンに含めるユーザの属性情報を取得する
  2. nonceやaud(クライアントID)など、同じく最終的にIDトークンに含める属性をクエリパラメータから取得する
  3. 1,2で取得した情報を暗号化してJWTにして認可コードとしてクライアントへ戻す
トークンエンドポイント
  1. クライアントからPOSTされた認可コードの検証、復号を行う
  2. 復号した情報をもとにIDトークンを生成してクライアントへ返却する


この辺りをミニマムで実装してみるとこんな感じのコードになると思います。(テスト実装なので細かいところは気にしないでください)



const router = require("express").Router();
const jwt = require("jsonwebtoken");

// jwt署名に使う共有シークレット
const jwtSecretForCode = "this_is_a_secret_for_code_signing";
const jwtSecretForToken = "this_is_a_secret_for_token_signing";

// 認可エンドポイント
router.get("/authorization", async (req, res) => {
    // 本来なら実装する処理
    // - ユーザの認証
    // - response_typeによるフローの振り分け
    // - client_idの登録状態の確認
    // - redirect_uriとclient_idが示すクライアントとの対応確認
    // - scopeの確認
    // - 属性送出に関する同意画面の表示

    // codeの生成(本来は暗号化しておく)
    // 最終的にid_tokenに入れる値をDBに保存する代わりに暗号化してcodeに入れておくことでバックエンドを持たずにすませる
    const jwtPayload = {
        iss: "myOpenIDProvider",
        aud: req.query.client_id,
        sub: "authenticatedUserIdentifier",
        email: "test@example.jp",
        given_name: "taro",
        family_name: "test",
        nonce: req.query.nonce
    };
    const jwtOptions = {
        algorithm: "HS256", // 面倒なのでHS256にする
        expiresIn: "30s" // コードの有効期限なので短め
    };
    const code = jwt.sign(jwtPayload, jwtSecretForCode, jwtOptions);
    // redirect_uriへリダイレクト
    res.redirect(req.query.redirect_uri + "?code=" + code + "&state=" + req.query.state);
});

// トークンエンドポイント
router.post("/token", (req, res) => {
    // 本来なら実装する処理
    // - クライアントの認証
    // - grant_typeの検証
    // - codeの検証(有効期限、発行先クライアント、スコープ)
    // - access_tokenの発行
    // - id_tokenの発行
    const code = req.body.code;
    jwt.verify(code, jwtSecretForCode, (err, decoded)=> {
        if(err){
            res.json({
                err: "counld not verify code"
            })
        };
        if(decoded){
            // 本来は検証が終わったらcodeを無効化する
            // codeの中身を取り出してid_tokenを作る
            const jwtPayload = {
                iss: decoded.iss,
                aud: decoded.aud,
                sub: decoded.sub,
                email: decoded.email,
                given_name: decoded.given_name,
                family_name: decoded.family_name,
                nonce: decoded.nonce
            };
            const jwtOptions = {
                algorithm: "HS256", // 面倒なのでHS256
                expiresIn: "10m" // id_tokenの有効期限なので少しだけ長め
            };
            const token = jwt.sign(jwtPayload, jwtSecretForToken, jwtOptions);
            res.json({
                access_token: token, // ちなみにEntra IDの場合はaccess_tokenもid_tokenとほぼ同じものが使われるケースもある。
                token_type: "Bearer",
                expires_in: 3600,
                id_token: token
            });
        }
    });
});

module.exports = router;


実際に動きを見てみます。
  • 認可エンドポイントへのアクセス
http://localhost:3000/oauth2/authorization?client_id=111&redirect_uri=https://localhost:3000/cb&state=hoge

  • コールバックへのリダイレクト
https://localhost:3000/cb?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteU9wZW5JRFByb3ZpZGVyIiwiYXVkIjoiMTExIiwic3ViIjoiYXV0aGVudGljYXRlZFVzZXJJZGVudGlmaWVyIiwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuanAiLCJnaXZlbl9uYW1lIjoidGFybyIsImZhbWlseV9uYW1lIjoidGVzdCIsImlhdCI6MTcwNDQyMDk1MCwiZXhwIjoxNzA0NDIwOTgwfQ.XKi6JLc5IwDNyZG1C7Lrk1UrBiWpOV5EC2NgpqWSXjk&state=hoge

  • 認可コードの中身(面倒なので今回は暗号化はしていません)
  • トークンエンドポイントへのPOST 

  • 取得できたIDトークンの中身



本当にこれでいいのか?

まぁ確かにスケーラビリティを考えると非常にエコなので実装としてはありだと思いますが、色々と割り切りをしないといけません。

例えば、

  • 認可コードのサイズが大きくなる
  • アクセストークンはJWT形式(内包型トークン)が前提となり、Introspectionエンドポイントの実装はできない
などが代表的なところです。
特に認可コードのサイズが大きくなる問題は結構深刻で、認可エンドポイントからクライアントへのリダイレクト時のクエリパラメータの長さがものすごいことになりますので、クライアントの実装によっては認可コードが受け取れない、ということが結構あります。ここはMSALを使え!というMicrosoftの理屈なんだと思いますが、既存のアプリケーションとEntra IDを繋ぐ際にはしばしば問題になる可能性があるので、Entra IDの導入を行う際は要注意のポイントの一つです。

まぁ、いずれにしてもID基盤の導入はアプリケーション開発側との認識合わせ・仕様合わせが一番大きなポーションを占めると思うので、この辺りも念頭におきながらEntra IDライフを楽しむべきでしょう。

OpenID Providerを作ることでOpenID Connectを知る

$
0
0

こんにちは、富士榮です。

OpenID Connectってシンプルなプロトコルだと思いますが、やっぱりOpenID Providerの気持ちにならないと本当のところはわからないよね、ということで「OpenID Providerを作る」シリーズ(デ⚫︎ゴ⚫︎ティーニ風)でもやってみようかと思います。

超絶ベーシックなところまでは前回・前々回のポストを踏まえて2時間くらいで作ってみたのでまずはこちらを解説しつつ実装を一緒に育てていきたいな、と考えています。

とりあえず作ったところまではこちらのレポジトリで公開しています。(前回のポストからの追加部分としては認可コードをJWEにしたところくらいです。詳しくはReadmeを見てください)

https://github.com/fujie/oidc-study-op


ということで、中身は次回から解説しますが現状の実装で何ができるのかの確認を兼ねてOpenID Connectの通信が実際にどうなっているのかを、Azure AD B2Cを使って確認してみようと思います。結構Azure AD B2CとかAuth0を触るときにサービス側が外部IdPにどういう期待をしているのかを知るためにこれまでも簡易IdPを作って通信トレースをとったりしていたのですが、この辺りの試行錯誤がOpenID Connectそのものを知ることにつながりますし、Azure AD B2CやAuth0などのIDaaSについての知識も深められて一石二鳥です。


早速Azure AD B2Cに作ったOpenID ProviderをIDプロバイダとして読み込ませてみます。作ったOpenID Providerはローカルで動かしていますのでngrokを使ってインターネットからアクセスできる様にしています。

ちなみにAzure AD B2Cに限らずMicrosoftのID基盤はresponse_modeとしてform_postをデフォルトにしているのですが、そんなものは作っていないのでqueryに変更してあげる必要があります。

free版のngrokを使っているのでngrokを止めてしまうとURLが変更されてしまうので注意が必要です。(もし止めてしまったら一度IDプロバイダを消して再作成する必要があります)


なお、ngrokを使う利点としてインスペクションができることです。(http://127.0.0.1:4040をブラウザで開くと通信内容がトレースできます)



Azure AD B2CでIDプロバイダの設定をして保存を行うとDiscoveryとjwks_uriへのアクセスがあります。


この状態でユーザーフローを作成し、実行してみます。


順調にIDトークンが発行され、ユーザ登録画面に遷移しました。


インスペクションを見るとOpenID Connectのフローに則って順次アクセスされていることがわかります。

まぁ、徐々に育てていきましょう。


OpenID Providerを作る)まずは全体像から

$
0
0
こんにちは、富士榮です。

前回のポストに書いた通り、OpenID Providerを作っていきましょう。

なお、今回は一番オーソドックスなところから、ということで認可コードフロー(Authorization code flow)をベースに考えていきたいと思います。ちなみにOpenID Connect Core 1.0の仕様はOpenIDファウンデーションジャパンの翻訳・教育WGが日本語化していますので必要に応じて参照していきたいと思います。

まずは全体のイメージをおさらいしておきたいと思います。
下図が認可コードフローの全体シーケンスです。よく見るやつですね。なお、そもそも論となりますがOpenID Connectの究極の目標は「Relying PartyがIDトークンを取得すること」です。その過程においてユーザ認証を行なったり、APIアクセスするためのアクセストークンを取得することもありますが、まずは周辺の仕様を削ぎ落としてど真ん中だけを考えることが理解を進めるためには必要だと思います。


シーケンスは大きくは4つのフェーズで構成されますが、実際の認可コードフローのコアとなるのは2番目、3番目のフェーズです。
各フェーズでやっていることは以下の通りです。
  1. ディスカバリ
  • OpenID Providerが提供している機能やエンドポイントなどの情報をRelying Partyに提供する(ディスカバリエンドポイントを通してメタデータを提供する)
  • 認可※OpenID ConnectのベースとなるOAuth2.0を踏襲するため認可と呼称されるが仕様上は認証リクエスト・レスポンス(Authentication Request/Response)
    • 認可コードをRelying Partyに提供する(認可エンドポイント)
  • トークン発行
    • 認可コードを受け取りIDトークンをRelying Partyに提供する(トークンエンドポイント)
  • ユーザ情報提供
    • ユーザ情報をRelying Partyへ提供する(userInfoエンドポイント)
    • OAuth文脈において認可サーバから見ると保護対象リソースとなるためアクセストークンを使って認可される

    次回からそれぞれのフェーズについてgithubに公開したコードをベースに解説していきたいと思います。




    OpenID Providerを作る)まずはOpenID Providerの情報をRelying Partyに提供する

    $
    0
    0

    こんにちは、富士榮です。

    前回のポストではOpenID Connectの認可コードフローの全体像を書きましたので今回から中身について深掘りしていきたいと思います。


    今回は①のディスカバリの部分です。

    ディスカバリとは何をするフェーズなのか?ですが、簡単にいうとOpenID Provider自身に関する情報をRelying Partyに伝えることが目標となります。

    もちろんRelying Partyに対してOpenID Providerの構成情報を都度細かく伝えても良いのですが、Relying Partyの数が増えてくるとチリも積もるので設定作業が面倒になってきますし、何よりも何らかの構成変更がOpenID Provider側に発生した場合に変更情報を全Relying Partyに伝え直すのは至難の技です。

    この課題を解決するためにOpenID ConnectにはOpenID Connect Discoveryという仕様が定められています。

    Discoveryの流れ

    大まかな流れとしては、以下の2つのフェーズに分かれます。

    1. ユーザの識別子からOpenID ProviderのURLを取得する
    2. OpenID Providerの構成情報を取得する

    まず最初のフェーズですが、元々はDiscoveryの仕様はWebfingerの仕様に則ってOpenID ProviderのURLを取得することを想定して策定されていました。

    こんな流れです。

    1. Relying Partyにユーザが自身の識別子(例:test@example.jp)を提供する
    2. Relying Partyのユーザの識別子のドメインパート(example.jp)に対応するOpenID Providerを検索する(この部分がWebfinger)
    • 具体的には以下のGETリクエストを投げる
    • https://example.jp/.well-known/webfinger?
      • resource=test@example.jp&
      • rel=http://openid.net/specs/connect/1.0/issuer
  • 結果として返却されるOpenID ProviderのURLをRelying Partyは取得する
  • しかしながら、現実問題としてほとんどのRelying Partyではユーザ自身が利用するOpenID Providerを選択するUXとなっているため、このフェーズが利用されることはほとんどありません。(OpenID Providerが利用者の識別子がOpenID Providerのドメイン名と一致していないケースも多くなってきていることもあるんだと思います)

    そのため、本命は2番目のフェーズとなる、Relying PartyがあらかじめOpenID ProviderのURLまでは知っているところからスタートする構成情報の取得となっています。

    こちらは{OpenID ProviderのURL}/.well-known/openid-configurationというURLへのGETリクエストを投げ込むことでOpenID Providerの構成情報(エンドポイントやサポートする署名アルゴリズムなど)を取得するという仕組みとなっています。

    例えば、LINE Loginであれば「https://access.line.me/.well-known/openid-configuration」をGETすると以下のメタデータが返ってきます。

    {

        "issuer": "https://access.line.me",

        "authorization_endpoint": "https://access.line.me/oauth2/v2.1/authorize",

        "token_endpoint": "https://api.line.me/oauth2/v2.1/token",

        "revocation_endpoint": "https://api.line.me/oauth2/v2.1/revoke",

        "userinfo_endpoint": "https://api.line.me/oauth2/v2.1/userinfo",

        "scopes_supported": ["openid", "profile", "email"],

        "jwks_uri": "https://api.line.me/oauth2/v2.1/certs",

        "response_types_supported": ["code"],

        "subject_types_supported": ["pairwise"],

        "id_token_signing_alg_values_supported": ["ES256"],

        "code_challenge_methods_supported": ["S256"]

    }


    実態として.well-known/openid-configurationも提供していないOpenID Providerも存在しますし、返ってくる情報が当てにならないところもあるのでちゃんとドキュメントは読みましょう、という話にしかならないわけですが・・・(ちなみにLINE LoginもIDトークンの署名アルゴリズム/id_token_signing_alg_values_supportedがES256とありますが、実際の署名としてはRS256となっていたりします)

    OpenID Providerの構成情報にはどの様なものがあるのか

    仕様の「3.  OpenID Provider Metadata」にOpenID Providerが提供するメタデータに記載されている構成情報について解説されています。

    ざっくりこんな感じです。

    構成情報必須内容備考
    issuer必須OpenID Provider自体の識別子
    authorization_endpoint必須認可エンドポイントのURL
    token_endpointトークンエンドポイントのURLImplicit Flowの場合以外は必須
    userinfo_endpoint推奨userInfoエンドポイントのURL
    jwks_uri必須jwks_uriエンドポイントのURL
    registration_endpoint推奨動的クライアント登録エンドポイントのURL
    scopes_supported推奨サポートするscopeパラメータの値
    response_types_supported必須サポートするresponse_typeパラメータの値
    response_modes_supported任意サポートするresponse_modeパラメータの値
    grant_types_supported任意サポートするgrant_typeパラメータの値
    acr_values_supported任意サポートするacrパラメータの値
    subject_types_supported必須サポートする識別子種別の値
    id_token_signing_alg_values_supported必須サポートするid_tokenへの署名アルゴリズム
    id_token_encryption_alg_values_supported任意サポートするid_tokenの暗号化アルゴリズム(alg値)
    id_token_encryption_enc_values_supported任意サポートするid_tokenの暗号化アルゴリズム(enc値)
    userinfo_signing_alg_values_supported任意サポートするuserInfoの署名アルゴリズム
    userinfo_encryption_alg_values_supported任意サポートするuserInfoの暗号化アルゴリズム(alg値)
    userinfo_encryption_enc_values_supported任意サポートするuserInfoの暗号化アルゴリズム(enc値)
    request_object_signing_alg_values_supported任意サポートするリクエストオブジェクト(request_uriからの返却値)の署名アルゴリズム
    request_object_encryption_alg_values_supported任意サポートするリクエストオブジェクト(request_uriからの返却値)の暗号化アルゴリズム(alg値)
    request_object_encryption_enc_values_supported任意サポートするリクエストオブジェクト(request_uriからの返却値)の暗号化アルゴリズム(enc値)
    token_endpoint_auth_methods_supported任意サポートするトークンエンドポイントにおけるクライアント認証方式
    token_endpoint_auth_signing_alg_values_supported任意トークンエンドポイントでのクライアント認証にJWTを利用する場合にサポートする署名アルゴリズム
    display_values_supported任意サポートするdiplayパラメータの値
    claim_types_supported任意サポートするclaimタイプ(分散クレームなどのサポートのうむ)
    claims_supported推奨サポートするclaimの種類(sub,emailなど実際に提供するクレームの名前)
    service_documentation任意OpenID Providerの説明ページのURL
    claims_locales_supported任意サポートするclaimのロケール
    ui_locales_supported任意サポートするui_locale
    claims_parameter_supported任意OpenID Providerがclaimsパラメータを受け付けるかどうか
    request_parameter_supported任意OpenID Providerがrequestパラメータを受け付けるかどうか
    request_uri_parameter_supported任意OpenID Providerがrequest_uriパラメータを受け付けるかどうか
    require_request_uri_registration任意OpenID Providerが事前にrequest_uriパラメータの値を登録することを要求するかどうか
    op_policy_uri任意OpenID ProviderがRelying Partyに対して提供するユーザの情報がRelying Partyによってどの様に扱われることを求めているかを示すドキュメントのURL
    op_tos_uri任意OpenID Providerの利用規約のURL


    Discoveryエンドポイントを実装してみる

    では実装してみます。非常に単純なエンドポイントなので/.well-known/openid-configurationに対するGETリクエストがあったら必要な情報をJSONで返却するだけです。

    githubに公開しているソースのうち、この部分が該当します。

    https://github.com/fujie/oidc-study-op/blob/main/endpoints/discovery.js


    
    const router = require("express").Router();
    
    // Discoveryエンドポイント
    router.get("/openid-configuration", (req, res) => {
        const baseUrl = 'https://' + req.headers.host;
        const response_types = ["code"];
        const subject_types = ["public"];
        const alg_values = ["RS256"];
        const scopes = ["openid"];
        const auth_methods = ["client_secret_post", "client_secret_basic"];
        const claims =["sub", "name", "given_name", "family_name", "email", "email_verified", "iss", "aud", "exp", "exp", "iat"];
        res.json({
            issuer: baseUrl,
            authorization_endpoint: baseUrl + "/oauth2/authorize",
            token_endpoint: baseUrl + "/oauth2/token",
            userinfo_endpoint: baseUrl + "/userinfo",
            jwks_uri: baseUrl + "/jwks_uri",
            response_types_supported: response_types,
            subject_types_supported: subject_types,
            id_token_signing_alg_values_supported: alg_values,
            scopes_supported: scopes,
            token_endpoint_auth_methods_supported: auth_methods,
            claims_supported: claims
        });
    });
    module.exports = router;
    


    node.js.+ express routerで実装しているので/.well-knownへのアクセスはこのJSへルーティングされる様にしています。

    やっていることは単純で必要な情報をres.jsonで返却しているだけですね。


    Relying PartyはOpenID Providerへリクエストをする際にこのエンドポイントにアクセスして最新の構成情報を取得して各種エンドポイントを利用する、という動きをする、ということですね。(パフォーマンスの観点でRelying Party側でメタデータをキャッシュをすることも多いと思います)

    Verifiable Credentialsの署名を壊さずに選択的開示を行うSD-JWTを体感してみる

    $
    0
    0

    こんにちは、富士榮です。


    少し毛色を変えて今日はみんな大好き選択的開示(Selective Disclosure)です。

    特にVerifiable Credentialsを使うユースケースの典型的なシナリオに「バーに入る際の年齢証明に免許証を見せる」というものがあります。しかしながら年齢が21歳以上であることを証明したいだけなのに運転免許証を見せてしまうと住所や氏名などの本来不要な情報まで提示することになってしまいます。そこでSelective Disclosureが大事、という話になってきます。

    一見簡単そうに見えますが、署名が施されたデータの中身を改竄する(実際は不要なデータを間引く)ことになるのでデジタル署名の目標である真正性の担保という話と相反することになってしまいます。

    これまでもBBS+など仕組みは出てきていますが今日はJSON Web Tokenを対象としたSD-JWTの話をしたいと思います。


    簡単な仕組み

    従来の署名月JWTは対象となるデータ(JSON)をBase64Urlエンコードして署名アルゴリズムなどの情報をヘッダに入れ、署名を施して".(ピリオド)"で連結したものでした。OpenID ConnectにおけるIDトークンもこのフォーマットですね。

    例えば、こんなデータを対象としたいと思います。

    {

      "iss": "http://server.example.com",

      "sub": "248289761001",

      "aud": "s6BhdRkqt3",

      "nonce": "n-0S6_WzA2Mj",

      "exp": 1311281970,

      "iat": 1311280970,

      "name": "Jane Doe",

      "given_name": "Jane",

      "family_name": "Doe",

      "gender": "female",

      "birthdate": "0000-10-31",

      "email": "janedoe@example.com",

      "picture": "http://example.com/janedoe/me.jpg"

    }

    これをRS256で署名してJWTとする場合、ヘッダにはアルゴリズムの情報などを入れます。

    {

      "kid": "1e9gdk7",

      "alg": "RS256"

    }

    最後にヘッダ、ペイロードをそれぞれBase64Urlエンコードしてデジタル署名の値を最後に追加します。(ヘッダ・ペイロード・署名の間を"."で連結します)

    すると結果、こんな感じになります。

    eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAibmFtZSI6ICJKYW5lIERvZSIsCiAiZ2l2ZW5fbmFtZSI6ICJKYW5lIiwKICJmYW1pbHlfbmFtZSI6ICJEb2UiLAogImdlbmRlciI6ICJmZW1hbGUiLAogImJpcnRoZGF0ZSI6ICIwMDAwLTEwLTMxIiwKICJlbWFpbCI6ICJqYW5lZG9lQGV4YW1wbGUuY29tIiwKICJwaWN0dXJlIjogImh0dHA6Ly9leGFtcGxlLmNvbS9qYW5lZG9lL21lLmpwZyIKfQ.rHQjEmBqn9Jre0OLykYNnspA10Qql2rvx4FsD00jwlB0Sym4NzpgvPKsDjn_wMkHxcp6CilPcoKrWHcipR2iAjzLvDNAReF97zoJqq880ZD1bwY82JDauCXELVR9O6_B0w3K-E7yM2macAAgNCUwtik6SjoSUZRcf-O5lygIyLENx882p6MtmwaL1hd6qn5RZOQ0TLrOYu0532g9Exxcm-ChymrB4xLykpDj3lUivJt63eEGGN6DH5K6o33TcxkIjNrCD4XB1CKKumZvCedgHHF3IAK4dVEDSUoGlH9z4pP_eWYNXvqQOjGs-rDaQzUHl6cQQWNiDpWOl_lxXjQEvQ

    jwt.ioなどのサイトでデコードすると中身がわかります。


    一方でSD-JWTではデジタル署名を維持しつつ、このペイロードの中に記載されているクレーム(属性)の一部を隠したいわけです。

    そのため、実はSD-JWTで選択的開示をする際はペイロード自体には何の加工もしません(やっぱりペイロードを改竄しちゃうと署名はこわれるので)。その代わりにSD-JWTを作成する際に以下の工夫を施します。

    • 選択的開示の対象となる属性のハッシュをペイロードに入れる
    • 従来のJWTの最後(署名の後)にDisclosureと言われる各属性に対応する値を追加する(~(チルダ)で連結する)

    このDisclosureがあればペイロード内のハッシュ化された値を元に戻せるので開示したくない属性についてはDisclosureを削除してしまうことで受け取り側は値を知ることができなくなる、という仕組みです。

    先ほどのJWTのペイロードをSD-JWTにする場合はこんな感じになります。要するに隠す可能性がある属性について"_sd"という要素にハッシュ化した値を入れる感じです。

    {

      "iss": "http://server.example.com",

      "aud": "s6BhdRkqt3",

      "nonce": "n-0S6_WzA2Mj",

      "iat": 1516239022,

      "exp": 1735689661,

      "_sd": [

        "5G_krZtMTMPltAWWNMhdDxJMt15SA1KFkd2gIlSvLtQ",

        "5p6bt53D9p4MfkVYQciL4pWXAkIjz0PbLXGD0NHii2w",

        "6Jt5lxreiTK8olTRzyHoHYYPmu0G-ZVvWpacVmYYT8M",

        "Ew-dKRmA2uSgw9EmGwNIfQksPpPIs0qA4OE-86DQ3_Y",

        "Hz-BYPQJmHsFlIfZ_9CJ6IboqisoZNtwzkb0Z3JAlF4",

        "J3P_gNA3LECIzTttBuX62cLVICDFzR-rroiCQkbBbuQ",

        "O2bDUHaELZIsyOXTyutWunAgm37QTkbCTRw73bi55xE",

        "Q4vtraDvprqBkjrPoLW_9FZDetFKKAGcya05rIwq4xg"

      ],

      "_sd_alg": "sha-256"

    }

    そしてこのペイロードを通常通りデジタル署名付きのJWTにしたものがこちらの文字列です。

    eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MzU2ODk2NjEsIl9zZCI6WyI1R19rclp0TVRNUGx0QVdXTk1oZER4Sk10MTVTQTFLRmtkMmdJbFN2THRRIiwiNXA2YnQ1M0Q5cDRNZmtWWVFjaUw0cFdYQWtJanowUGJMWEdEME5IaWkydyIsIjZKdDVseHJlaVRLOG9sVFJ6eUhvSFlZUG11MEctWlZ2V3BhY1ZtWVlUOE0iLCJFdy1kS1JtQTJ1U2d3OUVtR3dOSWZRa3NQcFBJczBxQTRPRS04NkRRM19ZIiwiSHotQllQUUptSHNGbElmWl85Q0o2SWJvcWlzb1pOdHd6a2IwWjNKQWxGNCIsIkozUF9nTkEzTEVDSXpUdHRCdVg2MmNMVklDREZ6Ui1ycm9pQ1FrYkJidVEiLCJPMmJEVUhhRUxaSXN5T1hUeXV0V3VuQWdtMzdRVGtiQ1RSdzczYmk1NXhFIiwiUTR2dHJhRHZwcnFCa2pyUG9MV185RlpEZXRGS0tBR2N5YTA1ckl3cTR4ZyJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.LAlXQnPK8rOhxBhbg_FU-hiBJJqCX5CYWYTp0YcbbIxnBZPN1ZhRgE_SLh0rEg1L559GxmG1WrpGMyOl1rDoAA

    このJWTに"_sd"に入れた各属性に対応するDisclosureを~で連結して追加していきます。赤字の部分が追加したものです。

    eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MzU2ODk2NjEsIl9zZCI6WyI1R19rclp0TVRNUGx0QVdXTk1oZER4Sk10MTVTQTFLRmtkMmdJbFN2THRRIiwiNXA2YnQ1M0Q5cDRNZmtWWVFjaUw0cFdYQWtJanowUGJMWEdEME5IaWkydyIsIjZKdDVseHJlaVRLOG9sVFJ6eUhvSFlZUG11MEctWlZ2V3BhY1ZtWVlUOE0iLCJFdy1kS1JtQTJ1U2d3OUVtR3dOSWZRa3NQcFBJczBxQTRPRS04NkRRM19ZIiwiSHotQllQUUptSHNGbElmWl85Q0o2SWJvcWlzb1pOdHd6a2IwWjNKQWxGNCIsIkozUF9nTkEzTEVDSXpUdHRCdVg2MmNMVklDREZ6Ui1ycm9pQ1FrYkJidVEiLCJPMmJEVUhhRUxaSXN5T1hUeXV0V3VuQWdtMzdRVGtiQ1RSdzczYmk1NXhFIiwiUTR2dHJhRHZwcnFCa2pyUG9MV185RlpEZXRGS0tBR2N5YTA1ckl3cTR4ZyJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.LAlXQnPK8rOhxBhbg_FU-hiBJJqCX5CYWYTp0YcbbIxnBZPN1ZhRgE_SLh0rEg1L559GxmG1WrpGMyOl1rDoAA~WyJ5OWVMRmI3azdXZnd4ZkllQWp5eUt3Iiwic3ViIiwiMjQ4Mjg5NzYxMDAxIl0~WyJUNkVlZzRsaVJyN3VtOUZxOFRYekR3IiwibmFtZSIsIkphbmUgRG9lIl0~WyJUUnRocWdUc29kZzVJbnBBR2ZvNjJnIiwiZ2l2ZW5fbmFtZSIsIkpvbmUiXQ~WyJmRWx5VzFIdlVjX1dYTnFLeUdRcEJ3IiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyI1blgwRmZTNHUtVjN3aFNORUpPQndnIiwiZ2VuZGVyIiwiZmVtYWxlIl0~WyJhSzhJYWc2dzB1Mno1WVpxMWlXOC13IiwiYmlydGhkYXRlIiwiMDAwMC0xMC0zMSJd~WyIwR0pLdnN2aDNpbnFkeGlUM3MxbVJRIiwiZW1haWwiLCJqb25lZG9lQGV4YW1wbGUuY29tIl0~WyJXMHpCa1lfLThqUVhNY3JnYTJTak9BIiwicGljdHVyZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9qYW5lZG9lL21lLmpwZyJd

    これでSD-JWTが出来上がりました。

    これを渡されたVerifierなどはDisclosureの値を使ってペイロード内の"_sd"の値を復号して実際の属性値を取得する、ということをやります。また、開示したくない値があれば対応するDisclosureの値を消した状態でSD-JWTを渡せば良い、という理屈です。


    体感してみる

    といっても細かい実装の話をするのは疲れるので世の中に出てきているツールを触って体感してみたいと思います。

    今回はこのツールを使ってみます。

    https://github.com/christianpaquin/sd-jwt

    詳細はREADMEに書いてある通りなのですが、取り合えずやってみましょう。

    ざっくりこんな手順です。

    1. SD-JWTの署名に使う鍵を生成する
    2. jwt(Disclose対象じゃない部分)のjsonを用意する
    3. sd(Disclose対象としたい部分)のjsonを用意する


    まずは鍵を生成します。

    npm run generate-issuer-keys -- -k <jwksPath> -p <privatePath> -a <keyAlg>

    - jwksPathは公開鍵のファイル名です(なければ作ってくれます)

    - privatePathは秘密鍵のファイル名です(同上)

    - keyAlgはアルゴリズムです。指定しないとES256になります


    鍵の準備ができたらSD-JWTを作ってみます。まずはDisclose対象じゃない部分のJSONを用意します。今回はこんなファイルを用意しました。(先ほどの例の個人属性以外の部分ですね)

      {

        "iss": "http://server.example.com",

        "aud": "s6BhdRkqt3",

        "nonce": "n-0S6_WzA2Mj",

        "iat": 1516239022,

        "exp": 1735689661

      }

    そして、Disclose対象の部分のJSONです。(先ほどの例の個人属性部分です)

    {

        "sub": "248289761001",

        "name": "Jane Doe",

        "given_name": "Jone",

        "family_name": "Doe",

        "gender": "female",

        "birthdate": "0000-10-31",

        "email": "jonedoe@example.com",

        "picture": "http://example.com/janedoe/me.jpg"

      }

    ではSD-JWTを生成してみます。

    npm run create-sd-jwt -- -k {作成した秘密鍵} -t {作成したDisclose対象外のJSON} -h sha-256 -c {作成したDisclose対象のJSON} -o {出力したいSD-JWT}

    こんな感じの書式です。では実際に生成してみます。

    npm run create-sd-jwt -- -k private.jwk -t jwt.json -h sha-256 -c sdClaimsFlat.json -o sd-jwt.json

    こちらが実行時の出力です。

    % npm run create-sd-jwt -- -k private.jwk -t jwt.json -h sha-256 -c sdClaimsFlat.json -o sd-jwt.json

    > sd-jwt@1.0.0 create-sd-jwt
    > ts-node --files src/create-sd-jwt-cl.ts -k private.jwk -t jwt.json -h sha-256 -c sdClaimsFlat.json -o sd-jwt.json

    Creating SD-JWT from the JWT jwt.json using the private key private.jwk, encoding selectively-disclosable claims from sdClaimsFlat.json

    JWT: {"iss":"http://server.example.com","aud":"s6BhdRkqt3","nonce":"n-0S6_WzA2Mj","iat":1516239022,"exp":1735689661,"_sd":["5G_krZtMTMPltAWWNMhdDxJMt15SA1KFkd2gIlSvLtQ","5p6bt53D9p4MfkVYQciL4pWXAkIjz0PbLXGD0NHii2w","6Jt5lxreiTK8olTRzyHoHYYPmu0G-ZVvWpacVmYYT8M","Ew-dKRmA2uSgw9EmGwNIfQksPpPIs0qA4OE-86DQ3_Y","Hz-BYPQJmHsFlIfZ_9CJ6IboqisoZNtwzkb0Z3JAlF4","J3P_gNA3LECIzTttBuX62cLVICDFzR-rroiCQkbBbuQ","O2bDUHaELZIsyOXTyutWunAgm37QTkbCTRw73bi55xE","Q4vtraDvprqBkjrPoLW_9FZDetFKKAGcya05rIwq4xg"],"_sd_alg":"sha-256"}

    JWS payload: 7B22697373223A22687474703A2F2F7365727665722E6578616D706C652E636F6D222C22617564223A227336426864526B717433222C226E6F6E6365223A226E2D3053365F577A41324D6A222C22696174223A313531363233393032322C22657870223A313733353638393636312C225F7364223A5B2235475F6B725A744D544D506C744157574E4D686444784A4D7431355341314B466B643267496C53764C7451222C2235703662743533443970344D666B56595163694C34705758416B496A7A3050624C584744304E4869693277222C22364A74356C78726569544B386F6C54527A79486F485959506D7530472D5A567657706163566D595954384D222C2245772D644B526D41327553677739456D47774E4966516B735070504973307141344F452D38364451335F59222C22487A2D425950514A6D4873466C49665A5F39434A3649626F7169736F5A4E74777A6B62305A334A416C4634222C224A33505F674E41334C4543497A5474744275583632634C56494344467A522D72726F6943516B6242627551222C224F326244554861454C5A4973794F585479757457756E41676D333751546B62435452773733626935357845222C225134767472614476707271426B6A72506F4C575F39465A446574464B4B4147637961303572497771347867225D2C225F73645F616C67223A227368612D323536227D

    JWS: eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MzU2ODk2NjEsIl9zZCI6WyI1R19rclp0TVRNUGx0QVdXTk1oZER4Sk10MTVTQTFLRmtkMmdJbFN2THRRIiwiNXA2YnQ1M0Q5cDRNZmtWWVFjaUw0cFdYQWtJanowUGJMWEdEME5IaWkydyIsIjZKdDVseHJlaVRLOG9sVFJ6eUhvSFlZUG11MEctWlZ2V3BhY1ZtWVlUOE0iLCJFdy1kS1JtQTJ1U2d3OUVtR3dOSWZRa3NQcFBJczBxQTRPRS04NkRRM19ZIiwiSHotQllQUUptSHNGbElmWl85Q0o2SWJvcWlzb1pOdHd6a2IwWjNKQWxGNCIsIkozUF9nTkEzTEVDSXpUdHRCdVg2MmNMVklDREZ6Ui1ycm9pQ1FrYkJidVEiLCJPMmJEVUhhRUxaSXN5T1hUeXV0V3VuQWdtMzdRVGtiQ1RSdzczYmk1NXhFIiwiUTR2dHJhRHZwcnFCa2pyUG9MV185RlpEZXRGS0tBR2N5YTA1ckl3cTR4ZyJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.LAlXQnPK8rOhxBhbg_FU-hiBJJqCX5CYWYTp0YcbbIxnBZPN1ZhRgE_SLh0rEg1L559GxmG1WrpGMyOl1rDoAA~WyJ5OWVMRmI3azdXZnd4ZkllQWp5eUt3Iiwic3ViIiwiMjQ4Mjg5NzYxMDAxIl0~WyJUNkVlZzRsaVJyN3VtOUZxOFRYekR3IiwibmFtZSIsIkphbmUgRG9lIl0~WyJUUnRocWdUc29kZzVJbnBBR2ZvNjJnIiwiZ2l2ZW5fbmFtZSIsIkpvbmUiXQ~WyJmRWx5VzFIdlVjX1dYTnFLeUdRcEJ3IiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyI1blgwRmZTNHUtVjN3aFNORUpPQndnIiwiZ2VuZGVyIiwiZmVtYWxlIl0~WyJhSzhJYWc2dzB1Mno1WVpxMWlXOC13IiwiYmlydGhkYXRlIiwiMDAwMC0xMC0zMSJd~WyIwR0pLdnN2aDNpbnFkeGlUM3MxbVJRIiwiZW1haWwiLCJqb25lZG9lQGV4YW1wbGUuY29tIl0~WyJXMHpCa1lfLThqUVhNY3JnYTJTak9BIiwicGljdHVyZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9qYW5lZG9lL21lLmpwZyJd

    SD-JWT written to sd-jwt.json

    出来上がったJWSがsd-jwt.jsonに書き込まれています。

    jwt.ioでデコードしてみるとちゃんとSD-JWTになっていることがわかります。

    (ちなみにjwt.ioではDisclosure部分は見えません)



    次は一部の属性を取り除いた状態で検証できるかどうかの確認です。

    出力されたsd-jwt.jsonのDisclosureを一部取り除いてみましょう。なお、Disclosureは上から順番に対象の属性と対応しているので何番目の属性を隠すのかでどのDisclosureを削除すれば良いのかが決まります。

    例えば今回7番目の属性がemailなので7番目のDisclosureを削除してみます。ちなみに私はMacのTerminalのviでファイルの編集をしたのですが、保存をするとLine terminator(0a)が行末に追加されてしまうので、awkで削る必要がありました。

    awk '{printf("%s", $0)}' sd-jwt.json > user-sd-jwt.json

    こんな感じです。

    % file user-sd-jwt.json 

    user-sd-jwt.json: ASCII text, with very long lines (1260), with no line terminators

    fileコマンドで確認するとwith no line terminatorsとなっているのでこれで準備完了です。


    早速ですが検証をするためのコマンドを叩きます。

    npm run verify-sd-jwt -- -t {インプットとなるSD-JWTファイル} -k {公開鍵} -o {出力されるJWT} -d {開示された属性}

    という書式です。

    実際に実行するとこんな感じになり、email属性が消えていることがわかります。

    % npm run verify-sd-jwt -- -t user-sd-jwt.json -k publicKey.jwks -o outJwt.json -d disclosedClaims.json

    > sd-jwt@1.0.0 verify-sd-jwt
    > ts-node --files src/verify-sd-jwt-cl.ts -t user-sd-jwt.json -k publicKey.jwks -o outJwt.json -d disclosedClaims.json

    Verifying SD-JWT from user-sd-jwt.json
    payload: {"iss":"http://server.example.com","aud":"s6BhdRkqt3","nonce":"n-0S6_WzA2Mj","iat":1516239022,"exp":1735689661,"_sd":["10nhdtWZB3RXMn2AxXsK3eJJFf2T0UosLfFVYDg6nN8","2OFg4dmWIuCvX1O7XvvNEbiR30setroa4Y0_JXLUyBY","2nB9Dno76pMtIo3EUWo-4ckypEhW2MM4fXXLLi5use4","9x8lAtlY7dhnojZ2Qv8HRH5JO7oW3PnPK8Z6xe79Y-w","AclFaDd78h1fnbxZ4mtqRMdmQxrDxH9QhEO1QgT0FME","D3B9GFLw9Zn8-7JLWo9WXlrR04OtAPCFiUPPL9GYDNg","ZA-Bh-ucVIOm9RjmV6El7ym7CQ_gwe5ACDzeV7KSGx0","bnqv7hEmIi_3WWLFhwPSAEsoMU0T2rTFZdcmYAnBKxA"],"_sd_alg":"sha-256"}

    disclosedClaims: {"sub":"248289761001","name":"Jane Doe","given_name":"Jone","family_name":"Doe","gender":"female","birthdate":"0000-10-31","picture":"http://example.com/janedoe/me.jpg"}

    JWT written to outJwt.json
    JWT written to disclosedClaims.json


    ということでみんな大好きなVerifiable CredentialsとSelective Disclosureでした。


    OpenID Providerを作る)認可エンドポイントを作る

    $
    0
    0
    こんにちは、富士榮です。

    引き続き、OpenID Providerを作ろうと思います。
    これまでのポストはこちらです。
    再掲となりますが、認可コードフローの全体像はこちらです。


    今回のポストでは②の認可エンドポイント(Authorize)の部分について実装してみましょう。
    なお、今回のシリーズでは最低限の実装を通してOpenID Connectを知ることを目的としていますので、まずは大まかな実装をしておいて細かいところは後から充実させていこうと思います。そのため、先日バックエンドが充実していない環境におけるOpenID Providerの実装について紹介した際の実装をベースにしたいと思います。

    なお、本日紹介する部分のコードはこちらで公開しています。

    ということで初めていきましょう。

    認可エンドポイントの役割と処理内容

    認可コードフローに限定して話すと認可エンドポイントの目標は「認可コード」を発行することです。クライアント(Relying Party)はこの「認可コード」を後日紹介する「トークンエンドポイント」でIDトークンやアクセストークンと交換しますので、認可エンドポイントが正しく認可コードを発行することはOpenID Connectのフロー全体の安全性を担保する上で非常に重要な第一歩となります。
    なお、認可コードフローにおける認可エンドポイントの仕様の詳細はこちらに記載があります。

    サポートすべきHTTPメソッドはGET/POST、各種パラメータは以下の通りです。
    パラメータ区分意味
    scope必須OAuthにおける認可スコープを示すがOpenID Connectの場合はopenidを必ず含める必要がある
    response_type必須OAuthにおけるresponse_typeを示す。認可コードフローの場合はcodeを指定する
    client_id必須OAuthにおけるクライアントの識別子を示す。OpenID Connectの場合はRelying Partyの識別子となる
    redirect_uri必須レスポンスが返されるURIを示す。あらかじめクライアントIDに対して登録されたものと完全一致する必要がある
    state推奨リクエストとコールバックの一貫性を確認するために利用する
    nonce推奨クライアントのセッションと発行されるIDトークンの紐付けを行うために利用する
    display推奨認証および同意のための画面表示の方法を指定する
    prompt推奨再認証や同意を求めるかどうかを指定する
    max_age任意ユーザが認証されてから再認証を求めるまでの最大許容時間
    ui_locales任意UIの言語
    id_token_hint任意OpenID Providerが以前発行したIDトークン。過去のセッションに関連した認証を行う場合などに利用する
    login_hint任意ログインさせるユーザの識別子を指定する場合に利用する
    acr_values任意認証コンテキスト(Authentication Context Class / acr)を指定する場合に利用する

    結構色々とパラメータがありますが、大まかな処理の流れはこんな感じになります。(response_typeがcodeの場合)
    1. クライアントの確認を行う(指定されたclient_id、あらかじめ登録されたredirect_uriが合致しているか確認する)
    2. ユーザ認証を行う(通常の実装ではこのエンドポイントを保護しておく)
    3. 指定されたscopeに応じて認証されたユーザの属性情報をユーザDBから取得する
    4. 同意画面の表示
    5. 認可コード(code)とクライアントから指定されたstateをクエリパラメータにつけてredirect_uriにリダイレクトする(UserAgentへHTTP301を返す)

    この時、後からトークンエンドポイントで認可コードに紐づくIDトークン(ユーザの情報を含む)を発行することを考えるとバックエンドのDBにクライアントからの要求情報(client_id、redirect_uri、scope)、認証されたユーザの情報と認可コードを保存しておく必要があります。また、同時に認可コードの悪用や再利用を避けるためには認可コード自体の有効期限を極力短くしておく必要があるのと、トークンエンドポイント側で認可コードを利用したら無効化する処理などを実装する必要がありますので、それらの情報(フラグや有効期限など)もDBに保存しておく必要があるはずです。

    ただ、処理の流れを知る意味では単純に認可コードを発行さえすればOKですのでクライアント関連の情報やユーザの認証などは飛ばして関連する情報を認可コードの中に入れてしまいましょう。以前のポストでは単純にJWTにしていましたが、Entra IDと同じ様にJWE(JSON Web Encryption)で暗号化した状態にしてあります。

    認可エンドポイントの実装

    以下の様な実装を行いたいとお思います。
    // 認可エンドポイント
    router.get("/authorize", async (req, res) => {
    // 本来なら実装する処理
    // - ユーザの認証
    // - response_typeによるフローの振り分け
    // - client_idの登録状態の確認
    // - redirect_uriとclient_idが示すクライアントとの対応確認
    // - scopeの確認
    // - 属性送出に関する同意画面の表示

    // codeの生成(本来は暗号化しておく)
    // 最終的にid_tokenに入れる値をDBに保存する代わりに暗号化してcodeに入れておくことでバックエンドを持たずにすませる
    constbaseUrl='https://'+req.headers.host;

    constdate=newDate();
    constjwePayload= {
    iss:baseUrl,
    aud:req.query.client_id,
    sub:"test",
    email:"test@example.jp",
    name:"taro test",
    given_name:"taro",
    family_name:"test",
    nonce:req.query.nonce,
    exp:Math.floor((date.getTime() + (1000*30)) /1000) // 有効期限は30秒
    };
    constcode=awaitutils.generateJWE(jwePayload);
    console.log(code);
    // redirect_uriへリダイレクト
    res.redirect(req.query.redirect_uri+"?code="+code+"&state="+req.query.state);
    });

    この実装を順番に解説していきたいと思います。

    まず、エンドポイントとメソッドの実装です。
    router.get("/authorize", async (req, res) => {

    次に、本来はバックエンドDBに保存する情報を認可コード自体に含めるため、JSONペイロードを作ります。
    constbaseUrl='https://'+req.headers.host;

    constdate=newDate();
    constjwePayload= {
    iss:baseUrl,
    aud:req.query.client_id,
    sub:"test",
    email:"test@example.jp",
    name:"taro test",
    given_name:"taro",
    family_name:"test",
    nonce:req.query.nonce,
    exp:Math.floor((date.getTime() + (1000*30)) /1000) // 有効期限は30秒
    };

    後からIDトークンに入れる情報として、
    • iss:Issuerの識別子
    • aud:クライアントID
    • sub、email、name、given_name、family_name:本来はユーザ認証した結果取得する属性情報
    • nonce:リクエストに指定されたnonceの値
    • exp:認可コードの有効期限(短めが良いのでとりあえず30秒)
    をペイロードとして定義しています。

    このペイロードを暗号化します。JWEを作成するコードは別のJSファイルに定義してあります。
    constcode=awaitutils.generateJWE(jwePayload);

    こちらがJWEを生成するコードです。node-joseというライブラリを利用しています。また、今回は暗号化と復号を同じサーバで実行できれば良いので対象鍵を利用しています。
    // JWEの作成
    exports.generateJWE=asyncfunction(payload) {
    constkey=awaitjose.JWK.asKey({kty:'oct', k:jose.util.base64url.encode(encryptKeyString)});
    returnjose.JWE.createEncrypt({format:'compact'},key).update(JSON.stringify(payload)).final();
    }

    実際に作られた認可コードはこの様なものです。
    eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTI1NktXIiwia2lkIjoiOXBheXlGVzBMdEZ0czkyRDE5VVJCSS15bHc5TGh2ZWI1dFJKYkkxU19vdyJ9.XAMPBCzLxSipsZILlvL9r6qdhL0teaYZkpO17RYgVqirv4btHMsdPQ.2kJJ1PExtsZLhI-nPSpLNQ.Kyxyt6TC2CdV81xbq8_roFaZfERZKSg9vnlInTZHnxS8eb9cVAOW_RESgKFWkt6g9vW6CXgO6qE5w-3Oe2J8jV92gVThUwlz5PcQCKgxZ6xkBr5_cRQv5S22GXeEreYVn746qpJGAcEOACyeB5jEFy-RG0ItrGq3FAMaVVBHcHGfAJc9LCk_TaKdOdm3CvVFk67RpNxE9jVsL7oyWSNJqWkrtAsQIYxDi80ZrgGszOc.lYl0xrOMOHNFROtCHCPtng

    jwt.ioで見てみても暗号化されていることがわかります。

    生成した認可コードとstateをredirect_uriに渡してあげることで認可エンドポイントの役割はおしまいです。
    以下の様なクエリがRelying Partyに飛びます。
    https://localhost:3000/cb?code=eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTI1NktXIiwia2lkIjoiOXBheXlGVzBMdEZ0czkyRDE5VVJCSS15bHc5TGh2ZWI1dFJKYkkxU19vdyJ9.XAMPBCzLxSipsZILlvL9r6qdhL0teaYZkpO17RYgVqirv4btHMsdPQ.2kJJ1PExtsZLhI-nPSpLNQ.Kyxyt6TC2CdV81xbq8_roFaZfERZKSg9vnlInTZHnxS8eb9cVAOW_RESgKFWkt6g9vW6CXgO6qE5w-3Oe2J8jV92gVThUwlz5PcQCKgxZ6xkBr5_cRQv5S22GXeEreYVn746qpJGAcEOACyeB5jEFy-RG0ItrGq3FAMaVVBHcHGfAJc9LCk_TaKdOdm3CvVFk67RpNxE9jVsL7oyWSNJqWkrtAsQIYxDi80ZrgGszOc.lYl0xrOMOHNFROtCHCPtng&state=hoge


    ということで今回は認可エンドポイントを雑に(?)作りました。次回はトークンエンドポイントです。 

    OpenID Providerを作る)トークンエンドポイントを作る

    $
    0
    0
    こんにちは、富士です。

    色々と積み残しはありますが、認可エンドポイントの実装が終わったのでトークンエンドポイントを実装してみたいと思います。

    その前にこれまでのポストはこちらです。


    今回はトークンエンドポイントなので③が対象です。
    なお、本日紹介する部分のコードはこちらで公開しています。

    ということで始めていきましょう。

    トークンエンドポイントの役割と処理

    前回、認可エンドポイントの目標はトークンエンドポイントでトークンと交換するための認可コードを取得することである、という説明をしました。今回はトークンエンドポイントなので認可エンドポイントで発行された認可コードを受け取りトークン(IDトークン、アクセストークン、リフレッシュトークン)をクライアントへ渡すことが目標となります。

    なお、認可コードフローにおけるトークンエンドポイントの仕様の詳細はこちらに記載があります。
    3.1.3.1. Token Requestによるとリクエストをする際は、クライアントの認証をあらかじめ定めた方法で行った上でgrant_typeにauthorization_codeを指定し、取得した認可コードをPOSTする必要がある様です。

    こちらが仕様に記載されたリクエストのサンプルです。
      POST /token HTTP/1.1
      Host: server.example.com
      Content-Type: application/x-www-form-urlencoded
      Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
    
      grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
        &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    

    この例だとAuthorizationヘッダにBasicとあるのでクライアント認証にはBasic認証が使われている様です。このクライアント認証の方式は以前のポストで紹介したメタデータの中に記載されているOpenID Providerがサポートする方式(token_endpoint_auth_methods_supported)から選ぶ必要があります。取りうる値は以下の4種類です。
    • client_secret_basic:クライアントID、シークレットでHTTP Basic認証
    • client_secret_post:クライアントID、シークレットをリクエストボディに含める
    • client_secret_jwt:シークレットを使いJWTを生成し認証に利用する
    • private_key_jwt:あらかじめ公開鍵を登録済みの場合に限られるが秘密鍵で署名したJWTを利用し認証に利用する
    シンプルに実装するならclient_secret_basicとclient_secret_postの2つをサポートしておけば十分だと思います。(実際、世の中の実装を見てもこの2つのみをサポートしているOpenId Providerが多いと思います)

    今回実装するものでは実際に認証を行いませんので方式は問いませんが、Postmanでテストをする上でわかりやすい(画面ショットを撮る時一枚で済む)のでclient_secret_postを使いたいと思います。

    早速コードの全体像を見てみます。前回も述べましたがシンプルに実装するためにバックエンドDBを持たせない構成を取りますので認可コードの中にIDトークンを発行するのに必要な情報が暗号化された状態で埋め込まれている前提で実装しています。
    また、スコープ指定もopenidのみとしており、アクセストークンやリフレッシュトークンの発行に関しても考慮対象外としています。

    トークンエンドポイントの実装

    こちらがコードです。
    // トークンエンドポイント
    router.post("/token", async (req, res) => {
    // 本来なら実装する処理
    // - クライアントの認証
    // - grant_typeの検証
    // - codeの検証(有効期限、発行先クライアント、スコープ)
    // - access_tokenの発行
    // - id_tokenの発行
    constdecodedCode=awaitutils.decryptJWE(req.body.code);
    letdecodedCodeJSON=JSON.parse(decodedCode);
    // 有効期限確認
    constdate=newDate();
    if(decodedCodeJSON.exp< (Math.floor(date.getTime() /1000))){
    res.statusCode=400;
    res.json({
    errorMessage:"AuthZ code is expired."
    });
    } else {
    // payload作成
    // 単純に期限を延長しているだけ
    decodedCodeJSON.exp=Math.floor((date.getTime() + (1000*60*10)) /1000);
    decodedCodeJSON.iat=Math.floor(date.getTime() /1000);
    consttoken=awaitutils.generateJWS(decodedCodeJSON);
    console.log(token);
    res.json({
    access_token:token, // ちなみにEntra IDの場合はaccess_tokenもid_tokenとほぼ同じものが使われるケースもある。
    token_type:"Bearer",
    expires_in:3600,
    id_token:token
    });
    }
    });

    この実装を順番に解説していきたいと思います。

    まず、エンドポイントとメソッドの実装です。
    router.post("/token", async (req, res) => {
    前回も述べた通り、express routerを用いて/oauth2へのリクエストはこのJSファイルへルーティングされる様にしてありますので、/oauth2/tokenに対するPOST要求を拾う様に実装しています。
    また、認可コード(JWE)の復号処理が非同期処理となるので後半でawaitを使っています。そのためこの関数自体をasyncにしています。

    JWEの復号を行い、有効期限のチェックを行います。
    constdecodedCode=awaitutils.decryptJWE(req.body.code);
    letdecodedCodeJSON=JSON.parse(decodedCode);
    // 有効期限確認
    constdate=newDate();
    if(decodedCodeJSON.exp< (Math.floor(date.getTime() /1000))){
    res.statusCode=400;
    res.json({
    errorMessage:"AuthZ code is expired."
    });

    復号処理自体は別のJSファイルに定義した関数を使っています。こちらも前回の暗号化の処理と同じくnode-joseのライブラリを使っています。
    有効期限についてはJSONの中のexpに定義されているため、受け取った時刻と比較し、発行から30秒以内であることの確認をし、有効期限切れと判断したらHTTP 400を返却しています。

    認可コードに問題がなければ復号済みのJSONの発行時刻を付加、有効期限を更新してデジタル署名を施した上でIDトークンとしてクライアントへ送り返します。
    } else {
    // payload作成
    // 単純に期限を延長しているだけ
    decodedCodeJSON.exp=Math.floor((date.getTime() + (1000*60*10)) /1000);
    decodedCodeJSON.iat=Math.floor(date.getTime() /1000);
    consttoken=awaitutils.generateJWS(decodedCodeJSON);
    console.log(token);
    res.json({
    access_token:token, // ちなみにEntra IDの場合はaccess_tokenもid_tokenとほぼ同じものが使われるケースもある。
    token_type:"Bearer",
    expires_in:3600,
    id_token:token
    });
    }

    なお、こちらも流儀がありますがIDトークンの有効期限はそれほど長く撮る必要はないと考えています。理由はIDトークン自体は認証状態をクライアント(Relying Party)へ伝達することが第一目標となりますので、トークンの使い回しやリプレイ攻撃を避ける意味でもそれほど長い期間使われるものではないからです。今回のコードでは10分間としています。
    ちなみに認証サーバとアプリケーションの間の時刻のズレなどを勘案して伝統的に5分くらいの誤差は許容しましょう、というなんとなくな慣習が私の周りにはありましたが、現在は基本的にサーバの時刻同期がされている前提があると思うので、もう少しシビアに有効期限を設定しても良いのかもしれません。

    これで無事にIDトークンをクライアントに提供することができたので、トークンエンドポイントの役割は終わりです。実際にリクエストをPostmanで投げてみた結果がこちらです。



    この後はクライアント側の処理となるのですが、提供されたトークンが本当に最初の認証リクエストに対して発行されたものなのか、そして発行元が意図したOpenID Providerであり、このIDトークンが自クライアントに対して発行されたものなのか、途中でIDトークンの改竄がなされていないか、を確認する必要があります。

    それぞれの確認方法は以下の通りです。
    • 提供されたトークンが本当に最初の認証リクエストに対して発行されたものかどうか
      • 認証リクエストを認可エンドポイントに対して実行する際にnonceというパラメータをつけることを覚えているでしょうか?OpenID Providerはこのnonceの値をIDトークンの中に含めてデジタル署名を行いクライアントへ返却します。クライアントは返却されたIDトークンに含まれるnonceの値と最初の認証リクエストの際に指定したnonceの値が等しいことを確認し、トランザクションの一貫性を検証します。
    • 発行元が意図したOpenID Providerなのか
      • IDトークンの中のissというクレームにトークン発行元であるOpenID Providerの識別子が入りますので、クライアントはissの値を確認することで意図したOpenID Providerから発行されたトークンであることを検証します。
    • トークンが自クライアントに対して発行されたものなのか
      • IDトークンの中のaudというクレームにトークン発行先となるクライアントIDの値がセットされますので、クライアントは受け取ったaudの値が自クライアントのクライアントIDと等しいことで宛先の検証を行います。
    • トークンが途中で改竄されていないこと
      • IDトークン自体はデジタル署名付きのJWTなので、署名検証を行うことで真正性の検証を行います。検証に利用する公開鍵はjwks_uriエンドポイントから取得します。

    最後のjwks_uriの実装はこの様な形になっています。
    // jwks_uriエンドポイント
    router.get('/jwks_uri', async (req, res) => {
    constks=fs.readFileSync(path.resolve(__dirname, "../keys/keystoreSign.json"));
    constkeyStore=awaitjose.JWK.asKeyStore(ks.toString());
    res.json(keyStore.toJSON())
    });

    以下のスクリプトでnode-joseの鍵ペア生成の機能を利用してIDトークンに署名するための秘密鍵および検証するための公開鍵の生成を行い、公開鍵をKeyStoreに保存をしています。
    // 署名用鍵の生成
    constkeyStoreForSygn=jose.JWK.createKeyStore();
    keyStoreForSygn.generate('RSA', 2048, {alg:'RS256', use:'sig' })
    .then(result=> {
    fs.writeFileSync(
    'keystoreSign.json',
    JSON.stringify(keyStoreForSygn.toJSON(true), null, '')
    )
    });

    先のjwks_uriエンドポイントでは保存したKeyStoreの中身をそのまま表示しています。なお、本当は鍵のローテーションなども考慮し、クライアントは受け取ったIDトークンのJWTヘッダから鍵ID(kid)を取得し、そのIDに合致する公開鍵をjwks_uriから取得して署名検証に利用します。
    また、公開鍵の情報はそれほど頻繁に更新されるものでもないため、クライアントはjwks_uriから取得した鍵の情報をキャッシュに保存しておき、未知のkidを持つIDトークンが送られてくるまではキャッシュした公開鍵を使って署名検証を行う様に実装するのが定番だと思います。(鍵取得処理のオーバーヘッド回避の意味もあります)


    ということでトークンエンドポイントの実装はここまでです。
    認可コードフローにおけるコアな処理は前回の認可エンドポイント、今回のトークンエンドポイントでおしまいですが、次回は追加でユーザ情報を取得するためのAPIであるuserInfoエンドポイントの実装についても触れたいと思います。











    OpenID Providerを作る)UserInfoエンドポイントを作る

    $
    0
    0

    こんにちは、富士榮です。

    OpenID Connectを理解するにはOpenID Providerを作るのが一番、ということで各種エンドポイントを実装してきていますが、一応今回で基本的な部分はおしまいです。

    その前にこれまでのポストはこちらです。


    今回はUserInfoエンドポイントなので④が対象です。
    なお、本日紹介する部分のコードはこちらで公開しています。

    ということで始めていきましょう。

    UserInfoエンドポイントの役割と処理

    前回までの処理でOpenID Connectの最大の目標であるIDトークンをクライアント(Relying Party)に発行する、という処理は終わっていますので、今回のUserInfoエンドポイントについては必ずしも実装する必要はありません。
    UserInfoエンドポイントはユーザの属性情報を提供するための標準化されたAPIで、OpenID Provider(のベースとなるOAuth2.0の認可サーバ)の保護対象リソースとなります。そのためOpenID ProviderからIDトークンに加えてアクセストークンの発行を受ける必要があり、クライアントはアクセストークンをAuthorizationヘッダに付加した状態でUserInfoエンドポイントへアクセスすることでユーザの属性情報を取得します。

    なお、OpenID Connectではユーザ情報をIDトークンの中に含めることも可能なので、UserInfoエンドポイントとの使い分けをどうするのか?はID基盤全体としての設計上のポイントとなります。
    IDトークンはユーザ認証のイベントに連動して発行される一方でUserInfoは認証イベントとは非同期で(様するに後からでも)ユーザ情報を取得することができるのでクライアントの利用シーンによって使い分けることが重要です。例えばモバイルアプリケーションなどは毎回ログインするわけではないのでユーザの属性情報を取得するのにIDトークンの利用はできませんが、リフレッシュトークンを使ってバックエンドでアクセストークンを更新しつつUserInfoエンドポイントへアクセスすることでログインとは連動せずに最新のユーザ情報を取得することが可能となります。
    IDトークンUserInfoエンドポイント
    認証との連動性同期非同期
    情報の鮮度認証時点API実行時点

    なお、今回は実装を簡素化するためにバックエンドなしの構成としており、アクセストークンの情報を元にUserInfoエンドポイントからの情報返却をするため上述の使い方はできません。この辺りは今後の拡張としていきたいと思います。

    UserInfoエンドポイントの実装

    実装すべき仕様はこちらに記載されています。
    先に述べた通り簡易実装のためバックエンドでユーザDBを検索して属性を取得して、、という部分は実装せずにIDトークンと同じ内容をアクセストークンに入れておき、トークンの検証と中身の取り出しを行うことでユーザ情報を返却するAPIとして実装しています。

    こちらがコードとなります。
    // userInfoエンドポイント
    router.get("/userinfo", async (req, res) => {
    constaccess_token=req.headers.authorization.replace("Bearer ", "");
    constdecodedPayload=awaitutils.verifyJWS(access_token);
    constdecodedTokenJSON=JSON.parse(decodedPayload);
    // 有効期限確認
    constdate=newDate();
    if(decodedTokenJSON.exp< (Math.floor(date.getTime() /1000))){
    res.statusCode=403;
    res.json({
    errorMessage:"access token is expired."
    });
    } else {
    // 不要な要素を削除する
    deletedecodedTokenJSON.iss;
    deletedecodedTokenJSON.aud;
    deletedecodedTokenJSON.exp;
    deletedecodedTokenJSON.iat;
    res.json(decodedTokenJSON);
    }
    });

    処理としては、まずアクセストークンの署名検証と有効期限の検証を行います。
    constaccess_token=req.headers.authorization.replace("Bearer ", "");
    constdecodedPayload=awaitutils.verifyJWS(access_token);
    constdecodedTokenJSON=JSON.parse(decodedPayload);
    // 有効期限確認
    constdate=newDate();
    if(decodedTokenJSON.exp< (Math.floor(date.getTime() /1000))){
    res.statusCode=403;
    res.json({
    errorMessage:"access token is expired."
    });

    アクセストークンは通常Authorizationヘッダに「Bearer xxxx」という形でAPIに送信されることが多いので今回もヘッダからアクセストークンを取り出しています。その上でJWSの署名検証を前回までと同様にnode-joseのライブラリを使って実施しています。
    その上で、トークンのexpの値と現在時刻を比較して有効期限が切れていないことの確認を行なっています。

    検証に成功したら、アクセストークン(今回はIDトークンと同じものを使うEntra方式を取ります)から不要な属性(iss、aud、exp、iat)を取り除いた上でJSONとしてクライアントへ返却しています。
    } else {
    // 不要な要素を削除する
    deletedecodedTokenJSON.iss;
    deletedecodedTokenJSON.aud;
    deletedecodedTokenJSON.exp;
    deletedecodedTokenJSON.iat;
    res.json(decodedTokenJSON);
    }
    });


    PostmanでUserInfoエンドポイントを叩くとこんな感じで値が返ります。

    ということでUserInfoエンドポイントについても実装ができました。
    これで最低限のプロトコルの流れは実装できましたので、次回以降はこれまで実装を飛ばした部分を確認しながら実装していきたいと思います。


    OpenID Providerを作る)response_typeを実装する

    $
    0
    0

    こんにちは、富士榮です。

    前回までで一通り認可コードフロー(code flow)の流れを実装しました。



    ただ、簡易的にプロトコルの流れを知る目的だったこともあり、かなりの実装を飛ばしてきました。今回はその中の一つのresponse_typeパラメータについてです。

    このパラメータはこれまでみてきた認可コードフローに代表されるOpenID Connectのフロー(トークンの払い出しを行うための処理の流れ)を指定するための利用します。
    OpenID Connect core 1.0で定義されているフローには、以下の種類が存在します。
    • 認可コードフロー(Authorization code flow)
    • インプリシットフロー(Implicit flow)
    • ハイブリッドフロー(Hybrid flow)

    一番よく使われているのは認可コードフローだと思いますが、特定の条件下においてはインプリシットやハイブリッドフローを用いられることがあります。
    例えば、インプリシットフローはトークンエンドポイントに対してRelying Partyからバックエンド通信が発生しないので、OpenID Providerが社内などファイアーウォールの内側に配置されていてRelying Partyがクラウド上に配置されているなど、Relying PartyからOpenID Providerへ直接の通信が行えない環境において利用されることがありますし、認可コードとアクセストークンとIDトークンが同一のトランザクションで発行されていることを確認したいような高いセキュリティレベルが求められるようなケースではハイブリッドフローが用いられることがあります。

    認可エンドポイントの実装を拡張する

    実装をする上では認可エンドポイントでresponse_typeパラメータに指定された値をみて振る舞いを変えていくことになります。
    実際にパラメータとして指定される可能性があるのは、以下のパターンです。尚、複数値が指定される場合のデリミタはスペース(%20)です。
    • 認可コードフロー
      • response_type=code
    • インプリシットフロー
      • response_type=token
      • response_type=id_token
      • response_type-token id_token
    • ハイブリッドフロー
      • response_type=code token
      • response_type=code id_token
      • response_type=code token id_token

    実装方法は色々とあると思いますが、まずはクエリパラメータからresponse_typeを取得して配列に入れておこうと思います。
    // response_typeの判断
    constresponse_types=req.query.response_type.split("");
    尚、本来は値のサニタイズなどちゃんと実装してください。

    あとはどの値が含まれるかによって処理を変えていきます。
    • codeが含まれる場合
      • 認可コードを生成する
    • id_tokenが含まれる場合
      • id_tokenを生成する
    • tokenが含まれる場合
      • access_tokenを生成する
    またid_token、tokenが含まれる場合はインプリシットもしくはハイブリッドフローなので、redirect_uriへのリダイレクト時のパラメータはクエリ文字列(?)ではなくフラグメント(#)で返却する必要があります。

    そこで、返却パターンを判別するためのフラグを用意しました。
    // implicitもしくはHybridを判定するフラグ(フラグメントでレスポンスを返すかどうかの判定)
    letisImplcitOrHybrid=false;

    また、返却するパラメータを入れておくバッファとして利用する空の配列も定義しておきます。
    // レスポンスを保存する配列
    letresponseArr= [];

    これまで解説したとおり、認可コード、IDトークン、アクセストークンの中身はバックエンドサーバもないので共通のものを使っています。
    // ペイロード(暫定なので固定値。有効期限関係だけ個別に含める)
    letpayload= {
    iss:baseUrl,
    aud:req.query.client_id,
    sub:"test",
    email:"test@example.jp",
    name:"taro test",
    given_name:"taro",
    family_name:"test",
    nonce:req.query.nonce
    };

    これで下準備は済んだので実際にresponse_typeの値を判別して処理をしていきましょう。
    まずはcodeです。
    この場合は認可コードを生成するので、前回までに実装したコードをベースにJWEを生成します。生成したら返却するパラメータに追加しておきます。なお、有効期限(exp)は短めです。
    if(response_types.includes("code")){
    // code flow
    // 認可コードの作成
    payload.exp=Math.floor((date.getTime() + (1000*30)) /1000); // 有効期限は30秒
    constcode=awaitutils.generateJWE(payload);
    responseArr.push("code="+code);
    }

    次はid_tokenです。
    こちらはiat(発行した時刻)に現在時刻を設定し、有効期限(exp)は10分にしておきます。また、インプリシットかハイブリッドであることが確定するのでisImplicitOrHybridのフラグをONにしておきます。最後に認可コードと同じように返却するパラメータに値を追加しておきます。
    if(response_types.includes("id_token")){
    // implicit/hybrid
    // id_tokenの生成
    payload.exp=Math.floor((date.getTime() + (1000*60*10)) /1000);
    payload.iat=Math.floor(date.getTime() /1000);
    constid_token=awaitutils.generateJWS(payload);
    responseArr.push("id_token="+id_token);
    isImplcitOrHybrid=true;
    }

    最後にtokenです。
    こちらも基本はid_tokenと同じことをやりますが、有効期限(exp)は60分にしておきます。
    if(response_types.includes("token")){
    // implicit/hybrid
    // access_tokenの生成
    payload.exp=Math.floor((date.getTime() + (1000*60*60)) /1000);
    payload.iat=Math.floor(date.getTime() /1000);
    constaccess_token=awaitutils.generateJWS(payload);
    responseArr.push("access_token="+access_token);
    isImplcitOrHybrid=true;
    }

    response_typeによる振り分けが終わったら、Relying Partyから指定されたstateの値を返却するパラメータに追加しておきます。Relying Party側でのCSRF対策用ですね。
    // stateをレスポンスに含める
    responseArr.push("state="+req.query.state);

    そして返却するパラメータを&で繋ぎ合わせます。
    constresponseParam=responseArr.join("&");

    最後にインプリシットもしくはハイブリッドフローならフラグメントに、認可コードフローならクエリ文字列に返却パラメータを入れてredirect_uriへリダイレクトします。
    if(isImplcitOrHybrid){
    // implicit/Hybrid flowなのでフラグメントでレスポンスを返却する
    res.redirect(req.query.redirect_uri+"#"+responseParam);
    } else {
    // code flowなのでクエリでレスポンスを返却する
    res.redirect(req.query.redirect_uri+"?"+responseParam);
    }

    これで認可エンドポイントの拡張はおしまいです。

    ディスカバリエンドポイントの拡張

    上記実装によりこのOpenID Providerがサポートするフローが増えていますので、Discoveryエンドポイントで提供するメタデータにも情報を追記しましょう。
    response_types_supportedが返却する値に対応するフローの値を追加しておきます。
    constresponse_types= ["code", "code token", "code id_token", "code token id_token", "token", "id_token"];

    これで全て完了です。

    動作確認

    ちょうとMicrosoftが提供しているJWTデコードツール(https://jwt.ms)はインプリシット・ハイブリッドフローで提供されるIDトークンのデコードに対応しています。

    http://localhost:3000/oauth2/authorize?
       response_type=id_token&
       client_id=111&
       redirect_uri=https://jwt.ms&
       state=hoge

    という形でjwt.msをredirect_uriにセットし、response_type=id_tokenでリクエストしてみます。
    https://jwt.ms/#id_token=eyJ0eXAiOiJqd3QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImpURkZodFR4ZVVsUHFEVlNEQ0dTNVBCOF9HNkpTSjdIS0IxSGFyQU1GMUkifQ.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDozMDAwIiwiYXVkIjoiMTExIiwic3ViIjoidGVzdCIsImVtYWlsIjoidGVzdEBleGFtcGxlLmpwIiwibmFtZSI6InRhcm8gdGVzdCIsImdpdmVuX25hbWUiOiJ0YXJvIiwiZmFtaWx5X25hbWUiOiJ0ZXN0IiwiZXhwIjoxNzA1MTM0NjAyLCJpYXQiOjE3MDUxMzQwMDJ9.bSgUeKQBf8cYv2wveFvq5IFTWDpYUu7_s2iVci3dZKyp8tkful3u1Jj9oN4xzS-mn8bz-Ww9a4jIS6KyTKBgeT6HHMlhF-YiMtwMn8_FKqpOY_O94GPFkgUMgwB3Qu1QGTxx5O4fWCzQoZpUFgFF5JW339CAAH4TRtDRcjSJnBR5z7aMU8Ig-UQXIuhLtAAuHDZ01fc3xYyXiOWi4C9Rio6yFDxrO-kVxgAlFWfP7k8qvUEWrX3IlZYIVQQ0GgNtKQSAxGVQZxb9TgShZi2sQvl2EeE-I9DKVSw9W_OI6UOTIdQlc4KNQ5qqLHsuoE74iE1SVf6Oqr_Z67NZYm3GMw&state=hoge
    という形でフラグメントにIDトークンが返却され、jwt.msのサイトでデコードされた情報が表示されます。

    ということで今回はresponse_typeについて実装してみました。
    今回の拡張部分についてもこちらのレポジトリに反映してありますので、全体を確認したい方はご覧ください。

    引き続き実装を拡張していきます。

    OpenID Providerを作る)Hybridフローを実装する

    $
    0
    0

    こんにちは、富士榮です。

    認可コードフロー(code flow)の流れの実装に加えてresponse_typeの実装を行いインプリシットとハイブリッドフローの入り口を実装してきました。


    前回のインプリシットおよびハイブリッドフローの実装はあくまで入り口でしたが、今回はハイブリッドフローの主たる目的にフォーカスしてみましょう。

    前回ハイブリッドフローの説明として、以下を述べました。
    認可コードとアクセストークンとIDトークンが同一のトランザクションで発行されていることを確認したいような高いセキュリティレベルが求められるようなケースではハイブリッドフローが用いられることがあります。
    仕様としてはこちらに記載されている通りですが、特徴的なのは認可エンドポイントが認可コードやアクセストークンと共にIDトークンを返却し、返却されたIDトークンが認可コードやアクセストークンと同一のトランザクションであること、つまりコード置き換えなどの攻撃が行われていないことを検証可能としている点です。
    この実装を行うためにデジタル署名されたIDトークンの中に認可コードやアクセストークンのハッシュの値がc_hash(認可コードのハッシュ)やat_hash(アクセストークンのハッシュとして埋め込みます。
    このことにより各種トークン(認可コードを含む)を発行するサーバ(OpenID Provider)側でトークンが一つのトランザクションの中に紐づいていることを保証するわけです。

    ということで早速実装していきましょう。
    仕様を確認すると、認可コードの検証するためにはc_hash、アクセストークンを検証するためにはat_hashという属性値を生成する必要がありそうです。
    認可コードのハッシュは以下の仕組みで実装する必要があるようです。
    • code の ASCII オクテットのハッシュ値を計算する. ハッシュアルゴリズムは, ID Token の JOSE Header に含まれる alg Header Parameter で利用されるものを利用する. 各 alg Header Parameter に対応するハッシュアルゴリズムは JWA [JWA] で定義されている. たとえば, alg が RS256 であれば, 使用されるハッシュアルゴリズムは SHA-256 である.
    • ハッシュの左半分を取得し, base64url エンコードする.

    同じくアクセストークンのハッシュは以下通り生成する必要があるようです。

    • Access Token のハッシュ値. この値は, access_token の ASCII オクテット列のハッシュ値の左半分を base64url エンコードしたものであり, ハッシュアルゴリズムは ID Token の JOSE Header にある alg Header Parameter で用いられるハッシュアルゴリズムと同じものを用いる. 例えば alg が RS256 であれば, access_token の SHA-256 ハッシュ値を計算し, その左半分の128ビットを base64url エンコードする. at_hash は大文字小文字を区別する文字列である.

     

    要するに、それぞれの値の半分に割った左側の値をIDトークンの署名アルゴリズムに沿ってハッシュを取得してBase64Urlエンコードした値をIDトークンの中に含めれば良いと言うことです。

    ということで実装していきます。

    exports.createHash=functioncreateHash(plainText){
    // SHA256でハッシュを取る
    consth=crypto.createHash('sha256').update(plainText).digest('hex');
    // 左半分をBase64Urlエンコードする
    returnBuffer.from(h.slice(0, h.length/2)).toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/g, '');
    }

    今回はIDトークンはRS256で署名するのでハッシュはSHA256なので、こんな感じの実装になりそうです。

    上記に記載した通り、

    1. SHA256でハッシュを取る
    2. ハッシュの前半をBase64Urlエンコードする
    というロジックです。

    これを認可コード、アクセストークンに適用します。
    認可コードはこんな感じ。

    if(response_types.includes("code")){
    // code flow
    // 認可コードの作成
    payload.exp=Math.floor((date.getTime() + (1000*30)) /1000); // 有効期限は30秒
    constcode=awaitutils.generateJWE(payload);
    responseArr.push("code="+code);
    // c_hashの作成
    c_hash=utils.createHash(Buffer.from(code));
    }

    アクセストークンはこんな感じです。

    if(response_types.includes("token")){
    // implicit/hybrid
    // access_tokenの生成
    payload.exp=Math.floor((date.getTime() + (1000*60*60)) /1000);
    payload.iat=Math.floor(date.getTime() /1000);
    constaccess_token=awaitutils.generateJWS(payload);
    responseArr.push("access_token="+access_token);
    isImplcitOrHybrid=true;
    // at_hashの作成
    at_hash=utils.createHash(Buffer.from(access_token));
    }


    これらの値(c_hash、at_hash)の値をIDトークンに含めていきます。

    if(response_types.includes("id_token")){
    // implicit/hybrid
    // id_tokenの生成
    payload.exp=Math.floor((date.getTime() + (1000*60*10)) /1000);
    payload.iat=Math.floor(date.getTime() /1000);
    // codeがある場合
    if(response_types.includes("code")){
    payload.c_hash=c_hash;
    }
    // access_tokenがある場合
    if(response_types.includes("token")){
    payload.at_hash=at_hash;
    }
    constid_token=awaitutils.generateJWS(payload);
    responseArr.push("id_token="+id_token);
    isImplcitOrHybrid=true;
    }


    これであとはRelying Partyが受け取った認可コードをc_hashで、アクセストークンをat_hashで検証することでトランザクションが同一で合ったことの検証を行います。



    どうしてもOAuthやOpenID Connectはプロトコル上行ったり来たりするのでこのようにトランザクションを確認する仕組みが必要になりますね。

    OpenID Providerを作る)Pairwise識別子を実装する

    $
    0
    0

    こんにちは、富士榮です。

    前回はHybridフローに必要なc_hashやat_hashの実装をしてみました。


    今回はユーザの識別子の話をしたいと思います。
    識別子の話はそれだけでご飯が3杯くらい食べられるくらいのおかずなので、今後も触れるかもしれませんが今回はPairwise識別子と実装についてみていこうと思います。

    そもそもPairwise識別子とは

    OpenID Providerを構築する場合、複数のRelying Partyと連携することが前提となると思います。(そうでなければ単にアプリケーションにローカルで認証する仕組みを組み込めば良いはずです)
    その場合に全てのRelying Partyがいわゆる1stパーティ(例えば自社サービス)である場合だけでなく、3rdパーティ(多事業者のサービス)との連携を行うケースも想定しなければならないことがあります。
    基本的にOpenID Providerが各Relying Partyに提供するユーザの情報は「ユーザの同意」に基づく「必要最低限」である必要があります。この辺りの原則は故Kim Cameronが提唱した「The Laws of Identity」の第1原則「User Control and Consent(ユーザによる制御と同意)」、第2原則「Minimal Disclosure for a Constraint Use(制限された利用に対する最低限の開示)」に準じて設計を行う必要があります。この辺りも2009年にポストしましたし、先日ポストしたVerifiable CredentialsにおけるSD-JWTの話題もこの原則を実現するために開発された仕様です。

    前置きが長くなりましたが、この話と識別子の話がどのように関係してくるかというと、簡単にいうと「名寄せ」を簡単に&機械的にできないようにするために識別子をRelying Party毎にユニークな値にしていきましょう、というのが今回のテーマである「Pairwise識別子」の役割です。なお、Pairwise識別子のことをPPID(Pairwise Pseudonymous Identifier)とか仮名という言い方をすることもあります。

    例えば、以下の図のような状態だと全てのRelying Partyへ同じ識別子(sub)の値(123)が提供されるので、例えばRelying Party同士が結託すると本当はメールアドレスしか提供するつもりではなかったのに名前や誕生日をユーザが意図しない形で収集されてしまう、という状態が発生します。もちろんこれが必ずしも悪なのか、というとそういうわけではなく先に書いたRelying Partyが全て1stパーティなどのケースでユーザが包括的に属性提供に関する同意をしているケースについては問題にはなりません。このようなケースにおける識別子のタイプをOpenID Connectの仕様では「public」として定義しています。

    しかしながら、主に3rdパーティのRelying Partyとの連携を前提とすると先ほどのThe Laws of Identityの原則に従うと各Relying Party同士が結託しても簡単には属性情報を取得できないようにする必要があるわけです。そこで識別子のタイプに「pairwise」を使用し、以下のような状態を作り上げます。

    この状態であれば各Relying Partyは隣のRelying Partyにアクセスしにきているユーザが自分のところにアクセスしてきているユーザと同一のユーザなのかを機械的に判断するのは難しくなります。(なお、識別子をpairwiseにしてもメールアドレスなど他の属性で識別可能になってしまう実装は世の中にたくさんあります。この辺りはユーザに対して提供する利便性とのバランスを含め考えていく必要があると思います)

    そういえば10年くらいまでに仮名に関する整理をした記憶が蘇ってきました。当時はSAMLについて書いてました。
    ※しかしslideshare、広告が出まくるので使いにくくなりましたね。。

    識別子というからにはRelying Partyが利用者を識別できなければならない、という話はあるのですが、SAMLにおいてはtransientというタイプの匿名識別子も定義されており、仮名(pairwise)と明確に区別されているのが面白いですね。

    この辺りは一貫性のあるPairwise識別子の提供を求めるOpenID Connectの思想とは少し異なるのかもしれません。

    Pairwise識別子を実装する

    OpenID Connect coreの仕様の中にPairwise識別子の生成に関する記述がありますので、そちらを参照しつつ実装していきましょう。

    Dynamic Client Registrationを使う場合は追加で色々と考えることがありますが、基本的に以下の原則に従って実装する必要があります。
    • Subject Identifier 値が, OpenID Provider 以外の Party にとって, 可逆であってはならない (MUST NOT).
    • 異なる Sector Identifier 値は, 異なる Subject Identifier 値にならなければならない (MUST).
    • 同じ入力に対して必ず同じ結果となる決定的アルゴリズムでなければならない (MUST).
    また、アルゴリズムの例として以下のパターンが提示されています。
    • Sector Identifierを, ローカルアカウントIDおよび Provider によって秘密にされているソルト値と連結する. そして連結した文字列を適切なアルゴリズムによってハッシュ化する.
      • sub = SHA-256 ( sector_identifier || local_account_id || salt ) を計算する.
    • Sector Identifierを, ローカルアカウントIDおよび Provider によって秘密にされているソルト値と連結する. そして連結した文字列を適切なアルゴリズムによって暗号化する.
      • sub = AES-128 ( sector_identifier || local_account_id || salt ) を計算する.
    • Issuer は, Sector Identifier とローカルアカウントIDのペアに対し, Globally Unique Identifier (GUID) を作成する.
    このSector IdentifierはクライアントであるRelying Partyがコントロールする値であるべきということもあり特にDynamic Client Registrationをサポートする場合はsector_identifier_uriを利用し、Sector Identifierは当該URLのホスト部を利用するのが一般的ですが、今回はredirect_uriのホスト部を使っておきましょう。ちなみに仕様上はホスト部(原文host component)とあるのでurl.hostname(ポート番号を含まないホスト名)ではなくurl.host(ポート番号指定がある場合はポート番号を含む)を利用すべきなんだと思いますが世の中の実装がどうなっているかは知りません。

    ということで今回はこんな仕様で実装してみます。
    • sector_identifierはredirect_uriのホスト部を利用する
    • local_identifierはユーザ認証をしていないので固定値を使う
    • saltはOpenID Providerにハードコードした値を使う
    • 上記3つの値を連結した文字列をSHA256でハッシュした値をPairwise識別子としてsubにセットする

    ということで識別子を生成するfunctionを書いていきます。
    まずsaltは固定値をコードにかいちゃいます。
    // Pairwise識別子生成用のsalt
    constsaltForPPID="1234567890";

    その上で生成する関数を作ります。
    // Pairwise識別子の生成
    exports.createPPID=functioncreatePPID(local_identifier, redirect_uri){
    // sector_identifierの生成
    consturl=newURL(redirect_uri);
    constsector_identifier=url.host;
    returncrypto.createHash('sha256').update(sector_identifier+local_identifier+saltForPPID).digest('hex');
    }

    ユーザ情報を含むペイロード生成時に上記関数で作成したPairwise識別子を埋め込みます。
    // Pairwise識別子の生成
    constPPID=utils.createPPID("test", req.query.redirect_uri);
    // ペイロード(暫定なので固定値。有効期限関係だけ個別に含める)
    letpayload= {
    iss:baseUrl,
    aud:req.query.client_id,
    sub:PPID,
    email:"test@example.jp",
    name:"taro test",
    given_name:"taro",
    family_name:"test",
    nonce:req.query.nonce
    };

    これで実装は完了です。

    一応Discovery側も変更しておきましょう。
    constsubject_types= ["pairwise"];

    こちらがredirect_uriにhttps://jwt.msを指定した際のsubを含むIDトークンです。


    こちらが別のredirect_uriを指定した際のsubの値です。


    識別子(sub)の値が異なることがわかりますね。
    ということで今回はPairwise識別子の実装をしてみました。

    ここまでの実装を含むコードはこちらに公開してありますので参考にしてください。

    前回のOpenID Summit Tokyoを振り返る

    $
    0
    0

    こんにちは、富士榮です。

    いよいよ今週末、4年に一度のOpenID Summit Tokyoが渋谷ストリームで開催されます。

    見どころなどは明日にでも紹介しようと思いますが、その前に4年前を少しだけ振り返っておきましょう。

    前回のテーマは「真のDXを支えるID」でした。時代的にDXというキーワードが出始めていた時期ですし、OpenID Connectの適用先もコンシューマからエンタープライズへ本格的に広がってきつつある時期だったと思います。また、新たなキーワードとして自己主権型アイデンティティという言葉もこの時期から注目を集め始めていた頃です。

    こちらが前回のプログラムですが、KYCやFAPI、FIDOなども注目のテーマの一つでしたね。

    時刻Grand hall(同時通訳あり)Breakout room
    10:00 - 10:10

    開会の挨拶

    Nov Matake — 一般社団法人OpenIDファウンデーション・ジャパン 事務局長
    Keynote1-3 同時中継 / エバに相談コーナー
    10:10 - 10:40

    Keynote 1 - The Future of Identity

    10:40 - 11:10

    Keynote 2 - Personal Digital Transformation and Holistic Digital Identity

    11:10 - 11:40

    Keynote 3 - Enabling large-scale multi-party federations with OpenID Connect

    11:40 - 12:30休憩
    12:30 - 13:00

    Open Banking beyond PSD2 in the EU

    Device Identity as Yet Untold - エンタープライズ・セキュリティとデバイスIdentity

    13:00 - 13:30

    次世代IDaaSのポイントは本人確認~NISTと、サプライチェーンセキュリティと、みなしごID~

    江川 淳一 — エクスジェンネットワークス株式会社 代表取締役

    公開情報から読むCloud-assisted BLE (caBLE) をつかったWebAuthn

    13:30- 14:00

    OpenIDファウンデーション・ジャパン KYC WGの活動報告

    OIDC活⽤で⽬指す⼈やサービスがつながる世界の社会実装

    14:00- 14:30

    韓国におけるFIDO/eKYC/DIDの現状と今後の取り組み

    Bradly Kim — RaonSecure Co., Ltd.R&D Center/CTO

    OpenID Connectとネイティブアプリを取り巻く仕様と動向 - Yahoo! JAPANの取り組み

    14:30 - 14:50休憩
    14:50- 15:20

    銀行オープンAPIの実装 ―これまでの歩みとこれから必要なこと

    三輪 純平 — 金融庁 フィンテック室長

    FIDO (WebAuthN) を利用したID認証のデザインパターン

    15:20- 15:50

    SNS/FIDOによるセキュアなID統合管理と活用ケース - クラウドID基盤SELMIDのご紹介

    花井 杏夏 — 伊藤忠テクノソリューションズ株式会社 西日本ビジネス開発チーム

    OpenID Connect Client Initiated Backchannel Authentication Flow (CIBA)のご紹介 ~ その新しいUXと実装例のご紹介

    15:50- 16:20

    OpenID Connect活用したgBizID(法人共通認証基盤)の現状と今後の展望

    満塩 尚史 — 経済産業省 CIO補佐官

    Our Identity Experience

    16:20 - 16:50

    NIIが進める研究データ管理空間

    山地 一禎 —国立情報学研究所
    (Closed)
    16:50 - 17:10休憩
    17:10 - 17:40

    Closing Keynote - No ID, No DX

    (Closed)
    17:40 - 17:50

    閉会の挨拶

    楠 正憲 — 一般社団法人OpenIDファウンデーション・ジャパン 代表理事
    (Closed)


    前回のサミットで思い出深い点が何点かあるのでその点を少しだけ紹介させてください。

    コロナ直前のイベントだった

    ちょうどこの直後からコロナの話題が大きく広がり、4月からは全国で外出自粛やリモートワークが展開され、結果としてしばらく大規模イベントとしての開催は休止されたり延期される事態になりました。またいみじくもコロナ禍においてはリモート環境における本人確認などID分野でもさまざまな新しいチャレンジが行われることになりましたが、その直前にこのようなイベントが開催できたのは非常に感慨深いものがありました。

    Kim Cameron氏の最後の国際イベント登壇だった

    非常に残念なことですが、この年のOpenID SummitがKim Cameron氏存命中の最後のリアル開催のイベント登壇となってしまいました。2020年のSummitでは自己主権型アイデンティティやブロックチェーンにアンカリングされた分散型識別子(DID)が話題に上り始めていた時期だったのでKimにはそれらの動向を踏まえて今後のデジタルアイデンティティがどのように変化してくのかを話してもらいたいというお願いをして来日いただいたのを鮮明に覚えています。
    Kimのスピーチはこちら
    本ブログでの文字起こしと翻訳はこちら

    自己主権型アイデンティティからVerifiable Credentialsへ

    先にも触れた通り、自己主権型アイデンティティの文脈が今後どうなっていくのか?について議論をする目的もありKim Cameron氏に来日いただいたこともあり、実はイベントの翌週にOpenID Foundation関係者で再度イベント(合宿)を行いました。こちらは残念ながら非公開なのですが、主に企画をOpenID FoundationでeKYC and Identity Assurance WGの共同議長を一緒にやっていたTorsten Lodderstedt氏と私で取り回していました。元々はeKYCの文脈で自己主権型アイデンティティや分散型識別子(DID)、検証可能な資格情報(Verifiable Credentials)をどのように利活用すべきか、という議論を半日かけてKim Cameron氏の考えを伺いながら議論をしました。この再度イベントには後にW3CのVerifiable Credentials Working Groupの共同議長、最近OpenID Foundationに設立されたDigital Credentials Protocol Working Groupの共同議長を務めることになる安田クリスチーナさんも参加していました。その流れもありTorsten Lodderstedt氏と安田クリスチーナさんが現在のOpenID for Verifiable Credentials関連のプロファイルの仕様策定者となっているのは非常に感慨深いものがあります。まさにこのサイドイベントがきっかけとなりデジタルクレデンシャルに関する仕様が産まれて4年が経過し、EUや日本の政府機関でもVerifiable Credentialsに関するプロジェクトが進み、まさに今年OpenID Summitが再び東京で開催されるのは運命的なものを感じます。


    本当にこの4年間は世界が大きく変わったといっても過言ではない4年間だったと思います。私たちアイデンティティ・コミュニティもKim Cameron氏に加えてCraig Burton氏、そして昨年はVittorio Bertocci氏などかけがえのない方々を送り出すことになってしまいましたし、もしかすると皆様にとっても大切な人々が旅立ってしまったということもあったかもしれません。

    ぜひ皆さんもこの4年間を振り返っていただき、OpenID Summitをお迎えください。皆様にお会いできるのを楽しみにしております。

    いよいよ明日からOpenID関連のイベントが開催されます!

    $
    0
    0
    こんにちは、富士榮です。

    いよいよ明日〜に迫ってきました。4年に一度日本で開催されるOpenID Summit Tokyoが今週金曜日、2024年1月19日に渋谷ストリームで開催されます。また、その前日の18日(木)には米国OpenID Foundation主催のOpenID Foundation Hybrid Workshopが大手町のKDDIホールで開催されます。



    すでにレジストレーションは終わってしまっていますのでこれから新規に申し込んでいただくことはできませんが、簡単に見どころを。

    OpenID Foundation Hybrid Workshop


    アジェンダ:

    Workshop Agenda

    TIME

    TOPIC

    PRESENTERS

    1:00-1:05pm

    Welcome

    Nat Sakimura

    1:05-1:15pm

    2024 OIDF Strategic Initiatives

    Gail Hodges & Dima Postnikov

    1:15-1:35pm

    2024 OIDF-Japan Strategic Initiatives

    Naohiro Fujie

    1:35-1:50pm

    AuthZEN WG Update

    David Brossard

    1:50-2:05pm

    AB/Connect WG Update

    Michael Jones

    2:05-2:20pm

    eKYC & IDA WG Update

    Mark Haine

    2:20-2:35pm

    MODRNA WG Update

    Bjorn Hjelm

    2:35-2:50pm

    DCP WG Update

    Torsten Lodderstedt

    2:50-3:05pm

    OIDF Certification Program Update

    Joseph Heenan

    3:05-3:25pm

    FAPI WG Update & FAPI Ecosystem Engagement

    Nat Sakimura, Mike Leszcz & Elcio Calefi

    3:25-3:50pm

    SIDI Hub Brief & Survey

    Gail Hodges

    3:50-4:00pm

    Q&A Panel & Closing Remarks

    Nat Sakimura


    OpenID Foundation Workshopは主要なアイデンティティ関連のイベントの周辺で開催されるもので、OpenID Foundationの各ワーキンググループの仕様策定状況のアップデートや関連するトピックスについて紹介されるワークショップです。最近OpenID Foundationが何をやっているのかを数時間でまとめて見ることができるので非常にお得なイベントです。

    今回はOpenID Foundationの今後の戦略・方向性および各ワーキンググループからの発表に加えて、OpenIDファウンデーションジャパンが日本国内で行なっている活動について私が発表させていただくのと、最近OpenID FoundationもスポンサーとなっているSIDI Hub(Sustainable & Interoperable Digital Identity Hub)に関してOpenID FoundationのExecutive DirectoryのGail Hodgesから紹介される予定です。

    OpenIDファウンデーションジャパンは米国OpenID Foundationの支援を受けて日本国内でOpenID関連技術の普及啓発を行うための法人ということもあり、他のOpenID Foundationのワーキングループとは少し違った動き方をしています。具体的には技術仕様の策定などのフォーラム標準化を行うわけではなく、あくまで日本国内における普及啓発活動が中心となり法人としても米国の法人とは独立したものとして設立されています。そのため、定期的に米国側への活動のレポートをすることで支援を受けているわけなのですが、グローバルのアイデンティティコミュニティにその存在や活動内容が知られる機会がそれほどあるわけではありません。今回は日本開催であること、ハイブリッド開催であり各国からも参加者がいることから良い機会ですのでOpenIDファウンデーションジャパンの活動を広く知ってもらう場として活用する予定です。
    また、Gailが紹介するSIDI Hubについては、昨年11月にパリで開催されたTRUSTECH 2023の中で併催されたSIDI Hub Summitでは日本政府を含む各国政府も支援を表明しています。SIDI Hubはグローバルサウスを含む全世界におけるデジタルアイデンティティに関する持続可能で相互運用性が担保されたアイデンティティ層をどのように構築していくのか、を大きなテーマとして定義しています。今後、日本もこのような取り組みに積極的に参加することで国際社会での役割を果たしていくことが重要になってくると考えらますので注目しておく必要があります。

    OpenID Summit Tokyo 2024

    アジェンダ:
    時刻Grand hall(同時通訳あり)Breakout room
    10:00 - 10:20

    開会の挨拶

    富士榮 尚寛 — 一般社団法人OpenIDファウンデーション・ジャパン 代表理事
    曽我 紘子 — 一般社団法人OpenIDファウンデーション・ジャパン 事務局長
    10:20 - 10:45

    OIDF Strategic Outlook for 2024 and Briefing on the Sustainable Interoperable Digital Identity (SIDI) Summit in Paris

    10:45 - 11:10

    OpenIDファウンデーション・ジャパン ワーキンググループ活動報告

    小岩井 航介 — KDDI 株式会社 サービス開発1部 ID・認証開発担当エキスパート
    Nov Matake — 一般社団法人OpenIDファウンデーション・ジャパン エバンジェリスト
    kura(倉林 雅)— 一般社団法人OpenID ファウンデーション・ジャパン 理事、エバンジェリスト
    11:10 - 11:40

    Panel: Celebrating Ten Years of OpenID Connect

    Moderator:
    Michael B. Jones — Building the Internet’s Missing Identity Layer, Self-Issued Consulting
    Panelists:
    Nat Sakimura — Chairman, OpenID Foundation
    Nov Matake — 一般社団法人OpenIDファウンデーション・ジャパン エバンジェリスト
    ritou — 一般社団法人OpenIDファウンデーション・ジャパン エバンジェリスト
    11:40 - 12:50休憩
    12:50 - 13:15

    EU Digital Identity Wallets (eIDAS 2) - status and way forward

    デジタルアイデンティティの技術を学ぼう! ~認証認可にまつわる標準仕様文書を読んでみよう~

    13:15 - 13:40

    Waiting for the EUDI Wallet: Securing the transition from SAML 2.0 to OpenID Connect

    Amir Sharif — Researcher at the Center for Cyber Security, Security & Trust research unit, Fondazione Bruno Kessler, Trento, Italy.

    OpenID Connectの活用実績とLINEヤフーの会社合併におけるID連携の貢献

    依馬 裕也 — LINEヤフー株式会社 マーケティングソリューションカンパニー ビジネスプラットフォーム 統括本部 ビジネスソリューション開発本部 ソリューションマーケティング部 部長
    三原 一樹 — LINEヤフー株式会社 コミュニケーションカンパニー LY会員サービス統括本部 ID本部 部長
    吉田 享平 — LINEヤフー株式会社 コミュニケーションカンパニー LY会員サービス統括本部 ID本部
    13:40 - 14:00

    Insights into Open Wallet Foundation's initiatives

    AMA (Ask Me Anything) on OpenID Connect

    Michael B. Jones and OIDF-J evangelists
    14:00 - 14:20休憩
    14:20 - 14:45

    Trusted Webの実現に向けて

    OpenID Connect活用時のなりすまし攻撃対策の検討

    14:45 - 15:10

    OpenID Federation 1.0: The Trust Chain vs The x.509 Certificate Chain

    メルカリアプリのアクセストークン: 独自仕様からOAuth 2.0 / OIDCベースへの切り替え

    15:10 - 15:30

    Passkeys and Identity Federation

    ritou — 一般社団法人OpenIDファウンデーション・ジャパン エバンジェリスト

    オープンソース・ソフトウェアへのOAuth 2.0ベースのセキュリティプロファイルの実装

    15:30 - 15:50休憩
    15:50 - 16:15

    The progress of Nubank and Open Finance in Brazil

    OIDFシェアードシグナルフレームワーク(ID2)を利用してリアルタイムでセキュリティシグナルを共有するための最新情報

    16:15 - 16:40

    事業の成長にどのようにID技術/IDチームが貢献してきたか - SoftBank の取り組み

    Verifiable Credential Demo ~ SD-JWT VC & mdoc/mDL issuance using OpenID for Verifiable Credential Issuance

    16:40 - 17:10

    パネルディスカッション: 組織内に「IDチーム」を確立・拡大するには?

    (Closed)
    17:10 - 17:30休憩
    17:30 - 17:55

    Your Identity Is Not Self-Sovereign

    (Closed)
    17:55 - 18:20

    Closing Keynote

    Nat Sakimura — Chairman, OpenID Foundation
    (Closed)
    18:20 - 18:25

    閉会の挨拶

    富士榮 尚寛 — 一般社団法人OpenIDファウンデーション・ジャパン 代表理事
    (Closed)


    見どころはなんといっても全体テーマにもなっているOpenID Connectの仕様がリリースされて10周年というところです。初期から仕様策定に関わってきた崎村さん、Mike Jones、OpenIDファウンデーションジャパンのエバンジェリストのnovさん、ritouさんがこれまでの歴史を振り返ります。
    また、内閣官房の成田次長からは2019年のダボス会議〜同年のG20首脳宣言にも組み込まれたDFFT(Data Free Flow with Trust)の流れを汲むTrusted Webに関する取り組みを解説いただきます。同様にTrusted Webの構成要素にも深く関連するDigital Identity Walletに関するEUの動向などもドイツから来日されるTorsten Lodderstedt博士により解説いただきます。
    他にもOpenIDファウンデーションジャパンのワーキンググループ活動の報告やIDチームの組成におけるディスカッションなど価値あるセッションが目白押しです。

    当日は私もMCとして全体の進行役を務めさせていただきますが、またイベントが終了次第振り返りの情報を皆さんにも提供させていただこうと思います。

    では、当日お会いできるのを楽しみにしています。











    OpenID Foundation Hybrid Workshopクイックレビュー

    $
    0
    0

    こんにちは、富士榮です。

    今日はOpenID Foundation Hybrid Workshopに参加しています。

    こちらのポストで紹介したものですね。


    私もスピーカーなのであまり余裕はありませんでしたが今後ポイントになりそうなところをピックアップしていきたいと思います。

    崎村さんのご挨拶から始まり、まずは各ワーキンググループからのUpdateが始まりました。全体アジェンダはこんな感じです。


    AuthZEN Working Group

    まずはAuthZEN WGです。


    認可(Auhorization)を管理するのって結構難しいですよね。というところから話は始まります。サイロ化されていたり、広く使われている認可コンポーネントが存在しないことなどが原因です。ABACとかXACMLとか。またSaaSやクラウドサービスが利用できる外部の認可サービスもありませんし。。

    ということでこのワーキンググループでは組織の内外、コンポーネント間、システム間を繋ぐ認可プロトコルとフォーマットについて、既存の標準や考え方との相互運用性を考慮しながら考えていく感じになるそうです。例えばポリシーベース言語やグラフベースなど、また標準的な認可のパターン(PAP、PDP、PEP、PIP)やNISTのABACのアーキテクチャなども考慮するということですね。

    仕様としては、

    • 標準的な認可パターン、ユースケース、コミュニケーションパターン、インテグレーションパターンなどを定義する
    • 認可リクエストと認可決定を行うためのAPIを定義する
    • 認可ポリシーと認可に利用するデータの間でコミュニケーションを行うAPIを定義する
    などを2024年のIdentiverseあたりを次のマイルストーンに決めていこうとしているようです。

    Shared Signals Working Group

    次はShare Signalsです。

    認証ばっかり守るんじゃなくて継続的にリスクを検知して緩和してかないとダメでしょ、というのがこの仕様のモチベーションの一つです。
    うーむ、まさに。

    IETFと密にやりとりしていて、OpenID FoundationとしてCAEP、RISC、Shared Signal Frameworkを、IETFではSET(Security Event Token/RFC8417)やベースとなるJWTなどのトークンの仕様を決めている、という役割分担です。

    昨年の12月にShared Signal FrameworkのImplementer's draft 2が承認されたのが大きなUpdateです。また、相互運用性に関するイベントが3月にロンドンで企画されていたり、明日のOpenID SummitやIIW、Identiverse、European Identity and Cloud Conferenceなどのイベントでのプレゼンテーションも予定されています。

    Apple、Okta、SGNL、MicrosoftなどさまざまなベンダーもShared Signalsのサポートを表明しています。また、CISA/NSAやUK Digital Identity and Attribute Trust Frameworkからのレポートも発表されています。

    途中でプライバシーに関する取り組みに関する質問がありましたが、すべてのやりとりはJWEなどで暗号化されている前提の元、シグナル自体のやり取りは事前の法的合意に基づきやりとりされるよ、という話がありました。この辺りのTrust Frameworkに関しては政府の役割も重要ですね。

    OpenID Foundation 2024 Strategic Initiatives

    次はGailからOpenID Foundation全体の戦略的な話です。


    こうやってみるとすごい量のアクティビティですね。

    FAPIやeKYC、OID4VCなどの仕様のUpdateやFinalizeはもちろん、認定プログラムで認定されたサービスやプロダクトがずいぶん増えました。そしてEU ARFやカリフォルニア州のDMVでのOID4VCの採用、GainやSIDI Hubなどのコミュニティーでの活動、NIST SP800-63-4へのコメントなど、とても忙しい一年になったということです。

    デジタルアイデンティティは通貨と同じように簡単にやり取りすることはできるのか?という問いかけもありました。通貨の世界ではクレジットカードなどが国による通貨の違いを吸収する仕組みが確立されていますが、デジタルアイデンティティにはそのような相互運用の仕組みがありません。この辺がこれから解かないといけない課題なんでしょう。

    また、前のセッションでTomさんから話のあったShared Signalsにも触れ、ゼロトラストなどの文脈ではもはや国家レベルのセキュリティのキモになってきている話である、と。


    次にDimaからOpenID Foundation Strategic Taskforceの2024のロードマップの紹介がされました。

    FAPIなどはすでにインターネットスケールでの展開を行う段階に位置づけられています。
    IDAやShared Signalはキャズムを超える段階にようやく到達しつつある感じですね。
    また、DCPやGAINなどは基礎を作る段階、SIDI HubやAuthZENなどは合意が取れてきている段階、今後はAI MonitoringやPostクォンタムなどはこれから、という感じですね。


    とここで私の番だったので、スキップ。
    わたしはOpenIDファウンデーションジャパンの最近の活動について話をしました。



    ここでWorking GroupのUpdateに戻ります。

    OpenID Connect Working Group

    Mike JonesからConnect Working GroupのUpdateです。


    メインのワーキンググループということもありスペックがてんこ盛りです。

    (自分の番が終わると力尽きてきた・・・)

    個人的にはOpenID for Verifiable Credentials関連のドラフトのUpdateが一番気になってますが、大きいのはOpenID Connect spec for ISO Public Available Specification(PAS)でしょうね。詳しくないのですがOpenID Connectの仕様をISOが認める公開仕様書とするということなんだと思います。

    eKYC & Identity Assurance Working Group


    次はMark HaineによるeKYC & Identity Assurance Working GroupのUpdateです。
    すでにImplementer's Draft 4まで来ているのでそろそろFinalizeですよね・・・

    ニュースとしては、IDAのClaimをIANAレジストリに登録ができたこととか、この前の10月ごろに大きくなりすぎたIDAスペックを4つに分割する意思決定をしたことが挙げられています。

    分割した理由の一つとしてはClaim SchemaはIDAだけではなくVerifiable Credentialsなどでも使えるよね、というあたりもありました。
    実はこのIDAの仕様って結構日本人が関与していて、日本での展開が期待されている分野の
    一つなんですよね。ということでそんな期待値も語られました。

    MORDNA Working Group

    次はMORDNAについてBjornから語られました。

    モバイルオペレーターが提供するIdentity Serviceのプロファイルを考えるワーキンググループですね。
    CIBAのMODRNA Authentication ProfileやAccount Porting、User Questioning APIのImplementer's draftについて触れられました。
    また、ETSIとのリエゾンの合意や、eKYC & IDA WorkingもKYCの文脈では関係するCAMARA Project(Linux Foundationのプロジェクト)とのリエゾンでのアクティビティなど外部との協業によるアウトリーチの拡大なども主要な活動だったようです。

    Digital Credentials Protocol Working Group

    次はTorstenによるDCPワーキンググループの話です。

    元々はDecentralized Identityの3パーティモデル(Issuer/Holder/Verifier)に関連するプロトコルを策定するために設立されたワーキンググループです。
    考え方はOpenID Connectと類似ですが、OAuthの上に構築されたプロトコルでCredentials formatには関係なく利用できるプロトコルを作っています。
    また、プロトコルとCredentials formatの組み合わせをプロファイルとして定義していたり、トランスポートもBLEチャネルを使うケースを想定した仕様の策定もしています。

    コンフォーマンステストに関しても開発が進んでいたり、OID4VP mdocプロファイルのテストが成功したり、Formal Security Analysisが完了したり、とかなり活発なワーキンググループです。

    Aries JavaScript Frameworkや.NET Wallet FrameworkがOpenID for VC/VPをサポートしてきているのも良い傾向ですね。(元々はDIDCommしかサポートしていなかった)

    その辺りも含めて相互運用性テスト(Plugfest)などの必要性などについても活発な議論が起きていました。

    FAPI Working Group

    次は崎村さんによるFAPI Working GroupのUpdateです。

    ざっくりいうと高いセキュリティレベルのプロファイルをOpenID Connect/OAuthの上に作りましょう、というのがFAPIですね。
    FAPI1.0関連の仕様はすでに発行されていますが、FAPI CIBA ProfileやFAPI2.0 Security ProfileなどImplementer's draftの状態の仕様も多数存在し、活発に議論が行われています。

    FAPI2.0のスペックのファイナライズを2024 Q1に、という野望も語られましたがなかなかチャレンジングなんでしょうねぇ。

    同じくFAPIのエコシステムに関してMike Leszczから語られました。





    ひろがってますね。素晴らしいことです。
    明日のOpenID SummitでもブラジルからNuBankの方が来日、FAPIの実装について話していただけることになっていますので、本当にグローバルで注目されているプロファイルなんだなぁ、と実感できます。

    続いてElcioからOpen Finance BrazilのFAPI採用の話もありました。


    最初はFAPI1.0から始めてどんどん進化させて行った感じですね。
    どんどん複雑化して行ったところを標準を使うことでシンプル化していった、と。この辺りの意思決定は非常に重要ですね。

    Certification Program


    次はJosephによる認定プログラムの話です。
    これまでもOpenID Connect、OpenID Connect logout、FAPI関係の認定テストが開発されてきましたが、今はOpenID for Verifiable PresentationやIdentity Assurance、Verifiable Credential Issuanceなどの開発が進んでおり、日本政府もサポートを表明しているのはとても素晴らしいことです。

    SIDI Hub


    最後はSIDI (Sustainable Interoperable Digital Identity)HubについてGailから紹介です。
    明日のOpenID Summitでも紹介されるので簡単に、ということです。

    日本政府を含む各国政府やOpenID Foundationを含む標準化団体が多数参加していますね。


    もうこのスライドに尽きると思います。

    今年も忙しくなりそうです、とのこと。



    ということでおしまいです。
    明日のOpenID Summitでお会いしましょう!

    OpenID Summit Tokyo 2024クィックレビュー

    $
    0
    0

    こんにちは、富士榮です。

    とうとうやってきました。OpenID Summit Tokyo 2024が開催されましたのでクィックレビューです。

    OpenID Summitは約4年に一度、東京で開催されてきたイベントで2011年、2015年、2020年、そして今年2024年は4回目となります。

    前回の開催はコロナ禍の直前ということで、本当にこの4年間は色々と世の中が変わってしまいましたが無事に開催に漕ぎ着けられて本当に良かったと思います。



    ということで早速。


    OIDF Strategic Outlook for 2024 and Briefing on the Sustainable Interoperable Digital Identity (SIDI) Summit in Paris

    まずはGailのキーノートからです。





    OpenIDテクノロジーの利用拡大、ワーキンググループを含む活動、ホワイトペーパーの発行、政府その他とのパートナーシップ、、本当に多くの活動が行われていることがわかります。

    また、昨日のWorkshopでも話がありましたが、OpenID Ecosystemを構成する上で、Open DataをAPIなどで接続していく必要がありますが、そのためには通貨におけるクレジットカードのように結節点となる仕組みが必要であると考えられます。


    また、セキュリティを考えるとShared Signalsでリスク情報を共有する仕組みなども重要になります。


    そして、SIDI Hubの話です。SIDI Hubの目標について「To define what we need to achieve global interoperability for digital identity.」と解説しています。この辺りは当然のことながら先ほどのOpen Dataの接続という文脈とも繋がります。



    上記のようにたくさんの国や団体が興味を持ち参加していいます。
    サーベイの結果では、参加者の92%がこの取り組みを継続するべきであると考えているという結果が出ている。そのくらいこの取り組みは重要なものである。

    実際に接続するためにはテクノロジだけではなくTrust Framework同士のマッピング(相互運用)まども必要になるということにも触れられました。

    OpenIDファウンデーション・ジャパン ワーキンググループ活動報告



    まずはKDDIの小岩井さんからKYCワーキンググループの紹介です。
    すでに5年目に突入ですね。延べ290人の方が参加、合計5つのホワイトペーパーを発行しています。4年前のOpenID Summit Tokyoで最初のホワイトペーパーの発表をしたのは懐かしいです。

    サブワーキンググループの紹介もありました。
    • 次世代KYCサブワーキンググループ
      • OpenID for Identity Assuranceの国内向けのプロファイルを策定中
    • 法人KYCサブワーキンググループ
      • 法人に対するKYCの現状整理
      • 今年中にレポートを発行する予定
    アクティブに活動していますね。


    続いて、エヴァンジェリストのnovから翻訳ワーキンググループの活動報告です。
    このワーキンググループの特徴は会員企業以外でも翻訳活動に参加できることです。昨年はNIST SP800-63-4の翻訳をしました。

    同じく続いて理事・エバンジェリストのkuraからデジタルアイデンティティ人材育成ワーキンググループの紹介です。今年度新しく設立されたワーキンググループですね。
    ID人材育成の悩みはみんなが持っているのでOpenIDファウンデーションジャパンの中でワーキンググループとして組成することで協調的な学習環境、実践と理論の結びつきのシェア、継続的な議論と建設的なフィードバックを得ることができるのではないか、と考えてワーキンググループ組成に至っています。
    現在18社42名の方々が参加し3つのサブワーキンググループを組成しています。
    • 技術サブワーキンググループ
    • ビジネスサブワーキンググループ
    • 翻訳サブワーキンググループ
    2024年夏〜秋には書籍の出版を目指します!

    Panel: Celebrating Ten Years of OpenID Connect

    次は10周年を迎えるOpenID Connectに関するパネルディスカッションです。
    Mike Jones、崎村さん、nov、ritouの4名でのセッションです。みんなOpenID Connectを創って育ててきた人たちですね。


    改めてOpenID Connectの設計思想が紹介されました。
    • Keep simple things simple
    • Make complex things possible
    今でも新しいプロファイルを作成する際など、しばしば思い出す本当に大切な原則です。

    もう一つの原則である「Extensible by Design」についても語られました。
    エコシステムを作る上で非常に重要ですね。フレームワークとプロファイルを分割するモジュラー型の思想で作られている仕組みなので例えばLogoutやIdentity Assuranceなど用途によって仕様を拡張していくことができている、というわけです。

    この10年で達成したこととして以下が紹介されました。
    • 最も使われているIdentityプロトコルとなった
    • 数千の相互運用のある実装が行われている
    • 認定プログラムが開発され活用されている
    • ISOのPAS認定を受けた

    Novの番です。
    元々のOpenID Connectに関するモチベーションはFacebook Connectだったそうです。ところが、結構動きがおかしいことが多かったとのこと。そしてある日突然FacebookがFacebbok ConnectをOAuth2.0ベースにする、という発表が行われそれまでの開発物が全て水の泡に・・・しかしOAuth2.0ベースになったことでシンプルかつ安定した実装になったので「これは素晴らしい」ということでRubyのライブラリを書いた、とのこと。その後OAuth2.0ベースでOpenID Connectが開発されるということで先のRubyのライブラリを拡張する形でOpenID Connectの開発に関わるようになって行った、、というエピソードが紹介されています。

    また最近感じていることとしてdo business on complex thingsになってきているところに技術をどう追いついていくか、というのが課題になってきているということです。例えば金融シナリオなどFAPIをサポートする必要が出てきている(つまり複雑なことをやる必要がでてきた)なかで、従来のライブラリでは動かなくなってきている、この辺りの状況つまり、ビジネス化をするためには複雑なことをしないといけなくなってきた、という状況を今後どのようにシンプルにしていくのか、というのが次のテーマだ、という話です。たしかに。

    次はritouです。
    当時はフィーチャーフォン全盛だったのでURL長の制限があったり、JavaScriptのサポートが不足していたりということでOpenID 2.0を日本のモバイルデバイスで動かそうとすると色々な課題があったとのこと。そこでバックチャネルでも動かせる仕組みが当時SAMLにもあった(Artifact Bindging)ので、これをOpenIDの世界にも持ち込んだらどうだろうか、という流れだったということです。

    そして最後は崎村さんです。
    案の定時間がないので詳細はこちらから(笑)

    昨晩、Youtube Liveでやっていた「25 years of OpenID」のセッションですね。

    初期のデザイン原則として以下を掲げたとのことです。
    No canonicalization
    ASCII Armoring
    JSON
    REST

    しかし、当時JSONの署名の仕組みすらなかったのでJSON Simple SignatureをIIWで発表したが当時MicrosoftにいたMike Jonesと合流してJWx(JWT、JWS)へ繋がった、

    その後、Dick Hardtらが提案してきたOAuth WRAP(当時のOAuthから署名を取り去ったもの)が出てきて、のちのOAuth2.0へ繋がった、という話もありました。

    JWT、JWS、OAuth2.0の流れがOpenID Connectに繋がった、ということですね。

    結果的に成功要因としてこのような教訓が紹介されていました。
    Developerのフィードバックに耳を傾ける
    解決できなかったことを解決する
    正規化しない
    シンプルなユーザケースのためのシンプルな実装
    セキュリティとプライバシー

    *****

    ここから午後のセッションです。

    テーマは「Cutting Edge OAuth/OIDC」ということで最新の仕様の一つであるOpenID for Verifiable Credentialsの関係、特にWalletのユースケースについてEUの事例を中心に話がありました。

    EU Digital Identity Wallets (eIDAS 2) - status and way forward

    まずはTorsten Lodderstedt博士によるEU Digital Walletの話です。


    EUDIW(EU Digital Identity Wallet)はユーザが自身をIdentifyした年齢を証明したり、医療証や免許証や学位などを保持・提示したり、契約に署名したり、支払いを行うために利用することができます。

    Coreコンセプトとして以下の要素が紹介されました。

    • Personal Identity Data(PID)
    • Electronic Attestation of Attributes(EAAs)
    • Qualified Electronic Attestation of Attributes(QEAA)
      • 特にEAAの中でもQTSP(Qualified Trust Service Provider)によって発行されたものを指します。


    こちらがEUDI Walletの全体像です。

    前述のEU Walletの役割を考えると、全てのWalletは認定されている必要があり、そのための認定の仕組みが重要となります。

    そして、eIDAS2で定められているリファレンスアーキテクチャ(ARF/Architecture Reference Framework)の中ではOpenID関連の標準技術を使うことが定められ絵います。

    プロトコル

    • OpenID for Verifiable Credentials
    • ISO 18013-5

    クレデンシャルフォーマット

    • SD-JWT
    • ISO mdoc

    ※PIDsは両方のフォーマットで発行される必要があります。


    また現在ディスカッション中のテーマとしてこれらの事項があるそうです。

    • ペイロードとしてW3C JWT VCsを使うかSD-JWT VCsを使うか
    • WalletのトラストとWalletのライフサイクル管理
    • RPやIssuerのトラスト
    • PIDと(Q)EAAsの間のIDマッチングやリンク
    • オンラインの仮名での認証


    特にVCのペイロードの話でSD-JWT-VCの話は熱い話題でした。ざっくりいうとSD-JWT-VCがシンプルでいいよね、って話でした。


    Waiting for the EUDI Wallet: Securing the transition from SAML 2.0 to OpenID Connect

    次はAmirさんの話です。Kim Camronアワードを受賞している人ですね。


    今回はイタリアのデジタルアイデンティティエコシステムについて話してもらいます。

    イタリアでは以下の2つのIDシステムを使っているそうです。

    • SPID:Public Digital IdentitySystem(デジタルID)
    • CIE id:based on the Electronic Identity Card(物理カード)

    そして最近Digital Identity System(SPID)をSAML2.0からOpenID Connectへ移行を始めたそうです。

    イタリアのプロファイルの特徴はOpenID Federation 1.0とOpenID Connect iGov Profileを使っているところかもしれません。


    特にOpenID Federationを利用している理由として、Dynamic、Scalabe、Transparentを挙げていました。

    また、OpenID ConnectのフローとしてはAuthorization Code Flow with PKCEを採用しているそうです。※SAMLからの移行ならImplicitの方が楽だったんじゃ?と思いましたが他国のことなので黙っておきます。

    また、EU DIWによるパラダイムの変化について語られましたが、やはりIdP/OPへのリダイレクトモデルからの脱却がポイントになっているようですね。


    すでにモバイル運転免許証をはじめとする大規模パイロット運用が始まっているんですね。


     

    イタリアの方ということもあり、次回のOAuth Security Workshopの紹介もありました。


    Insights into Open Wallet Foundation's initiatives

    次はJosephによるOpen Wallet Foundationの活動に関するセッションです。


    Linuxファウンデーションの参加ということもあり、OSSの優位性である早くて安いというところを全面に押し出しています。

    プロジェクトは多くのスポンサーによって支えられています。

    残念あがら日本とのアクティブなやり取りはなさそうですが、この機会に何か協業ができるといいですね。


    Open Wallet Foundationの中でも色々なプロジェクトが動いています。様々な言語でWalletの開発ができるのはとても良いことだと思います。

    *****
    次のブロックは「Auth/OIDCによるID/APIエコシステムの推進」というテーマです。

    Trusted Webの実現に向けて

    まずは内閣官房デジタル市場競争本部事務局次長の成田さんからTrusted Webの取り組みに関する講演です。


    いうまでもなくTrusted Webはデジタル空間におけるトラストを構築する取り組みです。
    ”一握りの巨大企業への過度な依存”でも”監視社会”でもなく、DFFT(Data Free Flow with Trust)を実現するための”第三の道”を模索する取り組みで、ホワイトペーパー(現在第3版)の発行やこれまでに25の事業者による実証実験などにも取り組んできています。

    一握りの巨大企業に過度に依存している状態である一番左側の状態と真ん中のすべてを検証する状態(ブロックチェーンの利活用など)のバランスをうまく取りながら検証と信頼のバランスをとる世界観を目指しています。


    2022年度に選定されたユースケースは個人属性情報(学習・就業・共助実績)、法人と行政庁との情報のやり取り、サプライチェーンにおける情報のやりとり、の3つにカテゴライズされます。全てのケースにおいてやり取りややり取りされるデータ、そしてやりとりする相手方を検証することで信頼性が高まり、確認コストの削減や不正の削減などに役立てることができるという話です。

    アーキテクチャとしてはオーバーレイアプローチを取ります。

    アーキテクチャを構成するコンポーネントとしては、
    • Verifiable Data:検証可能なデータ
    • Verifiable Message:検証可能なメッセージ交換)
    • Verifiable Identity:検証可能なアイデンティティ(コミュニティによって裏打ちされる)
    が存在します。
    そして、アーキテクチャと合わせて
    • Trusted Webという考え方自体に関するガバナンス
    • Trusted Webの考え方に準拠したトラストフレームワーク提供者に関するガバナンス
    • トラストフレームワークに従って構成・運営されるシステムに関するガバナンス
    の階層構造のガバナンスも重要となります。

    そして、実際に事業者がシステムとして実装する際に参照可能な実装ガイドラインもgithub上で公開されています。エンジニアの方々はぜひ見ていただき積極的に議論に参加してください。

    またこのような取り組みはグローバルな取り組みとして推進していく必要があるのでG7群馬高崎デジタル・技術大臣会合においてTrusted Webの取り組みの発表、EUやカナダとの国際連携なども進めようとしています。

    今後の活動として
    • ユースケースの創出
    • 企業・エンジニアによる取り組みのさらなる促進
    • 社会実装の加速化
    • 国際連携
    • 全体として感が主導している他の取り組み(ウラヌス・エコシステムなど)との連携
    が予定されています。

    OpenID Federation 1.0: The Trust Chain vs The x.509 Certificate Chain

    次はVladimirによるOpenID Federationの話ですね。
    ざっくりいうとFederationの仕組みの中でTrust Chainを辿っていく仕組みです。

    X.509におけるCertificate Chainを構成するものとして、
    • issuer, subject
    • not-before, not-after
    • contrains
    • public keys
    が挙げられます。

    一方でJWTでのTrust Chainを構成するものとして
    • iss, sub
    • iat, exp
    • JWK Set
    • trust mark
    • contrains
    • entity metadata
    • metadata policies
    が挙げられます。

    こちらが比較です。

    要するにX.509では公開鍵のアテストしかできない、ポリシーやメタデータで情報を伝えたりすることができないよね、という話です。

    2035年にはCertificate ChainからTrust Chainに諸々置き換わってこんな状態が実現するのかもしれません(笑)



    Passkeys and Identity Federation

    次はエバンジェリストのritouからパスキーとフェデレーションの話です。


    パスキーとID連携はどういう関係なのか?というのはよくある質問です。
    その辺りをときほぐしていきましょう。

    パスキーの課題として「アカウントリカバリ」「クロス・プラットフォーム同期」などが挙げられます。
    一方でID連携は、
    • 認証方式の一つ
    • メールアドレスの確認
    • 本人確認済み状態のやりとり
    などの用途で使われています。

    例えば単純に認証方式として比較するとパスキーの優位点はConditional Mediationとの組み合わせによるUX改善やプライバシーリスクの削減、再認証に使いやすいなどが挙げられます。

    そういう意味でID連携の弱点をUXなどの面でパスキーが補強する使い方もありますし、パスキーに対応していない環境をサポートするためのID連携を使う、という補完関係にあると言えます。

    OpenID Connectのacr/amrと組み合わせて認証コンテキストや方式を要求する場合にパスキーと組み合わせるということができます。

    同様に再認証のユースケースではauth_timeやmax_age、login_hint、id_token_hintを使って確実に再認証させることもできるようになります。

    RFC 9470のOAuth2.0 Step Up Authntication Challenge Protocolを使うと例えば決済APIへのアクセスをする際、JWTベースのアクセストークンの中身を保護対象リソース側で見てacr_valuesを指定して追加認証を求めることで安全性を高めるなどもできるようになります。

    *****
    次のトラックは「ビジネスへのOAuth/OIDC活用事例」というトラックです。
    ビジネスという意味では2つの側面があると思います。一つはOAuth/OIDCを使ったシステムを使ってどのようにビジネスを推進しているのか、いわゆる事例の話、そして二つ目はビジネスを進める上で必要となるアイデンティティ・エキスパートの育成・チームの組成というテーマです。

    まずはブラジルのNuBankの事例からです。

    The progress of Nubank and Open Finance in Brazil

    NuBankのOpen FinanceのGeneral ManagerのLucianaさんから事例の紹介です。

    90M以上の顧客を持つということなので巨大な銀行ですね。
    クレジットカード、投資、ローン、などを含め総合的にサービス展開をされているようです。

    インハウスでシステム開発を進めることで諸々の意思決定を含むコントロールができる状態を作り出しているんですね、素晴らしいです。


    Open FinanceがRegulatoryドリブンなのかマーケットドリブンなのか、ハイブリッドなのかという話は国によって異なりますがブラジルはRegulatoryドリブン、日本と一緒なんですね。日本でももっとOpen FinanceやAPiエコシステムが浸透するといいですね。

    ブラジルでは800もの事業者がOpen Financeエコシステムに参加しているとのこと、そうなるとOAuth2.0、OpenID Connect、FAPIが必要になります。そしてブラジルの標準を各仕様のリファインや範囲を限定することで最適化をしているそうです。



    Open FinanceはNuBankのミッションである「金融サービスを再発明することにより人々の暮らしをエンパワーするために複雑性と戦う」というテーマにマッチしているということです。
    そのために3つの柱を据えて取り組んでいるそうです。
    • より良い金融面の意思決定をしてもらえるように人々を支援する
    • 金融移管する生活体験を集中化、シンプル化する
    • 従来のOpen Financeが提供するものを超えていく

    非常に刺激的な事例でした。

    事業の成長にどのようにID技術/IDチームが貢献してきたか - SoftBank の取り組み

    次は小松さんからSoftBankでどのようにID技術やIDチームの存在が事業成長に繋がったのか、という話です。


    まずはSoftBankにおけるID技術がどのように導入されてきたのか、という歴史の話です。
    フィーチャーフォンからスマートフォンへのプラットフォームの移行、コンテンツビジネスから決済ビジネスへの拡大などこれまでの歴史について語られます。
    その中でスマートフォン向けのサービスが増えていくとAPIアクセス保護の必要性が出てくることでOAuthやOpenID Connectの技術が必要になってきた、ということですね。
    しかしながら、スマートフォンに切り替わるにつれて従来のガラケーの回線認証ではなくID/パスワードによる認証が必要となってきてしまい、問い合わせが殺到、スマートフォンでも使える回線認証を導入してきたということです。

    一方でリスト型攻撃などの攻撃も激化、キャリア決済の不正利用なども増えてきたことから認証ポリシーの定義と複数要素での認証機能の追加を行ってきました。

    その後、グループ企業とのシナジー創出が事業課題となってきた時代も出てきます。
    その際もID連携技術が活躍したんですね。



    全体を振り返ると「業界標準のOpenIDをありがたく適用させていただくだけで大体の課題を解決できた」とのコメントがありました。素晴らしい。

    次のテーマとしてのチーム組成の話はみなさんにとっても大きな課題なんではないでしょうか?
    要するに開発者からマネージャへ、という話ですがチームの設計は非常に難しいので小松さんの話はとても参考になります。

    事業への貢献、新規ビジネスの創出、そして教育や業界への貢献などバランスをとりながらチーム設計をされています。

    組織成長のための鍵としていろいろな観点で語られたのですがその中でも「ID技術のエンジニアである前に事業を支えるエンジニアになってほしい」という言葉は非常に重要だと思います。また、モチベーションとして「IDが好きかどうか」というのは重要な要素であることについても語られました。この辺りはOpenIDファウンデーションジャパンなどの場もうまく活用していっていただきたいと思います。

    パネルディスカッション: 組織内に「IDチーム」を確立・拡大するには?

    次は柴田さん、工藤さん、菊池さん、渡邊さんによるパネルディスカッションです。小松さんの話に続きチーム組成の話です。


    工藤さんの経歴。サンマイクロシステムズ時代はiPlanetとかSun Identity Managerとかをやっていらっしゃいました。とてもお世話になりました。しかしちょうど10年ごとに転職してるんですねw


    自身のキャリアとデジタルID分野との関係

    • 菊池さん:自分で選んだ。新規事業をやることになりブロックチェーンを使ってデジタル身分証を作る、というプロジェクトがありやりはじめた。地味だけど無くなることはなさそう、というのが選んだ理由。結果的にブロックチェーンは使わなかったが。
    • 渡邊さん:どちらかというと流れに任せてIDの世界へ。ECサイトの再構築などをやっているうちにIDのキャリアがある人に結果的になってしまった(笑)
    • 柴田さん:同じくどちらかというと流れに身を任せた。前職で事業開発をすることになり、その場に崎村さんがいたのでアイデンティティを使って事業開発をすることになりはや15年、という感じ。
    デジタルIDが自身のキャリアにどのように役立っているのか
    • 渡邊さん:ECサイトの統合などの求人に対して自身の経験が目に留まることがあり転職につながった
    • 柴田さん:コンサルの経験の中で技術の変遷とビジネスの変遷を幅広く知識を得ることができた。このような経験をしている人はID屋さん以外には少なく、希少性があがり転職につながった
    • 菊池さん:転職の直接のきっかけ自体がOpenIDファウンデーションジャパンのイベントだった。デジタル庁自体が省庁の中ではスタートアップ的な雰囲気があるので、専門性に加えてスタートアップとしての経験が役に立った。
    IDをやることの嬉しさ・辛さ
    • 渡邊さん:サービスがたくさんある中でそれぞれに関われることがIDならではだと思う。辛さは絶対に止められない、という辛さ。前職でECサイトのログインサイトを止めて社長にブチギレられた経験も・・。ただ経験として止めてはいけないシステムを運営したのはキャリアとしては非常に貴重
    • 柴田さん:ソリューションを提供する側だったので止めるとお客様にご迷惑をおかけする経験は辛いものがある。奥深いところが面白い。分野では法律などへ踏み込むことになるし、技術面でもインフラからアプリケーションのレイヤまで踏み込むことになるでいろいろな経験ができる
    IDチームの構成は
    • 渡邊さん:プロパーが10名くらい、あとは協力会社。みんながID経験があるわけではなく未経験の中から学んでいく。OpenIDなどの標準が整ってくることで学習効率があがり、育成していく中で何名かはID好きになってくる
    • 柴田さん:クライアントの事例だが、個別の担当者ベースでやっているイメージがある。ポリシー変更などがあった場合は根性で対応されていることも多く、効率化するためにアドバイスをすることはある
    • 菊池さん:デジタル庁はマトリクス型組織。アイデンティティユニットはPKIとアイデンティティをやっている。その中で法人、個人(マイナンバーカード)、などで担当が別れている。専門家がデジタル庁に集中しすぎ問題と言われるが普通にスカウトメールなど経由も多い。ただ最終面接する段階では誰かの知り合いだった、ということも多い
    採用戦略
    • 渡邊さん:採用時点ではID経験はとわないようにしている。ID業界は専門用語が多く裾野が広がりにくいと考えているので、なるべく専門用語を使わずに裾野が広いように見せていくことが大切だと思う。トークン、とか
    • 柴田さん:同じく採用時点でIDを知っていることはないので育成をしていくことになる。数年単位で人材育成をしていくことになる。教育プログラムを作ったりしていた
    IDの難しさとは
    • 柴田さん:仕様はオープンだが、その仕様がなぜそうなっているのかが書いていない。なぜそうなっているのかをわからないとインテグレーションができないこともあり、口伝などで情報伝達をしないとうまくいかない
    • 渡邊さん:OIDCがわかる本、を読んでもらうだけでは理解できないので自分で読んで解説するところまで必要。開発チームにも自分たちはOIDCに準拠しているのでちゃんと対応してほしい、と明確に伝える、伝え続けることが必要。標準を使うことで新しい人を呼び込むことにもつながる。独自で作るものを学ばされるよりも標準の方がメリットがある。ChatGPTに聴けるのも標準ならでは。
    • 菊池さん:デジタル庁の中では国際標準を使うのがデフォルト。社会課題を標準仕様で解く、ということが必要だが存在する実装では実現できないこともあるので、他のチームと連携しながら解いていく必要がある
    どうやってIDチームは交流していくのか
    • 柴田さん:OpenIDファウンデーションに入ってもらう、イベントに参加してもらう。OpenIDファウンデーションジャパンにも人材育成WGがあるので是非参加してください!
    • 渡邊さん:自社のIDチームに人を引き込むためにID業界としてこういうことがあると嬉しい、という観点だと「とっつきやすさ」だと思う
    • 菊池さん:技術の専門家とユースケースを知っている人が別々、ということが多い。その交流ができる場があると補完関係になって良い
    • 柴田さん:人材育成WGはビジネスサブWGと技術サブWGがあるので是非!
    最後に
    • 渡邊さん:IDチーム募集しています!とっつきやすいチームを目指しています!
    • 菊池さん;IDユニット、引き続き人を募集しています。官民連携を進めたいと思います
    • 柴田さん:絶賛採用中!
    • 工藤さん:弊社も!(w

    ということで最後は採用アピール大会になりましたがとても参考になりました。

    *****
    いよいよ最後のブロックです。
    テーマは「日本のID界を盛り上げていきましょう」です。

    Your Identity Is Not Self-Sovereign

    まずはJustinからです。昨年のEICで彼の話を聞いて、とても面白かったので呼んでもらいました。
    「Self」とはなに?というところから始まるわけですが、このセッションは動画で見ないとおお白くないので動画公開をお待ちいただこうと思います(笑)

    ヒントは牛丼ですねw

    「Soverign」とは?
    「Trust」とは?

    そういえばNortonさんは米国の皇帝だって名乗っていたことありますねw
    まさにSelf Sovereign。

    Source of truthはどこに視点を置くかによって変わる、という視点も非常に重要ですね。
    ※これはUSの標準をありがたく参考にしている日本人は本当に考えないとダメだとおもいますよ・・・

    そして最後は「Identity」
    これはEntity - Identityモデルの話で自観と他観の話なわけですが、どうやって自分のアイデンティティを表明するのか、という話。
    そう考えるとアイデンティティは受け取った側がどう受け取るのかによるわけですよね。これが他観の話。
    つまり、自分がどういうアイデンティティを表明したかったとしても相手に受け取られた段階で自分では何のコントロールもできない、ということ。


    これが自己主権型アイデンティティだと思っている人はしっかり考え直しましょう。

    Closing Keynote

    最後は崎村さんによるクロージングです。
    テーマは「分散の誤謬」です。

    最初にKim Cameron Awardのアナウンスがありました。OpenID Foundationがスポンサーとなっています。

    本題です。

    Web1.0、Web2.0の流れがあり、OAuthやOpenID ConnectはまさにWeb2.0の申し子なわけです。
    OpenIDの基本コンセプトに立ち返ると自分のブログのアドレスを使ってサイトにログインしていく、というようなまさに自己主権型の仕組みでした。
    しかしながらOpenID URLを使うと全てのサイトに対して同じ識別子を提供することになってしまうのでPairwise IDが作れないので、認証提供サイトであるOpenID Providerというエンティティが登場するわけです。これがOpenID Authentication 2.0。
    しかし必然的に自分のアドレスではなくOPのアドレスを使うことになり、自己主権を犠牲にしているわけです。これは現在EUで起きているEUDIWの議論とも共通します。

    XRIとかSXIPなどからOpenID Authentication 2.0への流れを見ていくとこの段階でGAFAは決してプレイヤーだったわけではありません。これを見ていくとWeb2.0が巨大新興企業が率いているという話は間違っていることがわかるわけです。むしろ当時のGAFAは巨人IBMを倒すことで民衆に熱狂を持って受け入れられた革命児だったはずです。

    しかし、その流れで生まれてきたWeb2.0は極限まで分散されているにもかかわらず、なぜ巨大企業に支配されているのか?

    Googleに売上の推移をみるとわかるとおり、IT産業は収穫低減のモデルなので富の集約は必然っていうことですね。

    ではweb3はどうなのか?
    「分散」という言葉を語るときに対象を明確化する必要があります。
    そして集中と分散はバイナリではなくグラデーションがあるわけです。

    そこに分散台帳を当てはめてみると、実は分散台帳は主体が一つの台帳を使うので完全集中だということがわかります。つまり完全集中しているシステムに「分散台帳」という名前をつけるマーケティングセンスは天才的だといえます。

    分散型IDやWalletの世界に当てはめるとどうなるか。
    Wallet=IdPという世界観なのでIdPが個人のWalletに分散するという側面で物事が語られるのではないか?ただし個人のデータがWalletに集中することをみると分散ではないと思われます。
     Web2.0の文脈では世界中にIdPが分散していることもありある意味分散型。しかしWalletモデルで見るとIdP提供者よりもWallet提供者の数はずっと少ない状態であり、IdPモデルと比較すると集中していると言えるわけです。

    つまり、web3の世界におけるWalletモデルもいずれWalletプロバイダへの集中やプラットフォームベンダへの集約が起きるのは必然となるわけです。

    こうなると政策介入しかなくなるわけですが、一部の国がやっているように独立したアプリストアを許可するようにしたとしても本当に使われるのか。そしてさまざまなWalletを許可した場合、本当にそのWalletは信頼できるのか?実際にWalletからの情報流出の事故は発生しているわけです。

    こういう形でみんなが分散しようとしても結果として集中が発生してしまう「分散の誤謬」というものが発生しているわけですね。


    ということでご参加いただいた皆さん、お疲れ様でした。
    動画は追って公開する調整が行われると思いますので残念ながら参加できなかった方も、もう一度見たい方も楽しみにしておいてください。



    Viewing all 775 articles
    Browse latest View live