なぜ OAuth でリフレッシュトークンを使うのか?
はじめに
OAuth2 でリフレッシュトークンを使う場合、そのメリットが何なのか十分理解できていなかったのでまとめました。
「ここの説明は間違っている」「こういったケースで有効だった」などありましたら、コメントいただけると嬉しいです。
前提事項
OAuth2 の認可コードによる付与(Authorization Code Grant)でリフレッシュトークンを使う場合、という前提のもとに書いていますので、その点ご留意ください。
(他のフローやリフレッシュトークンを使わない場合は異なる場合があるかもしれません)
アクセストークンとは?
アクセストークンは、クライアントがリソースサーバーの保護されたリソースにアクセスするために必要のトークンです。
アクセストークンの有効期限は短く設定されている場合が多いです。(イメージとしては数十分〜数時間)
リフレッシュトークンとは?
リフレッシュトークンは、アクセストークンを再発行するために使用するトークンです。
リフレッシュトークンは有効期限が長く設定される場合が多いです。(イメージとしては日単位)
アクセストークンを発行できるので、流出した場合のリスクが大きく、厳重に管理する必要があります。
(※補足:OAuth 的にリフレッシュトークンの発行は任意です)
アクセストークンとリフレッシュトークンに求められるセキュリティの違い
Access Tokenは秘密にしなければなりませんが、存続期間が短いため、セキュリティの考慮事項の制限も緩くなります。
https://auth0.com/blog/jp-refresh-tokens-what-are-they-and-when-to-use-them/
上記の通り、アクセストークンは有効期限が短いため、リフレッシュトークンと比較すると求められるセキュリティレベルは低くなります。
(※補足:あくまで比較すればの話で、どちらも秘匿すべき情報であることに変わりはありません)
リフレッシュトークンの安全性
先に説明した通り、リフレッシュトークンはアクセストークンを再発行するために使われるトークンです。
その性質上、保持するのはクライアント(ここではアプリケーションサーバーなど)のみで、認可サーバーとの間でのみやりとりされます。
また再発行の際にはクライアントIDとクライアントシークレットも必要です。
限定された通信と追加の秘匿情報が必要なため、流出のリスクを最小化することができます。
アクセストークンとJWT
JWT は "JSON Web Token" の略称で、トークンの形式の一種です。近年使われるケースが多いと筆者は感じています。
(詳細を知りたい方は、私が書いた JWTについてまとめてみる などを参考にしてください)
その特性の一つとして、トークン自体に有効期限などの情報を含めることができ、更に署名(JWS)により確かに発行者が指定したものであることが保証できます。
また、共通鍵暗号方式や公開鍵暗号方式を使用して署名の検証ができるので、API やデータベースへのアクセスが不要です。
これはサービスのスケーリングの観点で大きなメリットです。
こういった特性からアクセストークンとJWTは相性が良いです。
しかし外部への問い合わせを行わない場合、発行されたJWTを無効化することが実質できません。
そのため有効期限を短くすることで、流出した場合のリスクを最小限にできます。
JWTをアクセストークンとして使うケースでは、リフレッシュトークンを併用するメリットが大きいと思いました。
まとめ
アクセストークンとリフレッシュトークンの両方を使う理由は、セキュリティと利便性のバランスと取れるケースがあるから、と個人的には感じていました。
- 有効期限の短いアクセストークンは、秘匿はしつつもリスクは比較的低いので使用しやすい。
- 最近よく使われているJWTとの相性も良い。
- 有効期限の長いリフレッシュトークンは、安全に保管し、アクセストークンを再発行する時以外は使用しないことでリスクを最小化する。
ただ Stack Overflow の議論を見ても様々な観点での意見があり、使う人や状況によって意味合いは変わるのかもしれません。
(議論以前のコメントもいくつかありましたが・・・)
例えば、アクセストークンがリフレッシュトークンと同程度の安全な環境で使える場合、リフレッシュトークンを使うメリットはないと思います。
要はバランス的な話になってしまいますが、全ての状況に適した方法はないので、プロダクトの特性に応じて適した方法を採用できるように知識と思考力を広げていきたいところです。
余談
この記事を書いている時にふと思い出したので貼っておきます。
この記事は非常にすぐれていると思いました。その理由は、特定の前提条件においてアーキテクチャを選定した理由が明確であるからです。読者はこの記事の結論よりも方法論を学ぶべきだと思います。結果は前提条件によって変わりますが、方法論は普遍的なものだからです。
読者のために参考記事のURLを再掲します
https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/
参考リンク
- Refresh Token: どのような場合に使用し、どのように JWT と相互作用するか
- security - Why Does OAuth v2 Have Both Access and Refresh Tokens? - Stack Overflow
- HasuraのブログでJWTのベストプラクティスとして、最初JWTを短かめの有効期限設定でメモリに格納し、その後silent refreshでログイン状態の保持(refresh tokenはcookieに保存&HttpOnly)を行う、とありましたが、この手法についてはどう思われますか? https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/ - ockeghem page
Discussion
ここで、なぜ有効期限が異なる理由を深掘りするとさらに理解が進みそうです。
RFC 6750で定義されており、OAuth 2.0の実装で広く採用されている "Bearer Token" という形式の Access Token の場合、単体でリソースアクセスに利用できる = 漏洩時に不正利用されるリスクがある。よって有効期限を短くすることでそのリスクを下げるのが一般的となっています。
ここで説明されていますが、Bearer Token に対して、発行されたもののみが利用できる、"Sender-Constrained Token" という概念があります。
Client Secretを安全に保管でき、Client Authentication に利用できる Confidential Client の場合、Refresh Token は Sender-Constrained Token と言えます。
このようなトークンは、単体の漏洩リスクが Bearer Token よりも小さくなります。
Access Token にも Sender-Constrained Token な仕組みはいくつか存在し、FAPIと呼ばれる金融関係やヘルスケアのようなセンシティブな情報を扱う分野で使われる拡張プロファイルにおいては、それを要求するものもあります。
この辺りを意識して仕様を読んでみることをおすすめします。