こんにちは、kura(倉林 雅)です。
この記事はDigital Identity技術勉強会 #iddance Advent Calendar 2022の23日目の記事です。
前回、Cookie・OAuth 2.0・OpenID Connectの目的別プロトコル選定において目的別での適切なプロトコルのお話をいたしました。
今回はその話に関連したSPAにおける認可についてご紹介します。
なぜSaaSにおけるSPAではOpenID ConnectとID Tokenを用いるのか?
はじめに、プロトコル選定や設計・実装において多くの開発者の誤解に繋がっているのではないかと思われる仕様について触れたいと思います。
前回のお話では、新規にSPAを提供する場合はOAuth 2.0を選択するで解説したようにSPAでアプリケーションを提供する際には、OAuth 2.0による認可を行いフロントエンドのJavaScriptにAccess Tokenを発行する方法がよいと考えています。
このユースケースの要件は以下にあります。
- SPAのJavaScriptがユーザーリソースにアクセス(APIアクセス)するためにAccess Tokenを取得できること
- サーバーサイドでID管理し他社IdPとのID連携をするケースと異なり、このケースではID連携をする必要はないため、ID Tokenの検証やユーザー識別子の紐付けなどはSPA上では不要である
純粋にSPAではAPIへの「認可」ができれば機能を提供できるため、OAuth 2.0を用いればよいということになります。
ですが、SaaSによる認証・認可機能ではOAuth 2.0とAccess Tokenではなく、OpenID ConnectとID Token(JSON Web Token, JWT)で実現されている事例が見受けられます。
-
ID トークンの使用 -AWS Cognito-
- リソースサーバーにID Tokenでアクセスできる旨が記載されている
-
Control access to a REST API using Amazon Cognito user pools as authorizer
- API GatewayでAuthorizerを設定することでID Tokenでアクセスも可能である旨が記載されている
AWS Cognitoの仕様をみると、User Poolで管理しているユーザーの制御において、アプリケーション上でのユーザー認証やリソースアクセスのためにOpenID Connectのインターフェースで発行したID Tokenを利用できるとなっているようです。
また、ドキュメントに従ってSPAを実装しようとするとAmplifyのSDKの利用も提案されており、Amplify内部でもID Tokenが用いられたリソースアクセスになっているようです。
これは推測ですが、User Poolのユーザー管理に関連する制御機能は「ユーザー認証を目的とするID Token」でも柔軟にできるようにしようという意図でこのような仕様となっているのだと想像しています。
基本的にID TokenはOpenID Connectを提供するOpenID Provider(OP)で行ったユーザー認証の結果をID連携先であるRelying Party(RP)へ伝える仕組みであり、ユーザーリソースのアクセスに用いる想定はされていません。
あくまでCognitoの独自仕様であるということを理解しておく必要があります。
※ この仕様に至った経緯がわかる記事や実際に仕様策定に携わった方で背景をご存知の方がいましたら、ぜひ共有していただけると幸いです。
Cognitoは認可エンドポイントのscope
パラメーターでopenid
を指定することでOpenID ConnectのインターフェースでID Tokenを取得することができるわけですが、openid
を指定しなければID Tokenを発行せずに、純粋なOAuth 2.0として利用することも可能です。
その場合には、新規にSPAを提供する場合はOAuth 2.0を選択するの通りの実装ができると考えています。
「JWT=リソースアクセスのためのフォーマット」ということではない
本筋からは少し外れますが、JWTフォーマットのAccess Tokenについても触れておきます。
CognitoやAuth0ではAccess TokenもID Tokenと同じJWTのフォーマットになっています。
これはCognitoやAuth0を利用するアプリケーションが提供するAPIの内部でAccess Tokenの検証をしやすいように標準フォーマットを利用しているという認識です。
これはリソースアクセスに使用するAccess TokenがJWTフォーマットであるということであり、JWTフォーマットであるID Tokenもリソースアクセスに使用することを想定している、ということではないので注意しましょう。
ID TokenとAccess Tokenの用途については以下のAuth0の記事で整理されているためご参照ください。
SPAの認可ではメモリ上でAccess Tokenを管理することが重要
混乱を招いていると思われる仕様について整理できたので、改めてここからがSPAにおける認可のお話です。
現在、IETFのoauth WGでブラウザーベースのOAuth 2.0の実装についてベストプラクティスの策定が続いています。
Access Tokenをローカルストレージに保存するとXSSによるAccess Token漏えいの脆弱性のリスクが伴います。常にXSS対策が継続されていればよいですが、可能であればリスクを回避すべきです。
このdraftで定義されているリスク回避方法は、ローカルストレージに保存せず、Service Workerを用いてメモリ上でAccess Tokenを管理する方法です。
以下は、SPAのOAuth 2.0をService Workerで実装したフローのシーケンス図になります。
アプリケーションのメインスレッドから認可コードやAccess Tokenの管理を分離することで、XSSのリスクを回避するということになります。
Auth0やmercariにおいてはWeb Workerを利用した実装が提案、実現されているようです。各社、ユーザー認証の方式には違いがあるようですが、Access Tokenの管理をメインスレッドから分離するという方式については共通していそうです。
基本的なOAuth 2.0 / OpenID Connectのシーケンスについては以下にまとめているので、SPAのシーケンスとの違いを確認してみてください。
最後に
今回は、OpenID ConnectとID TokenによるリソースアクセスやJWTフォーマットのAccess Tokenなどで起こりがちな誤解をご説明し、改めてSPAにおける理想的な認可についてご紹介しました。
SPAの認可については方針が確認できたので、次の機会にはユーザー認証も含めてお話ができたらと思います。
最後まで読んでいただきありがとうございました。よいお年を!