Auth0のManagement APIのアクセストークンを取得するメモ


Auth0の[Management API](https://auth0.com/docs/api/management/v2#!/Users/get_users)の利用時の話です。

[前回の記事](https://qiita.com/n0bisuke/items/07c388f0c0be77ed601c)だとアクセストークンを直指定だったので、時間が立つとアクセストークンが切れてしまいます。

API経由で動的に生成する方法を調べたのでメモです。

## アクセストークンのテスト

APIトークンを試す際には、公式の[Get Access Tokens for Testing
](https://auth0.com/docs/api/management/v2/get-access-tokens-for-test)にあるように、管理画面からAPIトークンを取得して直接指定すると良いです。

> ![](https://cdn2.auth0.com/docs/media/articles/api/tokens/copy-token.png)

[Auth0でログインユーザーのロール(Roles)を元にハンドリングするRulesを書いてみた](https://qiita.com/n0bisuke/items/07c388f0c0be77ed601c)の記事で書いてたやり方です。

## アクセストークンを動的に生成する - プロダクション

公式の[Get Access Tokens for Production](https://auth0.com/docs/api/management/v2/get-access-tokens-for-production)に載っているAPI(`https://YOUR_DOMAIN/oauth/token`)にリクエストしてAPIトークンを生成して利用できます。

### 公式サンプル

```js
var options = {
  method: 'POST',
  url: 'https://YOUR_DOMAIN/oauth/token',
  headers: {'content-type': 'application/x-www-form-urlencoded'},
  form: {
    grant_type: 'client_credentials',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    audience: 'https://YOUR_DOMAIN/api/v2/'
  }
};

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
```

これをRules内(Node.js)で書いていきます。

### Rulesに適用する

[Auth0でログインユーザーのロール(Roles)を元にハンドリングするRulesを書いてみた](https://qiita.com/n0bisuke/items/07c388f0c0be77ed601c)のスニペットと合わせるとこんな感じです。

util.promisifyでrequestをpromise化して、async/awaitで利用する形にしました。

```js
async function (user, context, callback) {
  const util = require('util');
  const requestPromise = util.promisify(request);
  console.log(user);
  
  //1. メール確認
  if (!user.email || !user.email_verified) {
    return callback(new UnauthorizedError('Access denied.'));
  }
  //2-1. トークン生成
  const tokenApiOptions = {
    method: 'POST',
    url: `https://${configuration.domain}/oauth/token`,
    headers: {'content-type': 'application/x-www-form-urlencoded'},
    form: {
      grant_type: 'client_credentials',
      client_id: configuration.client_id,
      client_secret: configuration.client_secret,
      audience: `https://${configuration.domain}/api/v2/`
    }
  };
  const tokenApiRes = await requestPromise(tokenApiOptions);
  const token = JSON.parse(tokenApiRes.body).access_token; //API経由で取得したトークン
  
  //2-2. ユーザーが所属するロールを取得
  const options = {
    url: `https://${configuration.domain}/api/v2/users/${user.user_id}/roles`,
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
        },
    json: true,
  };
  const roleApiRes = await requestPromise(options);  
  
  //2-3. ロール判定
  const whiteList = ['role1','role2']; //許可するロール名
  const userRoles = roleApiRes.body.map(item => item.name);
  const userHasAccess = userRoles.some(value => whiteList.includes(value));
  
  if (!userHasAccess) {
    return callback(new UnauthorizedError('Access denied.'));
  }

  callback(null, user, context);
}
```

Settingsでconfigrationの値を設定すればOKです。

> ![](https://i.gyazo.com/9164aa0fb60f0cfe817dada119083981.png)

## エラーなど

### ClientIDとSecretについて

ちなみに、`client_id`と`client_secret`はテストでトークン作成した際の`MACHINE TO MACHINE`のものを利用したらうまく行きました。

> ![](https://i.gyazo.com/be0d0eeadaa5bee3179c2de7dc9fbaaa.png)

通常のログインアプリケーションのclient idを利用すると以下のようなエラーが出てうまくいかなかったです。

```
"error":"unauthorized_client","error_description":"Grant type 'client_credentials' not allowed for the client."
```

> [Generate token in WebApi. Error: “Grant type ‘client_credentials’ not allowed for the client.”](https://community.auth0.com/t/generate-token-in-webapi-error-grant-type-client-credentials-not-allowed-for-the-client/9373)

### function外だと`Unexpected token const`

function外には基本的に記述はしない方が良さそうです。

```js
const util = require('util');
const requestPromise = util.promisify(request);

async function (user, context, callback) {
  ・
  ・
  ・
  省略
  ・
  ・
  ・
}
```

```
{
"code": 400,
"message": "Compilation failed: Unexpected token const",
"error": "Unexpected token const",
・
・
・
省略
```

これだとエラーはでなかったです。

```js
async function (user, context, callback) {
   const util = require('util');
   const requestPromise = util.promisify(request);
  ・
  ・
  ・
  省略
  ・
  ・
  ・
}
```