Skip to content

Instantly share code, notes, and snippets.

@orekyuu
Last active February 29, 2020 09:33
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orekyuu/4f9b2da96463f80d6f6ea801e5726b8d to your computer and use it in GitHub Desktop.
Save orekyuu/4f9b2da96463f80d6f6ea801e5726b8d to your computer and use it in GitHub Desktop.
@startuml
UA -> App: /login
UA <-- App: Redirect
UA -> 認可サーバー: /auhorize
UA <- 認可サーバー: ログインページ表示
UA -> 認可サーバー: ログイン
UA <- 認可サーバー: 認可(AppにXXXを許可しますか?
UA -> 認可サーバー: OK
UA <-- 認可サーバー: Redierct
UA -> App: /callback?code=xxx&state=xxx
App -> 認可サーバー: /token に認可コードを渡す
App <- 認可サーバー: AccessToken
App -> App: セッションにAccessToken保存
UA <- App: ログイン完了
@enduml
@startuml
ユーザー -> MobileApp: ログイン画面表示
MobileApp -> AppBackend: WebViewなどでログイン画面表示
ユーザー <-- AppBackend: redirect
ユーザー -> 認可サーバー: /authorize
ユーザー <-- 認可サーバー: redirect
ユーザー -> AppBackend: /callback?code=xxx&state=xxx
AppBackend -> 認可サーバー: /token
AppBackend <- 認可サーバー: アクセストークン
ユーザー <-- AppBackend: カスタムスキームにトークンをつけてリダイレクト
ユーザー -> MobileApp: ログイン後画面
MobileApp -> MobileApp: アクセストークン、リフレッシュトークン保存
group ログイン後
MobileApp -> AppBackend: アクセストークンをつけてAPIを叩く
AppBackend -> 認可サーバー: もらったアクセストークンをつけて/user-infoを叩く
AppBackend <- 認可サーバー: ユーザー情報
MobileApp <- AppBackend: アクセストークンに紐づくユーザーとしてのレスポンス
end
@enduml

おさらい

セッションの仕組み

Webの世界は基本的にステートレスで状態を持つことができない。とはいえログイン中などの状態を持ちたい場合、セッションを作って状態を持つ。
具体的にはログイン後にランダムな値(セッションID)を作って、KVSにセッションIDをもとにユーザーIDを引けるように保存しておいてCookieでセッションIDを送る。
次回アクセス時にはセッションIDのクッキーを送ってもらって「さっきのXXXさんね。」ということをやっている。

複数のサイト間でログイン状態を共有したい場合はクッキーのdomainを*.hoge.netのようにワイルドカードにしてセッションIDとKVSを共有することもできる。良い実装かはしらんけど。

認証と認可

認証(Authentication): あなたは誰ですか?を特定するもの
認可(Authorization): この権限がありますか?

大体認可と認証はセットになっていることが多い。その人がどんな権限(管理者?CS?ユーザー?)があるのかを調べるためには誰であるかを調べる必要があるからだ。

シングルサインオン

おそらくほとんどの人が興味を持っているのはOAuthログイン(?)のようなシングルサインオンだと思う。
まずはOAuthの認可コードフローについておさらいをしてみる。

sequence dialog

これが一般的な認可コードフロー。基本的にはこれを使うことになると思う。

アクセストークンをもって認証とするとまずいケースがあるので、OAuth認証と呼ばれるものを作る場合は気をつける必要がある。
たとえば、モバイルアプリからAppBackendにアクセストークンを送っている 悪いケース を考える。

sequence dialog

これは何をしたいかと言うと、「アプリから送られてきたアクセストークンの持ち主はAさんだからAppBackendはAさんとして振る舞えば良し」といった実装をしている。アクセストークンを認証に使っている悪い実装例だ。
たとえば悪意のある占いサービスをAさんに使わせることができた場合、占いサービス用に発行されたAさんのアクセストークンをAppBackendに渡せばAさんになりすますことができる。

これを防ぐためにはApp側でそのアクセストークンがどのアプリケーションに対して発行されたアクセストークンなのかを検証する必要がある。もしくはユーザーの手の届くところにアクセストークンを置かないかだ。

OpenID Connect

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を使っていればアカウントの変更を検知して認可フローをもう一度踏ませる動きをするので勝手にアカウントの切り替えを伝えることができる。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment