読者です 読者をやめる 読者になる 読者になる

理系学生日記

おまえはいつまで学生気分なのか

忍者TOOLS

OAuth 2.0の認可権限無効化の仕様

OAuth 2.0 のプロバイダを作るんだったら、もしかして認可権限の取消画面も作らないといけないのかしら〜〜〜〜(ほげ〜〜〜〜)と思って調べてたら、その revoke の仕様も RFC で仕様化されていることを知りました。 これにより、必ずしも取消画面を作らなくて良いということにはなりませんが、API として公開することは容易そうなので心をなでおろしています。

OAuth 2.0 (RFC 6749) では認可サーバ側での認可権限の無効化について触れられていますが、RFC 7009 で触れられているのは、クライアントからの権限の無効化です。 Twitter における「アプリ連携」メニューでは、個々のアプリケーションに対して「許可を取り消す」というボタンが存在していますが、あれを仕様化したものと理解すれば良い。

トークンの無効化という文脈には以下の 2 つがあります。

  • リフレッシュトークンの無効化
  • アクセストークンの無効化

このうち、絶対に実装せよ(MUST) とされているのはリフレッシュトークンの無効化です。アクセストークンと比較して、リフレッシュトークンの有効期限は一般に長い (あるいは無期限) ので、これは当然といえば当然ですね。アクセストークン無効化したって、リフレッシュトークンが有効なままなら、新しいアクセストークンの再取得が可能ですし。

一方でアクセストークンの無効化は、アクセストークンを JWT で実装するといったケースでは非常に困難なものになります。このようなケースでは、アクセストークンの有効期限を短くして対応することになるでしょう。

Token Revocation

トークンの無効化に際して、認可サーバは「Token Revocation EndPoint」を実装する必要があります。これは一種の無効化 API のエンドポイントと考えてもらったら良さそうです。 クライアントは、認可サーバに以下のようなリクエストを送ります。

POST /revoke HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

Token Revocation Endpoint の仕様の概要としては、

  1. HTTPS で提供する (TLS のバージョンはもちろん新しいやつをつかいましょう)
  2. POST メソッドを受け付ける。パラメータは後述。
  3. クライアント認証は実施。(RFC 6749 参照)

POST メソッドのパラメータは以下のようなものになります。

パラメータ名 REQUIRED/OPTIONAL 概要
token REQUIRED 無効化したいトークン
token_type_hint OPTIOAL ※後述

このうち、token_type_hint は、リフレッシュトークンを無効化するのか、アクセストークンを無効化するのかのヒントを示しています。仕様上は access_tokenrefresh_token が定義されていることもあり、基本的にはこの 2 値を使うことになるでしょう。 認可サーバ側はリクエストを受け取り、トークンを無効化したら、HTTP レスポンス 200 を返却します。この後、クライアント側は無効化したトークンを使ってはいけません(使ってもエラーになりますが)。

おもしろいのは、無効なトークンに対する無効化要求に対しても、認可サーバは HTTP ステータス 200 を返却する点です。 たしかに、このケースでクライアントに 4XX や 5XX を返却しても、クライアントは何ができることはありませんね。

リフレッシュトークンの無効化は、紐づくアクセストークンの無効化を要求するか

悩ましいのは、リフレッシュトークンを無効化したとき、そのリフレッシュトークンから作成されたアクセストークンを無効化しないといけないかどうかですが、RFC 的には「アクセストークンの無効化をサポートしているのであれば、リフレッシュトークンの無効化でアクセストークンも無効化すべきでしょ (SHOULD)」という立場です。 JWT とかでアクセストークンを構成する場合はアクセストークンの無効化自体が困難なケースが多いので、そういう場合は、アクセストークンは「Expire するのに任せる」というのが良いかなと思います。