Auth0のユーザー管理・ログ閲覧ができるDashBoardをAuth0の拡張機能を使って用意する

## はじめに
ある程度以上の規模の会社やサービスでAuth0を使っていると、Auth0に対する権限を担当者ごとに分けたいというニーズが出てくるかと思います。例えば、以下の三者の役割ごとに権限を分けるといった場合です。

* Auth0のテナント管理者
* Auth0を利用するアプリの開発者
* エンドユーザーからの問い合わせを受け付けるヘルプデスク

Auth0のテナント管理者はテナントを作成したユーザーなので問題ないと思います。あとでテナント設定からAll Applicationsの権限を持たせてユーザーをテナントに招待することも可能です。Auth0を利用するアプリの開発者についても同様で、開発者をテナントに招待するときに任意のアプリのみに権限を絞ることで、そのアプリの設定以外は触れなくなるので、こちらも特に問題ないと思います。

しかし、ヘルプデスクの権限を考えるとちょっと悩ましいです。
ヘルプデスクの担当業務範囲にもよりますが、エンドユーザーからの問い合わせを受け付けるという業務の特性上、Auth0に登録されているユーザーの情報やログが見れた方が良い場合もあると思います。しかし、ヘルプデスクの担当者にはアプリの設定を変更できるような権限は付与したくありません。Auth0の管理DashBoard（テナント管理画面）だけでこういった要件を実現するのは難しいです。

ユーザー管理やログを見るためだけのWebアプリを作り、Management APIで必要な情報を引っ張ってきて頑張る。というのも一つの手ですが、Auth0にはまさにこの要件を叶える「Delegated Admin DashBoard」という拡張機能があります。この拡張機能を使ってユーザー情報・ログを見ることができるDashBoadを作成したので、その機能と作成（設定）方法とを紹介します。以降、このDashBoardのことをDelegated Admin DashBoardと表記します。

## Delegated Admin DashBoardでできること

### ユーザー管理

ユーザー一覧の参照や、ユーザーの作成・更新・削除を行うことができます。

![スクリーンショット 2020-03-16 17.04.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/1a09a680-9f00-9323-0036-560c8ec5f412.png)


後述するロールの設定にもよりますが大雑把に以下のような操作が可能です。

* ユーザーの検索
* ユーザー情報の閲覧
* ユーザーの作成
* ユーザーのブロック
* パスワードリセット（メール送信）
* 確認メール再送信
* メールアドレス変更
* パスワード変更
* ユーザーの削除

Auth0の管理DashBoardからできることとは微妙に違うので、詳細は以下のAuth0のドキュメントを参照してください。

[Delegated Administration: Manage Users #Available User Actions in the Delegated Administration Dashboard](https://auth0.com/docs/extensions/delegated-admin/v3/manage-users#available-user-actions-in-the-delegated-administration-dashboard)

### ログ閲覧

ログの一覧とログの詳細（json形式）を見ることができます。

![スクリーンショット 2020-03-16 17.05.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/24e527d5-9355-5b9f-8f10-b8e763001ab6.png)

しかし、Auth0の管理DashBoardほど過去のログは表示してくれないようです。

### Hookの設定
Delegated Admin DashBoardの動作をカスタマイズするHookを設定することができます。

![スクリーンショット 2020-03-16 17.08.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/1bcc0cf7-8ded-417b-dae9-111b3bf74efa.png)

後述の「Delegated Admin - Administrator」のロールを持つユーザーがHookを設定することができます。Hookの設定メニューはDelegated Admin DashBoardの右上に表示されているテナント名をクリックすると表示されるドロップダウンメニューから選択できます。

Hookには以下の4種類があり、それぞれ用途が異なります。

* Fileter Hook
* Access Hook
* Write Hook
* Memberships Hook
* Settings Query

Hookを使えばデフォルトのロール設定だけでは対応できない細かい権限設定も可能になります。

## Delegated Admin DashBoardの設定

### アプリケーション登録
Auth0のドキュメントに書いてある通り、Delegated Admin DashBoardをアプリケーション登録します。

[Create Delegated Admin Applications](https://auth0.com/docs/dashboard/guides/extensions/delegated-admin-create-app)

アプリケーションタイプは「Single Page Web Applications」を選択してください。

![スクリーンショット 2020-03-16 15.54.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/e7702333-e82a-aea8-8605-66cc349b2ebd.png)

次に、登録したアプリのCallback Allowed Callback URLsとAllowed Logout URLsを設定します。設定するURLはテナント名やテナントのリージョンごとに異なるので適切な値を設定してください。

| 設定項目 | 値 |
|:--|:--|
| Allowed Callback URLs | https://{xxx}.{yy}8.webtask.io/auth0-delegated-admin/login |
| Allowed Logout URLs | https://{xxx}.{yy}8.webtask.io/auth0-delegated-admin |

{xxx}にはAuth0のテナント名が入ります。テナントのドメインがabc.auth0.comなら、{xxx}に入るのはabcになります。また、{yy}にはリージョンごとの固有の値が入ります。具体的な値は先に記載したAuth0のドキュメント（[Create Delegated Admin Applications](https://auth0.com/docs/dashboard/guides/extensions/delegated-admin-create-app)）に載っているのでそちらを参照してください。

自分のテナントはUSリージョンなので以下のように設定しています。

![スクリーンショット 2020-03-16 16.09.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/9e58998e-74fb-6219-6fb0-19c97de12169.png)

**2020/5/19 追記**
Auth0でNode.js 12がサポートされるようになりました。
ランタイムをNode.js 12に切り替えた場合、以下のようにCallback URLとLogout URLを変更する必要があります。

| 設定項目 | 値 |
|:--|:--|
| Allowed Callback URLs | https://{xxx}.{yy}12.webtask.io/auth0-delegated-admin/login |
| Allowed Logout URLs | https://{xxx}.{yy}12.webtask.io/auth0-delegated-admin |

参考：[Migration Guide: Extensibility and Node 12 #delegated-administration-urls](https://auth0.com/docs/migrations/guides/extensibility-node12#delegated-administration-urls)

### データベース接続の作成

Delegated Admin DashBoardを利用するユーザーを管理するための新しいデータベースを作成します。

Auth0の管理DashBoardの`Connections > Database`を選択し、「CREATE DB CONNECTION」ボタンをクリックします。本記事ではデータベースをHelpDeskという名前で作成しています。

![スクリーンショット 2020-03-16 15.59.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/6516d9c3-c35d-e767-248f-46fc5a353b59.png)

ここで作成したデータベースはDisable Sign Upsの設定をONにします。この設定でサインアップを無効化しておかないと、サインアップすれば誰でもDelegated Admin DashBoardを利用することができてしまうからです。このデータベースへのユーザー追加はAuth0のテナント管理者のみが行うようにしましょう。

このデータベースはDelegated Admin DashBoard（と後述する`auth0-delegeted-admin`）以外のアプリからは利用しないので、作成後にデータベースの設定画面からApplicationsタブを選択し、Delegated Admin DashBoard以外のアプリとの接続設定がされていないことを確認してください。

デフォルトのテナント設定だと、新しいアプリがAuth0に登録されるたびに全てのデータベースとの接続が有効になってしまいます。今後、アプリ登録を行うと意図しないアプリがここで作成したデータベースと接続してしまう可能性があるので注意してください。
もしも支障がないのであればテナント設定のほうを変えてしまって、アプリ登録時に勝手にデータベースと接続しないようしたほうが良いと思います。`Tenant Settings > Advanced`のメニューから、`Enable Application Connections`の項目をOFFにすることで設定できます。

ただし、この項目をOFFにするとアプリを登録してもデータベースと自動的に接続されなくなるので、今度は逆に接続先データベースを設定する手間が毎回発生するようになります。

### DashBoadを利用するユーザーの作成

Delegated Admin DashBoard用に作成したHelpDeskデータベースにAuth0の管理DashBoardからユーザーを追加します。

`Users & Roles > Users`の画面から「CREATE USER」ボタンをクリックしてユーザーを登録します。`Connection`が先に作成したHelpDeskデータベースであることを確認して登録してください。

![スクリーンショット 2020-03-16 16.02.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/055ea87b-07ad-49a3-4fe7-7eb0058e4be8.png)

### ロールの作成と割り当て

Delegated Admin DashBoardはユーザーのロールに基づいて利用できる機能を制限しています。Delegated Admin DashBoard固有のロール定義は既に決まっているので、それに沿うようにロールの作成とユーザーへの割り当てを行います。

作成するロールは以下の4つです。この名称のロールしかDelegated Admin DashBoardは受け付けないので、このままの名称でロールを作成してください。

| 名称 | できること |
|:--|:--|
| Delegated Admin - Administrator | Delegated Admin DashBoardでできる全ての操作が可能 |
| Delegated Admin - User | ユーザーに関する操作は一通り可能。ログ閲覧とHookの設定は不可 |
| Delegated Admin - Auditor | ユーザー情報の閲覧のみ可能。 |
| Delegated Admin - Operator | ログの閲覧のみ可能 |

Users & Roles > Rolesの画面の「CREATE ROLE」ボタンをクリックしてロールを4つ作成します。
※作成したロールには個別にPermissionをつけることが可能ですが、Delegated Admin DashBoardを利用する上では設定不要です。

![スクリーンショット 2020-03-16 16.11.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/cccd9e92-4b62-8df1-b0c9-2e909f17eba9.png)

あとは作成したロールの中から適当なのを選んで、先ほど作成したユーザーにロールを割り当てます。

![スクリーンショット 2020-03-16 16.14.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/a3ac289a-8cfe-e142-12fc-3dc4242eab82.png)

### Rulesの作成

ユーザーがDelegated Admin DashBoardにログインしたときのIDトークンに、ロールの情報を追加するために以下のRulesを設定します。

```javascript
function (user, context, callback) {
  if (context.clientID === '{CLIENT_ID}') {
      const namespace = 'https://example.com/auth0-delegated-admin';
      context.idToken[namespace] = {
          roles: (context.authorization || {}).roles
      };
  }
  callback(null, user, context);
}
```

{CLIENT_ID}はアプリ登録したDelegated Admin DashBoardのクライアントIDに置き換えてください。
`namespace`の`https://example.com/auth0-delegated-admin`はこのまま設定してください。でないとDelegated Admin DashBoardが認識してくれません。

### 拡張機能のインストール

ここまできてようやく拡張機能のインストールです。
Auth0の管理DashBoardのExtensionsから`Delegated Admin DashBoard`を探してインストールします。インストール時に設定を聞かれるので`EXTENSION_CLIENT_ID`に先ほどアプリ登録したDelegated Admin DashBoardのクライアントIDを設定します。他の項目は必要に応じて設定してください。

![スクリーンショット 2020-03-16 16.16.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/6cb8def6-7f94-1ae2-528e-9e4edbe52fbd.png)

拡張機能インストールが完了するとApplicationsに`auth0-delegated-admin`が追加されます。これはDelegated Admin DashBoardがManagement APIを利用するために必要な設定みたいなので、下手に触らない方が無難です。

この拡張機能をインストールするとApplicationsに`auth0-delegeted-admin`というアプリが自動的に登録されます。Delegated Admin DashBoardからユーザー作成、更新、削除などを実施する場合は、その対象ユーザーが保存されているデータベースに、この`auth0-delegeted-admin`からも接続できなければならないので、必要に応じて`Connections`の設定から接続するデータベースをONにしてください。

![スクリーンショット 2020-03-16 16.22.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/2ecf0f82-c164-5d06-4b89-6b20fc8ddd36.png)


### Delegated Admin DashBoardにアクセス

Auth0の管理DashBoardの`Extensions`からInstalled Extensionsのタブを選択すると、先ほどインストールしたDelegated Administration Dashboardが表示されます。

![スクリーンショット 2020-03-16 16.23.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/7d094cd6-d1bd-bfd5-b61e-26a6e8fafc45.png)

この「Delegated Administration Dashboard」の文字列をクリックすると、ログイン画面が表示されるので、Delegated Admin DashBoardの利用ユーザーとして登録したメールアドレスとパスワードを入力してログインをします。

![スクリーンショット 2020-03-16 16.25.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/60a9e593-ce80-caa3-9d39-180671f4c281.png)

以下のような画面が表示されれば成功です。

![スクリーンショット 2020-03-16 17.04.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/1a09a680-9f00-9323-0036-560c8ec5f412.png)

このDelegated Admin DashBoardから先に述べたユーザー管理、ログ閲覧、Hookの設定を行うことができます。

## Hook
Auth0のドキュメントにサンプルが記載されていますが、自分でも少し試してみたのでそのコードを載せておきます。

### Filter Hook

Filter Hookを利用するとDelegated Admin DashBoardに表示されるユーザーをクエリで制御することができます。

[Delegated Administration Hooks: The Filter Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/filter)

````javascript
function(ctx, callback) {
  var namespace = 'https://example.com/auth0-delegated-admin';
  var userRoles = ctx.request.user[namespace] && ctx.request.user[namespace].roles;

  if (!userRoles || !userRoles.length) {
    return callback(new Error('Unauthorized error.'));
  }

  if (userRoles.includes('Delegated Admin - Administrator')) {
    return callback(null);
  }

  return callback(null, '- identities.connection:"HelpDesk"');
}
```

`Delegated Admin - Administrator`のロールを持たない人はDelegated Admin DashBoardからHelpDeskデータベースに登録されているユーザーが表示されないようにしています。callbackの第二引数に設定するクエリは`Lucene Query`という構文が使用できます。

[LuceneTutorial.com](http://www.lucenetutorial.com/lucene-query-syntax.html)

### Access Hook

Access Hookを設定するとDelegated Admin DashBoardに表示されているユーザーに対する操作を検知して任意のコードを実行することができます。

[Delegated Administration Hooks: The Access Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/access)

```javascript
function(ctx, callback) {
  var namespace = 'https://example.com/auth0-delegated-admin'
  var userRoles = ctx.request.user[namespace] && ctx.request.user[namespace].roles;
  
  if (!userRoles || !userRoles.length) {
    return callback(new Error('Unauthorized error.'));
  }

  switch (ctx.payload.action) {
    case 'delete:user':
      return callback(new Error('Unauthorized error.'));
    case 'change:email':
      if (!userRoles.includes('Delegated Admin - Administrator')) {
        return callback(new Error('Unauthorized error.'));
      }
      return callback();
    default:
      return callback();
  }
}
```

Delegated Admin DashBoardからはユーザーの削除は不可、emailの変更は`Delegated Admin - Administrator`のロールを持っている人のみ可能。としています。


### Write Hook

Write Hookはユーザーの作成やカスタムフィールドの更新をしたときに実行されるコードを設定できます。

[Delegated Administration Hooks: The Write Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/write)

```javascript
function(ctx, callback) {
  var newProfile = {
    email: ctx.payload.email,
    password: ctx.payload.password,
    connection: ctx.payload.connection,
    user_metadata: ctx.payload.user_metadata,
    app_metadata: {
      department: ctx.payload.memberships && ctx.payload.memberships[0],
      ...ctx.payload.app_metadata,
    },
  };

  if (ctx.method === 'update') {
    Object.keys(newProfile).forEach((key) => {
      if (newProfile[key] === ctx.request.originalUser[key]) delete newProfile[key];
    });
  }

  const whitelist = ['example.com', 'example.co.jp'];
  const userHasAccess = whitelist.some(
    (domain) => {
      const emailSplit = ctx.payload.email.split('@');
      return emailSplit[emailSplit.length - 1].toLowerCase() === domain;
    },
  );

  if (!userHasAccess) {
    return callback(new Error('Access denied.'));
  }

  return callback(null, newProfile);
}
```

`example.com`または`example.co.jp`以外のドメインのメールアドレスのユーザーを作成できないようにしています。ただ、このコードだとメールアドレスの変更まではブロックできないのでRoleやAccess Hookなどと組み合わせてメールアドレスの変更自体をできないようにしないと、このコードの使い所は無いと思います。

### Memberships Hook

Membership Hookのcallback関数に配列を渡すとDelegated Admin DashBoardからユーザーを作成するときに部署名（Membership）のフィールドが表示されるようになり、プルダウンで配列の内容を入力できるようになります。（配列の要素が一つだけだと部署名フィールドも表示されません。）

[Delegated Administration Hooks: The Memberships Query Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/membership)

```javascript
function(ctx, callback) {
  var namespace = 'https://example.com/auth0-delegated-admin';
  var userRoles = ctx.payload.user[namespace] && ctx.payload.user[namespace].roles;

  if (!userRoles || !userRoles.length) {
    return callback(null, []);
  }

  if (userRoles.includes('Delegated Admin - Administrator')) {
    return callback(null, ['IT', 'HR', 'Finance', 'Marketing']);
  }

  return callback(null, [ctx.payload.user.app_metadata.department]);
}
```

`Delegated Admin - Administrator`のロールを持つ人にのみ、以下のように部署名（Membership）フィールドが表示しています。

![スクリーンショット 2020-03-16 17.14.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/0cd384e0-9aa1-b67a-270d-ed99adb93e93.png)

### Settings Query

Setting Queryを利用するとDelegated Admin DashBoardのLook and Feelをカスタマイズすることができます。

[Delegated Administration Hooks: The Settings Query Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/settings)

```javascript
  var namespace = 'https://example.com/auth0-delegated-admin';
  var userRoles = ctx.request.user[namespace] && ctx.request.user[namespace].roles;

  var isAdmin = userRoles.includes('Delegated Admin - Administrator');

  return callback(null, {
    connections: ['Username-Password-Authentication'],
    canCreateUser: isAdmin,
  });
```

`connections`でDelegated Admin DashBoardからユーザーを登録するときの登録先データベースを`Username-Password-Authentication`だけに限定しています。さらに`canCreateUser`で`Delegated Admin - Administrator`のロールを持つ人だけにユーザー作成のボタンが表示されるようにしています。

## まとめ

Auth0の拡張機能である「Delegated Admin DashBoard」を使うとユーザー管理やログの閲覧が出来るDashBoardが簡単に作成できます。
RoleとHookを組み合わせて利用することで細かい権限管理も実現できそうなので、担当者の役割に応じてAuth0の管理DashBoardとDelegated Admin DashBoardを使い分けるという運用も可能だと思います。是非試してみてください。

## 参考にしたAuth0のドキュメント

[Delegated Administration Extension](https://auth0.com/docs/extensions/delegated-admin/v3)
[Create Delegated Admin Applications](https://auth0.com/docs/dashboard/guides/extensions/delegated-admin-create-app)
[Install the Delegated Admin Extension](https://auth0.com/docs/dashboard/guides/extensions/delegated-admin-install-extension)
[Use the Delegated Admin Extension](https://auth0.com/docs/dashboard/guides/extensions/delegated-admin-use-extension)
[Delegated Administration: Manage Users](https://auth0.com/docs/extensions/delegated-admin/v3/manage-users)
[Delegated Administration: Hooks](https://auth0.com/docs/extensions/delegated-admin/v3/hooks)
[Delegated Administration Hooks: The Filter Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/filter)
[Delegated Administration Hooks: The Access Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/access)
[Delegated Administration Hooks: The Write Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/write)
[Delegated Administration Hooks: The Memberships Query Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/membership)
[Delegated Administration Hooks: The Settings Query Hook](https://auth0.com/docs/extensions/delegated-admin/v3/hooks/settings)