こんにちは、ritouです。
Self-Issued OPについて、去年からなんかごちゃごちゃ言ってきましたが、やはり動くものが見れないと話にならないと思っていたので、年越しあたりでデモ用のアプリを作ってました。
Self-Issued OPとはなんなのかについての復習はこちらから。
OpenID ConnectのSelf-Issued OPの話 - r-weblife
Self-Issued OPによるAuthorizationの流れ
いつもながらRP(Client)がAuthorization RequestをOPに送り、OPがAuthorization Responseを返すわけですが、その際のOP側の一連の流れは以下のとおりです。
- 1. openid://というカスタムURIスキームで起動
- 2. Authorization Requestの処理
- 3. Consent Page(アクセスを許可するか確認する画面)の表示
- 4. ID Tokenの生成
- 5. Authorization Responseの生成
実装の話は長いので、先に画面の流れを紹介します。
スクリーンショット
アプリはこちらです。
https://play.google.com/store/apps/details?id=info.openidconnect.selfissued
立ち上げるだけじゃAuthorization Requestがなくて何も起こらないので、仕様とデモ用RPのURLを乗せてあります。ちなみにデモRPのコード(Perl)はgithubにありますがSelf-Issued用のものはまだpushしてません。
RPはWebアプリケーションです。
http://demo-client.openidconnect.info/selfissued/authorize
モバイルからの見た目がよくないのはごめんなさいごめんなさい。
Authorization Requestはopenid://で始まってるのでPCで見ながらクリックしても何も起こらないと思います。
クリックすると、アプリが立ち上がり、同意画面が表示されます。いわゆるConsent Pageです。
今回はscope=”openid”のみサポートしているので、(client_idに指定されたURLが)端末識別子の役割を果たす値を要求していますよ、許可しますか?って感じにしました。
キャンセルしたかったらアプリ落としてください。。。
Allowを押して進めると、OPはAuthorization Responseを作成します。一番最初のときだけ時間かかるかもしれません。
http://で始まるURLについてのIntentをブラウザで受け取ることで、RPはAuthorization Responseを取得できます。
RPはフラグメントからStateパラメータとID Tokenの値を取得して、値を検証します。
デモRPではずらずらと検証した結果を表示します。
何を検証しているかは、仕様のURLを見てください。
では、どう実装したのかを説明します。
実装
2. Authorization Requestの処理
クエリパラメータを検証します。
- response_type : id_tokenのみ許容
- client_id : Self-Issued OPの場合、client_idには戻り先のURIが指定されますが、前のエントリに書いたとおり、戻り先としてカスタムURIスキームを許容するのは若干の不安があるため、http(s)://なURLのみ許容としました。
- scope : プロフィール情報設定させたりとかがめんどくさかったので、"openid"のみ許容としました。
- state : チェックなし
- nonce : チェックなし
RPの情報(ポリシー、TOS、ロゴなど)を指定することでユーザーに表示されるというregistrationパラメータも今回はサポートしていません。
エラーがあった場合はエラー画面を表示させています。
Self-Issued OPの場合、事前の静的な登録などを行わずに進んでいくので、オープンリダイレクタのリスクもある気がします。
なので、しれっと戻すのはよくないかもと思ってます。
3. Consent Pageの表示
scope=openidのみをサポートしているので端末の識別子を要求してますよぐらいにしています。
4. ID Tokenの生成
RSAの鍵ペアを生成します。このときちょっとだけ動作がモッサリします。
Priivate KeyはID Tokenの署名生成に利用します。
Public KeyのExponentとModulusの値はID Token内にsub_jwkパラメータとして含まれ、それぞれの値を連結してハッシュ値をBase64 URLエンコードしたものがsubパラメータとなります。
client_id毎にPrivate/Public Exponent, Modulusの値を保存することで、RP単位で異なる識別子が返されるPPIDを実現しています。この保存については、KeyStoreなどを使うべきなのでしょうけども、デモってことでSharedPreferencesを使っています。
5. Authorization Responseの生成
ID TokenやStateパラメータを使ってAuthorization Responseを作成して、Intentを送ったらOPの処理は完了です。
動作させてみて改めての懸念点
一番アレだなーって思ったのはAuthorization Responseでブラウザに戻るときに別のタブで開かれちゃったときですかね。
ブラウザの設定でなんとかできればいいのかもですが・・・あとは前に書いたエントリのとおりです。
今後鍵の保管あたりをまじめにやったりとか、プロフィール情報を持って他のscopeをサポートするとか、registrationパラメータをサポートしてユーザーにClient情報を表示させるとか、カスタムURIスキームなClientをサポートするとかをやろうと思います。
そういえば来週、JICS(Japan Identity & Cloud Summit 2014)が開催されます。
https://jics.nii.ac.jp/
私もちょっとだけ何かします!
寒いのでこのへんで。ではまた!