LoginSignup
2
2

More than 3 years have passed since last update.

Auth0でパスワードリセット時にリフレッシュトークンを無効化する

Last updated at Posted at 2019-12-21

Auth0のHooksで検知できるイベントに「Post Change Password」が追加されたので、前々から実現したいと思っていたパスワードリセット時のリフレッシュトークンの無効化をやってみました。

設定手順

Applicationの追加

今回設定するHookはManagement APIを使うので、Hook用にManagement APIを叩くための権限を持たせたMachine to MachineのApplicationを用意します。

スクリーンショット 2019-12-21 23.24.48.png

APIはAuth0 Management APIを選択し、scopeはread:device_credentialsdelete:device_credentialsを設定します。

スクリーンショット 2019-12-21 23.24.25.png

新しいHookを追加

Auth0のDashboardのHooksメニューから「Post Change Password」のHookを追加します。

スクリーンショット 2019-12-21 23.31.15.png

ここから先は追加したHookに対して設定を行なっていきます。

Secretの追加

事前に登録しておいたHook用のApplicationのclient_idclient_secretをsecretに登録します。(あとついでにAuth0テナントのドメインも)

スクリーンショット 2019-12-21 23.11.00.png

このsecretに登録した値はHook内でcontext.webtask.secrets.{key}の形式で参照することができます。こうすれば機密情報や環境によって変わる情報をHookにハードコーディングしなくて済みます。

こうしておいたほうがAuth0の設定をコマンドラインツールでexport/importするときも管理が楽だと思います。

NPMモジュールの追加

Hookで利用するNPMモジュールを追加します。ruleと違い、Hookは任意のNPMモジュールが使えるので便利です。
今回はManagement APIにhttpリクエストをするためのaxiosと、Management APIを叩くためのアクセストークンを取得するためのauth0の2つのモジュールを追加します。

スクリーンショット 2019-12-21 23.07.20.png

Hookの実装

とりあえず動けばいいやぐらいの気持ちで書いたコードなので、エラーハンドリングとかもしてない雑な実装ですが…。

module.exports = async function revokeRefreshTokens(user, context, cb) {
  const { ManagementClient } = require('auth0');
  const axios = require('axios');

  const domain = context.webtask.secrets.domain;

  const auth0 = new ManagementClient({
    domain,
    clientId: context.webtask.secrets.client_id,
    clientSecret: context.webtask.secrets.client_secret,
  });

  async function getDeviceCredentials(accessToken) {
    const response = await axios({
      method: 'GET',
      url: `https://${domain}/api/v2/device-credentials`,
      data: {
        fields: 'id',
        include_fields: true,
        user_id: user.id,
        type: 'refresh_token',
      },
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  }

  function deleteDeviceCredential(accessToken, id) {
    axios({
      method: 'DELETE',
      url: `https://${domain}/api/v2/device-credentials/${id}`,
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
  }

  const accessToken = await auth0.getAccessToken();
  const deviceCredentials = await getDeviceCredentials(accessToken);
  await Promise.all(deviceCredentials.map((device) => deleteDeviceCredential(accessToken, device.id)));
  cb();
}

やっていることは単純で

  1. Management APIを叩くためのアクセストークンを取得
  2. GET /api/v2/device-credentialsでリフレッシュトークンが発行されているデバイスの一覧を取得
  3. DELETE /api/v2/device-credentialsでデバイスを一個ずつ削除

この3つだけです。最後の3でデバイスを削除することでリフレッシュトークンが無効化されます。

まとめ

Auth0のリフレッシュトークンは有効期限がないので一度発行されたらものをずっと利用可能ですが、このHookを実装することでパスワード変更時に発行済みの全てのリフレッシュトークンを無効化することができます。
また、このHookはUniversal Login画面の「パスワードをお忘れですか?」リンクからパスワードを変更した場合でも、Authentication APIのPOST /dbconnections/change_passwordを使ってパスワードを変更した場合でも動作します。

このHookを利用すればパスワード変更時にユーザをAuth0の同一テナント上の全てのアプリから強制ログアウトさせる。みたいなアプリ実装も可能になるのではないでしょうか。

〜2020/5/9 追記〜

Auth0のリフレッシュトークンは有効期限がないので一度発行されたらものをずっと利用可能

現在はリフレッシュトークンのローテーション機能を有効にすることで、有効期限を設定することができるようになっています。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2