Webの世界は基本的にステートレスで状態を持つことができない。とはいえログイン中などの状態を持ちたい場合、セッションを作って状態を持つ。
具体的にはログイン後にランダムな値(セッションID)を作って、KVSにセッションIDをもとにユーザーIDを引けるように保存しておいてCookieでセッションIDを送る。
次回アクセス時にはセッションIDのクッキーを送ってもらって「さっきのXXXさんね。」ということをやっている。
複数のサイト間でログイン状態を共有したい場合はクッキーのdomainを*.hoge.netのようにワイルドカードにしてセッションIDとKVSを共有することもできる。良い実装かはしらんけど。
認証(Authentication): あなたは誰ですか?を特定するもの
認可(Authorization): この権限がありますか?
大体認可と認証はセットになっていることが多い。その人がどんな権限(管理者?CS?ユーザー?)があるのかを調べるためには誰であるかを調べる必要があるからだ。
おそらくほとんどの人が興味を持っているのはOAuthログイン(?)のようなシングルサインオンだと思う。
まずはOAuthの認可コードフローについておさらいをしてみる。
これが一般的な認可コードフロー。基本的にはこれを使うことになると思う。
アクセストークンをもって認証とするとまずいケースがあるので、OAuth認証と呼ばれるものを作る場合は気をつける必要がある。
たとえば、モバイルアプリからAppBackendにアクセストークンを送っている 悪いケース を考える。
これは何をしたいかと言うと、「アプリから送られてきたアクセストークンの持ち主はAさんだからAppBackendはAさんとして振る舞えば良し」といった実装をしている。アクセストークンを認証に使っている悪い実装例だ。
たとえば悪意のある占いサービスをAさんに使わせることができた場合、占いサービス用に発行されたAさんのアクセストークンをAppBackendに渡せばAさんになりすますことができる。
これを防ぐためにはApp側でそのアクセストークンがどのアプリケーションに対して発行されたアクセストークンなのかを検証する必要がある。もしくはユーザーの手の届くところにアクセストークンを置かないかだ。
OpenID ConnectはOAuthを拡張したような仕様で、IDトークンと呼ばれるものをアクセストークンと一緒に返している。IDトークンには「誰が」「誰に対して」「誰のトークン」なのかといった情報が含まれる。
これをちゃんと検証できれば認証に使うことができるようになる。ID Tokenが一緒に返るくらいで認証フローはほぼ同じなので省略する。
シングルサインオンは結構市民権を得た言葉だとおもうが、シングルログアウトに関してはあまり知られてないとおもう。
どういったものかというと、サイトAでログアウトすればすべてのサイトBやCでログアウトされていてほしいといったケースだ。YoutubeとGoogleの関係というとイメージが付きやすいかもしれない。
ログアウトを実現するためにはセッションの管理を考えなければならない。DraftだがOpenID Connectでセッション管理を行うための仕様が存在するのでこれを使うと良い。
OpenID Connect Session Management 1.0 - draft 28
ざっくり仕組みを説明すると、見えないiframeで認可サーバーのページを開いてそこでログイン状態がかわれば親フレームにイベントを送って認可フローを踏ませるといった動きをする。この仕様の面白いところはiframeの認可サーバーのページでログイン状態の変化を確認するところで、ログイン状態を確認するときにCookieやlocalstorageの値を確認するのでネットワーク間のトラフィックが発生しないというところだ。都度確認してると複数タブひらいたりするだけですごいトラフィックになってしまうので、その問題をうまく解決してほぼリアルタイムなログイン状態の変化の検知をやっている。
せっかくGoogleっぽいことができたのでGoogleっぽいアカウントの切り替えについても考えてみる。
実はOpenID Connectの認可リクエストのpromptパラメータにはselect_accountというアカウント切り替えをリクエストするための値が用意されている。
では、これをどうやって実現するかというとcookieに複数のセッションIDを持つことで実装するといった方法がある。
実装例についてはHow to implement account switchingを読むと良い。
アカウントの切り替えを他のサービスに伝える方法も簡単で、さっきのOpenID Connect Session Managementを使っていればアカウントの変更を検知して認可フローをもう一度踏ませる動きをするので勝手にアカウントの切り替えを伝えることができる。