JavascriptでAuth0の認可を実装してみた

# はじめに

認証認可のシステムを社内で実装する手間を避けるために、SaaSを使用することも多いかと思います。この手のツールの中でもyoutubeに日本語関連の動画が少なかったAuth0について実際にコードから使用してみようというのが今回の記事になります。

対象読者は認証認可サービスに興味がある人なら誰でもOKです。

まず前提知識としてこれらの認証認可のサービスがどんなことをやるのかを頭に入れておく必要があります。私は以下の記事を一読し、似た単語などをGoogleで調べるなどして勉強してみました。

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe

ただ、ここまでは実装方式のホワイトペーパーを見ているにすぎないため、実際のサービスであるAuth0を叩いてみましょう。

今回やるのは以下です。

大目標「フロントアプリから適当なユーザでログインを行い、そのユーザの権限を使ってAPIへの問い合わせを実行する」

- Auth0での　Application/APIの設定,ユーザの登録と権限設定
- フロントエンドからAuth0にTokenを問い合わせてみる
- APIサーバに対してAuth0からもらったToken付きのリクエストを送ってみる

# 準備

## auth0のアカウント作成

以下のサイトからアカウントを作成することができます。必要なのはメールアドレスくらいなので、とりあえず作ってみましょう。トライアル期間もしばらくあります。

https://auth0.com/jp

作成の際に指定したテナントというのが会社の部署くらいに考えておいてもらえるといいかと思います。会社の部署はたくさんプロジェクトを抱えていると思いますので、それを次の項目で設定していきます。

## auth0/APIの設定

TOP画面は以下です。

![スクリーンショット 2022-08-16 0.54.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134021/9582b23b-2b2b-e779-7693-3456ab7213f0.png)

まずは「API」を先に設定するのがいいかと思います。このAPIというのはそのままWebAPIとかのAPIを想像してもらえれてばと思います。API AudienceがこのAPIを識別するIDになります。慣例としてアクセスフルパスを指定するそうです。今回は「testapp」というものを作ってみました。

APIの設定でまず確認したいのは以下です。

![api_rbac.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134021/0ab1ff94-c3e4-e8a8-ff10-e27e503e769c.png)

Enable RBACにチェックを入れています。ロールベースでアクセスコントロールをするという考え方です。あーるばっくと読むそうです。これと「Add Permissions in the access token」にチェックをいれることで、AccessTokenを検証した際に許可されているRoleを確認することができます。

またPermissionsのタブから
read:messages　を追加しておきます。この辺は感覚的に操作できるかと思います。

![スクリーンショット 2022-08-16 1.26.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134021/cde541db-5ce5-ab15-6fa6-d2e4cc4462c5.png)

ここまででこのAPIに対するread:messagesという権限を作りました。上で説明したRBACや認可処理（ユーザやプログラムに許可を出す処理）で実際に許可されるのはロールという単位なので、直接この権限をユーザにひもづけることはありません。必ずロールを挟む必要があります。

## auth0/Roleの設定

User Management > Roles からロールを作成していきます。
作成のロール名はなんでもいいです。次に Add Permissionsから上で指定した権限を追加します。

![スクリーンショット 2022-08-16 1.33.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134021/c25e5471-1f92-61ba-ba09-c962e42d10b1.png)

前述した通り、RBACの場合にはユーザにダイレクトで権限を付与するのではなく、ユーザにロールを、ロールに権限を...という手順で権限を付与します。試しに適当に作ったユーザに上のRoleを割り当ててみました。これで適当に作ったユーザはread権限をもつことができます。**このステップで適当にユーザを作り、Roleを割り当てておいてください。**


## auth0/Applicationの設定

さて、このAPIを作成していると、Applicationにtestapp (Test Application)というアプリケーションができているかと思います。画像は割愛しますが、settingsの中で以下の項目をいじります。

Token Endpoint Authentication Methodの項目 = None
Allowed Origins (CORS) = 問い合わせ元のフロントページを返却する Webサーバのドメイン名

二つ目について、CORSエラーを避けるため、Auth0のサービスを叩きにくるフロントエンドコンテンツを提供しているサーバのドメイン名を登録しておきます（日本語が複雑ですみません）。今回はフロントエンド用にはWebPackで簡易Webサーバをたてるので、webpackで返却されたIPアドレスを指定しました。


また、APIsのタブからtestappのapiにチェックがついているのを確認します。また、該当箇所をクリックしpermissionsも許可しておきます。

![スクリーンショット 2022-08-16 1.22.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134021/69d68cb9-afe9-9d23-5467-440d9f09f7ab.png)

Auth0内では　「複数のAPI vs 複数のApplication」を実現するため、Application側でチェックをいれてあげます。今回の手順外で別途作ったAPIについても、このステップでtestappから許可することが可能のようです。

# 実装

## フロントページ側
webpack+生JSでやっていきたいと思います。webpackのデフォルト設定をしていきますが、これは本記事では割愛します。コードを書けるところまで飛ばします。

準備が整ったら以下のgithubを参考にして npm install auth0-js をしていきます。

https://github.com/auth0/auth0.js/

以下、htmlから読み込んだjavascriptのコードです。

```auth_test.js
import auth0 from 'auth0-js';

var myauth = new auth0.WebAuth({
    domain: 'テナント名',
    clientID: 'test appのクライアントID'
});

myauth.client.login(
    {
        realm: 'Username-Password-Authentication', 
        username: '適当に作ったユーザの名前',
        password: '適当に作ったユーザのパスワード',
        audience: 'APIを作成するときに指定したaudience id',
        onRedirecting: function (done) {
            // Your custom code here
            done();
        }
    },
    function (err, authResult) {
        // Auth tokens in the result or an error
        console.log("error", err)
        console.log(authResult)
        console.log(authResult.accessToken)
        console.log(authResult.idToken)
        myauth.client.userInfo(authResult.accessToken, function (err, user) {
            // Now you have the user's information
            console.log("user:", user)
        });
    }
);

```

authResultに結果が格納されるため、コンソールに出力しています。webpack-serverを起動して、ブラウザから叩くと、accessTokenとかuserInfoが見れているかと思います。

ここで、一点つまったのですが、以下の部分を掲載するとIdTokenがとれなくなります。

```
        audience: 'APIを作成するときに指定したaudience id',
```

これは正常な仕様のようです。また逆に、この部分をコメントアウトすると、JWTをデコードした際に**ペイロードの値が取得できず、不正な形式になっている**ことがわかりました。

検証には以下のサイトを使ってみました。このサイトでAccessTokenをコピペして貼り付けてみたところ、ペイロード部分に値が入っていないことがわかりました。

https://jwt.io/

Auth0ではこのオーディエンスキーを指定し取得した認可トークン（AccessToken）をAPIの認可に用いる使用を想定しているようです。フロント側からのAuth0への問い合わせは以上とします。
**本来はこの後、取得したAccessTokenを利用してサーバサイドへの問い合わせ部分を実装するつもりでしたが、なぜかここで力尽きたので、執筆段階ではここまでで割愛します。**

## APIサーバ側
サーバ側はAPIリクエストの際に、header部でbearerにAccessTokenを指定されたリクエストをうけることを前提に構築します。とはいえ、この辺りはAuth0の公式Docにのっています。今回はサーバはPythonのFlaskを選択してサクッと構築します。(ほぼ丸々サイトのコピペなのでコードは割愛)

https://auth0.com/docs/quickstart/backend/python/interactive

プログラム中ではapplicationで確認することができるドメイン名を与えることで、Auth0に公開鍵を取得しにいき、accessTokenが正しいものか検証する仕組みを実装しています。また、特定のAPIアクセス時にはpermissionsまでチェックし、該当のAccessTokenが権限をもっているかを確認しています。

前述したとおり、Auth0はあくまでユーザにロールを通して権限のフラグをつけるだけしかしないため、その権限のフラグを持っているユーザの振る舞いをきめるのはAPIサーバ側のプログラムということになります。このサーバサイト実装のためのUI提供までがAuth0のサービスということになります。

実際に叩いてみます。フロント実装ではコンソールにはくまでしかしていませんので、コマンドラインから

```CMD

curl --request GET \
  --url APIパス \
  --header 'authorization: Bearer アクセストークン'

```

ブラウザのコンソールよりフロント側の実装で取得できたAccessTokenをコピペして、上のコマンドを叩いてみると、無事APIがたたけているのがわかるかと思います。


# おわりに

Auth0について実際にアプリ画面で設定を行い、コードから叩いてみました。ユーザに対してAPIへの問い合わせをAuth0により許可するという一連の流れが実施できたと思います。日本語のドキュメント類もまあまあ充実しているかなといった印象でした。

- Auth0での　Application/APIの設定,ユーザの登録と権限設定
**→APIの設定、権限の発行、その権限を使用したロールの作成、ロールのユーザへの割り当て などを行いました。**

- フロントエンドからAuth0にTokenを問い合わせてみる
**→Javascriptにより実装しました。今回はjavascript内でのAPIへの問い合わせまでは実装していません。**

- APIサーバに対してAuth0からもらったToken付きのリクエストを送ってみる
**→PythonによりAPIサーバを実装しました。AccessTokenの検証まで実施しました。**

実際にサービス化するときにはコールバックやログアウト処理、ページ遷移など複雑な設定/実装も必要なので、本当に最小限しかやっていません。流れは掴めたかと思いますので、これをもとに知識面と実装力の２面で理解を深めていければと思います。