【Auth0】アクセストークンが無いと呼び出せないWebAPIを作成する

# はじめに
Auth0が発行するアクセストークンが無いと呼び出せないWebAPIを作成してみます。

# 管理画面にAPIを登録

Auth0の管理画面のAPIsのページを開き右上の`CREATE API`を押します。

<img width="881" alt="スクリーンショット 2020-06-22 23.43.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/8472db3a-0b9c-d3a2-c12c-50d3c6f8c05e.png">

作成するWebAPIの情報として`Name`と`Identifier`を入力します。

![スクリーンショット 2020-06-22 18.35.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/4a90e396-3841-91b9-fe14-9dabc92bedc0.png)

`Name`は機能に影響しないので後から見てなにを表しているかわかりやすい名前をつけたほうが良いです。

`Identifier`はテナント内で一意となる識別子であれば形式はなんでも良いですが、Auth0はURL形式を推奨しています。
`Identifier`は後から変更することができないので注意してください。

`Singning Alogorithm`は`RS256`と`HS256`の2択となっています。
`RS256`だと署名が、`HS256`だとHMACがトークンに付与されます。選択したアルゴリズムに合わせてWebAPIはトークンを検証します。
本記事では`RS256`で設定を行います。

入力が完了したら`CREATE`を押すとAPIが登録されます。

登録したAPIの`Settings`タブを開くとAPIの設定を変更できます。
この画面でこのAPI向けのアクセストークンの有効期限を変更したり、リフレッシュトークンの利用可否を設定することが可能です。

![スクリーンショット 2020-06-22 19.08.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/09aa1c72-53aa-7baf-a05a-fd391bf02d4e.png)

# Permission設定
権限設定をやってみます。

`Permissions`タブを開いて`Permission`と`Description`を設定します。

![スクリーンショット 2020-06-22 19.17.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/a7515d3b-3692-9c92-fe83-9e23161fa9f3.png)

後ほど出てくるWebAPIのサンプルアプリケーションで定義されている`read:messages`を追加します。

<img width="377" alt="スクリーンショット 2020-06-22 22.06.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/5b9c97e7-1583-c437-a64d-3a8b3e177371.png">

# WebAPIの準備

WebAPIのサンプルアプリケーションがAuth0公式で用意されているのでそちらを利用します。
Auth0-SamplesのGitHubリポジトリにはいくつかAPIのサンプルがありますが本記事ではExpressのWebAPIのサンプルアプリケーションを使用します。

https://github.com/auth0-samples/auth0-express-api-samples

管理画面でAPIを登録するときに`RS256`を選択しているのでサンプルアプリケーションもそれに合わせて`01-Authorization-RS256`を使用します。

中に`.env.example`というファイルがあるので`.env`にリネームし中身を書き換えます。
`AUTH0_AUDIENCE`は先ほど管理画面で登録したAPIの`Identifier`を設定し`AUTH0_DOMAIN`にはAuth0のテナントのドメインを設定します。

```:.env
AUTH0_AUDIENCE={API_IDENTIFIER}
AUTH0_DOMAIN={DOMAIN}
```

設定が完了したらコマンドを入力してWebAPIを起動させます。

```
npm run start
```

成功すれば`localhost:3010`で起動します。

# アクセストークンの取得

準備したWebAPIにはAuth0が発行する専用のアクセストークンがないと呼び出せないAPIがあります。
WebAPIと同一テナントに登録されているアプリケーションであればどれからでも専用のアクセストークンを取得できますが
本記事では簡単に試すためにAPIを登録したときに自動で作成される`Test Application`を利用します。

`Machine to Machine Applications`タブを開くとAPIと接続可能なアプリケーションの一覧が表示されます。
本記事で登録したAPIの場合は`example (Test Application)`が自動で作成されたテストアプリケーションになります。

<img width="667" alt="スクリーンショット 2020-06-22 22.37.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/70605a30-a7b1-73b3-e301-ada27e767ede.png">

テストアプリケーションの右側のトグルボタンが`AUTHORIZED`となっていることを確認し`>`を押します。
`PERMISSIONS`の一覧が表示されるので先ほど登録した`read:messages`にチェックをいれて`UPDATE`を押します。

<img width="693" alt="スクリーンショット 2020-06-22 22.59.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/e19b076d-cd40-1d05-cfef-2832d524aeb6.png">

次に`Test`タブを開くとテストアプリケーションからアクセストークンを取得する方法が記載されているのでその内容を参考にCLIでアクセストークンを取得します。

<img width="693" alt="スクリーンショット 2020-06-22 23.02.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/9619b8b5-13d6-e529-2b11-20a553b8204c.png">


アクセストークンを取得するときに`audience`パラメーターで対象のAPIの`Identifier`を指定することでそのAPI専用のアクセストークンを取得しています。
`テナント名`、`クライアントID`、`クライアントシークレット`には適切な値を設定してください。

```
curl --request POST \
     --url https://テナント名/oauth/token \
     --header 'content-type: application/json' \
     --data '{"client_id":"クライアントID","client_secret":"クライアントシークレット","audience":"https://example.com/","grant_type":"client_credentials"}'
```

成功すればアクセストークンが取得できます。

```json
{
  "access_token": "eyJhbG...............",
  "token_type": "Bearer"
}
```

取得したアクセストークンはJWT形式になっています。
[jwt.io](https://jwt.io/)というサイトでアクセストークンをデコードして中に含まれている情報をみることができるので試してみてください。
アクセストークンの中に`aud`プロパティに`Identifier`の値が入っていることと`scope`プロパティに`Permission`の内容が含まれていることが確認できます。

https://jwt.io/

# WebAPIを呼び出す

WebAPIをCLIで呼び出してみます。
WebAPIのサンプルアプリケーションには`public`、`private`、`private-scoped`の3つのAPIが定義されているのでそれぞれ試します。

最初に`public`のAPIを呼び出してみます。
このAPIはアクセストークンがなくても呼び出し可能なのでアクセストークンを使用していません。

```
curl --request GET \
  --url http://localhost:3010/api/public
```

```json
{"message":"Hello from a public endpoint! You don't need to be authenticated to see this."}
```

次に`private`のAPIを呼び出してみます。
このAPIはアクセストークンが必須なので`authorization`ヘッダーにアクセストークンを設定します。
`アクセストークン`には実際のアクセストークンを設定します。

```
curl --request GET \
  --url http://localhost:3010/api/private \
  --header 'authorization: Bearer アクセストークン'
```

```json
{"message":"Hello from a private endpoint! You need to be authenticated to see this."}
```

アクセストークンなしで呼び出すとエラーになります。

```
curl --request GET \
  --url http://localhost:3010/api/private
```
```json
{"message":"No authorization token was found"}
```

最後に`private-scoped`のAPIを呼び出します。
こちらも同様に`アクセストークン`には実際のアクセストークンを設定します。

```
curl --request GET \
  --url http://localhost:3010/api/private-scoped \
  --header 'authorization: Bearer アクセストークン'
```

```json
{"message":"Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this."}
```

`private-scoped`のAPIはアクセストークンの有無だけではなくPermissionを保持しているかまで判断しています。
試しに`Machine to Machine Applications`タブで設定した`read:messages`のPermissionのチェックを外してテストアプリケーションがPermissionを持っていない状態で取得したアクセストークンを使ってAPIを呼び出すと下記のエラーメッセージが返ってきます。

```
Insufficient scope
```

# おわりに
アクセストークンが無いと呼び出せないWebAPIを公式のサンプルアプリケーションを使って用意してみました。
Auth0はログイン機能だけではなくAPIを守る仕組みも提供してくれます。ぜひ活用してみてください。