JWTと認証と認可
JWTはユーザが誰であるか?の特定のため、つまり認証目的で利用されることが多いかと思います。
一方で認可部分、例えばRole等もJWTに含める方法があります。
HASURAはJWTにRoleを含める
両者のメリデメは以下の回答が参考になります。
Is setting Roles in JWT a best practice?
具体例で考える (Identity Platform / Firebase auth)
上記の回答ではUI側や具体的な認証サーバについては触れられていません。
そこでGCPのIdentity Platformでカスタムクレーム内にRoleを追加する場合についてまとめました。
(考え方としてはAuth0等を使った場合も同じかと思います)
前提知識
- GCPの場合アクセストークンの更新は1時間毎
- ユーザ側もサーバ側もトークンが更新されるまで、新しいクレームは使用できない
(Role更新に気付けない) - リフレッシュトークンが無効化されてもUI側/サーバ側でGCPに接続して検証処理を挟まないと
(当然ながら)無効化に気付けない - トークンの更新に気付くための仕組みとしては以下がある
- ユーザ側:
- サーバのAPIを叩いてトークンを更新されていることを通知
(全APIにインターセプタ的に差し込む)
- サーバのAPIを叩いてトークンを更新されていることを通知
- サーバ側: 以下のどちらか
- JWTのRoleをダイレクトに利用せずにリクエスト毎にGCPに接続して最新のRoleを取得する
- Role更新時にリフレッシュトークンをRevokeし、
リクエスト毎にトークンがRevokeされていないかGCPに問い合わせる
- ユーザ側:
JWTにRole入れる場合のPros / Cons
Pros:
- IDPからJWT受け取り次第、Roleを含めたUIの表示が可能なので初期表示が高速になる
(DBでロール管理する場合はJWT受け取り後、サーバの/meエンドポイント等を叩く必要がある) - サーバ側でユーザリクエストの度のロール確認クエリを省略できる
Cons:
- 上記のRevoke的な実装かGCPへの都度接続が必要
(Revokeで実装する場合は以下が必要)- Revokeされていることをサーバ側で検知(JWT検証で毎回サーバ/IDPのラウンドトリップ発生)
- フロント側にエラーコードを返す
- フロント側でトークン/UI更新処理
備考1: どちらが安全か?
別の切り口として、GCPからユーザが削除された場合でもアクセストークンは最大1時間有効ということになります。
この1時間を許容できないシステムの場合、かつロールをJWT管理している場合はサーバ側で毎回GCPに接続して確認処理を挟む必要があります。
一方でRoleを自前のDBで管理している場合、ユーザ削除時にRoleを記録するためのテーブルからユーザを削除することによりアクセストークンが有効であったとしてもDBのリソースに対するアクセスは自然と防ぐことができます。
UI側でのハンドリングは必要ですが、見せてはいけないものを見せないようにするという意味では自前のDBでロール管理した方が安全かもしれません。
備考2: カスタムサーバ等への移行
開発が進みと、IDPのマネージドな認証サーバでは機能不足等が出てくる場合があります。
例えば本記事執筆時点では、IDPの2段階認証はSMS認証しかサポートしていません。
こういった場合にはカスタムサーバを導入する手があります。
JWTのカスタムクレームを利用して認証 / 認可を両方やっている場合は移行が大変かもしれません。
(分離できるものは最初から分離しておく)