SPAの認証アーキテクチャパターン
従来のMPAでの認証は、無思考にCookieとSessionを使っておけば良かった
だが、SPAやマイクロサービスというアーキテクチャが台頭し始めると、認証の選択肢が増えてきた
要件によって採用すべき認証方法が変わると思うので、しっかり特徴とメリデメをまとめておきたい
注意:トークン・セッションIDの保存先に関する議論は別でやって良い
セキュリティ観点からどこに保存するかみたいなことはあるが、今回とは切り離して考えれる
そもそも認証パターンには大きく2つある
セッション方式
セッション情報はサーバ側でストレージに保存
セッション ID をクライアント側のクッキーなどで保持
アプリケーションサーバを水平にスケールさせるには、セッションストアを共有する、スティッキーセッションを使うと言った方法が必要になる
トークン方式
認証後にトークンが発行され、以降のクライアントとリソースサーバのやりとりはそのトークンが使われる
トークンにはユーザー識別情報が埋め込まれている
各サーバーはクライアントから提供されたトークンの検証を行う
セッション情報はサーバー側では持たない
認証アーキテクチャパターン
①:分散セッションパターン
https://scrapbox.io/files/6373bd2ab87e47001d8ed171.png
セッション管理の仕組みを全マイクロサービスに入れるやり方
シンプル is the Best!!
個人的感想
実装は意外と面倒臭そうな印象を受ける
あとSession IDどこに保存するつもりなんやろ。ServiceXのCookieに保存するの難しくね?
普通にHeaderとかで渡せばいいんか。
②:SSOパターン
https://please-sleep.cou929.nu/images/microservices-auth-design-03.png
外部SSOサービス or 自前SSOサービスを使ってセッション管理を任せるやり方
前提として、従来的なセッション ID 文字列のような、利用者がその内容を読まないトークンのことを Opaque トークン と呼ぶらしい
1. Auth認証に成功すると Opaque トークンを発行
2. サービスはクライアントから Opaque トークンを受け取る
3. サービスは認証サービスに対して検証リクエストをなげ、ユーザー情報を取得する
「Firebase Authenticate」などを使う方法はこれにあたる
個人的感想
外部サービスを使うと簡単で楽だなぁってイメージ
あまりスケールとかセキュリティとか考えてないならこの方法が一番コスパ良いと思う
あと、Opaqueトークンとも限らない、JWTトークンの可能性もある
まあ、今回の場合は、Authサービスに逐一問い合わせてるのでJWTである意味は無いが
JWTの場合、ユーザー情報をトークン内に含めれるので問い合わせる意味ない
参考
③:JWTパターン
https://scrapbox.io/files/6373bff532089c002370a494.png?type=thumbnail
AuthサービスにJWTトークンを発行させて、そのJWTトークンの改ざんチェックするだけでログイン検証してしまうやり方
サービスXは、クライアントのログイン状態チェックに関して、わざわざAuthサービスに問い合わせない
事前に持ってた公開鍵を使ってJWTの有効性チェックを行い、有効ならそのトークン内にあるユーザー情報を得る
Authサービスへのトラフィックが発生しないので、マイクロサービスをスケールさせやすい
デフォルトだとログアウトできないっていう特徴がある
ログアウト処理作ろうと思うと、どうしてもAuth Serviceと繋がることになる
それによってステートレスの効果を失う
個人的感想
自前で割と簡単に実装できる点はお得感ある
本気でやるならリフレッシュトークンとか考えなあかんけど(セキュリティ対策)
まあ、適当なアプリならアクセストークンだけ用意しておけば良いんでね?
あとモバイル + Webアプリを作る場合はセッション方式よりも楽に作れると思う
④:API Gateway + トークン
https://scrapbox.io/files/6373c04c8de490001f1d6dbb.png
API Gatewayで認証を受け持ち、内部ではJWTトークン、外部ではOpaqueトークン(ex: セッションID)を利用するやり方
Chatwork、メルカリなどはこの方法(もしくは似たような方法)を採用してると思われる
メルカリなんかは、Opaqueトークンをもとに1リクエストに対して1JWTを毎回作ってるらしい
これだとログアウト処理も簡単に作れる
個人的感想
比較的大規模なマイクロサービスシステムになってくると、この方法を採用せざるおえないんだろうと見てる
ただ、小さいシステムとかでここまでする必要はないかなと
SSOパターン、JWTパターンで十分かと思う
hr.icon
分散セッションパターン
https://scrapbox.io/files/6373bd2ab87e47001d8ed171.png
各マイクロサービスからセッションストレージにアクセスできるようにする
分散ストレージの可用性 / スケーリングに注意が必要
マイクロサービスの原則 (各システムが単一責任でセルフサービスであること) の観点では微妙かもしれない
一方である意味シンプルで従来的なシステムから自然に拡張できるメリットがあると思う
共有セッションストアを用意して、認証情報を保管するのねonigiri.w2.icon
各サービスからセッション情報を逐一参照・更新させるって感じか
それぞれのドメイン用Cookieにセッションを置く感じになるのかな
整合性ちゃんと取れるんかな?更新が面倒くさそうだ
SSOパターン
https://please-sleep.cou929.nu/images/microservices-auth-design-03.png
前提として、従来的なセッション ID 文字列のような、利用者がその内容を読まないトークンのことを Opaque トークン と呼ぶらしい。
SSO パターンは認証サービスが Opaque トークンを使うパターン。
サービス全体の認証を行うマイクロサービスを作る
認証に成功すると Opaque トークンを発行
サービスはクライアントから Opaque トークンを受け取り、認証サービスに対して検証リクエストをなげ、ユーザー情報を取得する
Opaque トークンを使うため認証サービスへの検証リクエストが発生してしまう
図では処理はシンプルだが、認証サービスへの負荷集中 / SPOF となるデメリットがある
サービスが増えるほど認証サービスの負荷が増加
セッションストアをスケールさせるのは大変そう
文字列が意味を持ってないトークンのことをOpaqueトークンて言うのかonigiri.w2.icon
IDaaSでよくありそうな形式やなonigiri.w2.icon
認証サービスがトークンを発行して、そのトークンをサーバーが検証すると。
検証する際に認証サービスに問い合わせると。
なるほどね
これだとログアウトとかは管理できそうな感じあるな
Opaqueトークンの無効リストを管理したりすればよさそう
JWTパターン
https://scrapbox.io/files/6373bff532089c002370a494.png
SSO パターンと比べ、JWT を使うことでスケール・SPOF の問題を緩和する方式。
まあ一番実装が楽な仕組みではあるよなonigiri.w2.icon
JWTにユーザーIDとか入れておけば簡単に認証が作れる
あとは、権限情報などを入れておけば認可できる
セキュリティ面が少し弱いって言うのと、ログアウトを実装するのだるいって感じやな
ログアウトとかそもそも必要なんか?てのはあるけど
JWT + API Gatewayパターン
https://scrapbox.io/files/6373c04c8de490001f1d6dbb.png
JWT 利用パターンに加え、API Gateway でのトークン変換を行う方式。
ややこしいなonigiri.w2.icon
API Gatewayが性能のボトルネックになってきそうな気はする。スケールを上手いことできるのか心配
その上で管理が複雑にもなるという....
web-2ではCookie内のセッションIDに紐付けられたユーザーのセッション情報をデータベースに保持しており、セッションIDをアクセストークンに変換してAPIを呼び出す処理を行っていました。
なるほど、俗に言うOpaqueトークンを使って、セッション管理してたんやな多分onigiri.w2.icon
当初はセキュリティの観点からアクセストークンを直接ブラウザ上に保存せず、アクセストークンを含むセッション情報をJWTの形式でHttpOnly Cookieに保持させようと考えていました。
API Gatewayがあることで、恐らく1つのCookieでセッション管理ができると思われるonigiri.w2.icon
モバイルってCookie使えるのか??
しかし、十分に有効期限の短いアクセストークンを利用することで、アクセストークンをブラウザのLocalStorage上に保持し、アクセストークンを再発行する場合に限りHttpOnly Cookieとして保存されたセッションIDを用いるよう変更しました。
ChatWorkの事例と同じ考え方うやなonigiri.w2.icon
リフレッシュトークン的なものをCookieに入れてるんやな
モバイルでリフレッシュトークン持たせる場合ってどうするんやろ?
モバイルはあんま気にしないんかな。Cookieとか無理やり使うのだるいよな
この方法では、内部、または外部のSSOサービスを利用します。
サービスごとにSSOサービスとやり取りし、そのユーザーがログインしているのか、また、どのユーザーなのかといった情報を受け取って認証を行います。
まあ、firebase authenticateとかAuth0とか使うのと似た感じな気がする
あいつらはあれかなJWT認証に則ってやってるんかな?
公開鍵で認証しろあほてきなスタンスなんかな
一般的な推奨事項としてBorsos氏は,クライアント側トークンの使用,JWTの使用,APIゲートウェイを提案する。
これはやっぱりCookieにセッション情報を保存して...みたいな感じか?onigiri.w2.icon
API Gateway上で、セッションとJWTを紐付ける
JWTを各サービスに渡して認証しちゃう感じかな
todo.icon API Gateway + JWTパターンを一回実装してみるか?
Authorityサービスはリクエストに含まれるトークンの検証や必要な検証を他のマイクロサービスに依頼し、リクエストの正当性が確認できたら内部通信用トークンを発行して返却する。
内部通信用トークンは1リクエストごとに1つ生成する。
内部通信用トークンはJWT形式となっている。
リクエストごとに1回作るんか...onigiri.w2.icon
内部用トークンなんやから、そんな更新する必要なくない?
内部通信しかしないやろ?
インターネットを通るなら、まあわからんでもない
ゲートウェイを通過すればどのマイクロサービスもアクセスできる状況ですと、仮に開発者が闇堕ちして何かを企てたときに、その人が何でもできる状態になってしまうんですよ。
なるほど、内部の脆弱性を防ぐためにあるのかも
AWSのAssume Roleでの一時的認証情報発行に似た何かを感じる
https://scrapbox.io/files/6374b80b576fa6001eb3875b.png
これはめっちゃ実装工数が低いやり方でいいねonigiri.w2.icon
所謂SSOパターンってやつやろな。APIGateway + SSOパターン。
リフレッシュとかはFirebase Authenticateがしてくれるんやろな