API GatewayをAuth0で保護する！認証＆認可の実践ハンズオン【AWS SAMで構築】

# はじめに

この記事では、Auth0を使ってAPI Gatewayに認証認可をかける方法をハンズオン形式で紹介します。

最終的に、トークン認証されたAPI Gatewayを構築し、ログインしたユーザーのみがAPIリクエストが通る状態を目指します。

# 成果物

フロントとバックエンドを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/65b1ca68-98e2-46de-87be-42100e09b856.png)

## 機能

モリモリです！

Auth0でとりあえずやりたいだろう事を詰めました！

【ログイン機能】

- メールアドレスやGoogleアカウントでログイン出来る

【認証・認可】

- いいね数のGET ... 誰でも可能
- いいねのPOST ... ログインしているユーザーのみ可能（認証）
- いいねのDELETE ... 特別な権限のあるユーザーのみ可能（認可）

【ゴールド会員になる・辞める】

- LambdaからAuth0のAPIを叩きユーザーにRoleの追加・削除

# 構成図

![architecture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/ef8d5e44-d26d-4a1c-9766-cd5b45774609.jpeg)


1. クライアント（React）がアプリにログイン
1. Auth0がアクセストークンをクライアントに発行
1. アクセストークンを持ってAPIGatewayにリクエスト
1. LambdaAuthorizerがアクセストークンをチェック
    - Auth0が発行した正しいトークンか？
    - 必要なパーミッションを持ってるか？
1. チェックを通過したリクエストのみLambdaにアクセス

# 前提

下記インストール済みであること

- AWS CLI
    - [公式](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)からインストーラーでインストール可能
- AWS SAM CLI
    - [公式](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/install-sam-cli.html)からインストーラーでインストール可能
- Node.js（私の環境ではv22.14.0）

# 手順

30分 ~ 1時間ぐらいの内容として想定しています。

- DynamoDBのテーブル作成（AWSマネコン）
- Application・API作成（Auth0）
- フロントアプリ（React）のDL
    - ログイン機能
- AWS SAMプロジェクト（API+Lambda）のDL
    - APIの認証・認可
- Roleの追加・削除
    - LambdaでManagementAPIを操作

# :tv: 動画版

動画版も作成しました！

「とりあえず作業を眺めたい」「動画で確認しながら手を動かしたい」と言う方は是非:pray:

<iframe width="560" height="315" src="https://www.youtube.com/embed/8_O4Qa1-eJM?si=b5AwKQ7FR-OEF63z" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# DynamoDBのテーブル作成（AWSマネコン）

## マネコンでDynamoDBに移動

![dynamo_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/8f4b2e8e-4872-487e-8239-0b808622055f.png)

## テーブルの作成

![dynamo_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/633b36e7-86e5-43ca-a8a7-375184cb82ea.png)

- テーブル名
    - counters
- パーティションキー
    - name（文字列）
- ソートキー
    - 未設定
- テーブル設定
    - デフォルト設定

## 項目を探索 => 項目を作成

![dynamo_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/b71d66ef-a67b-44f9-a91d-bb3417b3a06a.png)

## 項目を作成

![dynamo_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/bb4fa7b5-c163-47f3-ba48-fe33bff118a2.png)

- name
    - like_counter
- count
    - 0（数値 **※文字列にしないよう注意**）

---

✅ ここまででAWS・マネコンでの設定は完了です！

# Application・API作成（Auth0）

## Applicationの作成

![app_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/336e568c-583c-4c39-bc20-c56ccf80acc4.png)

## + Create Application

![app_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/6f68061a-0ac7-4593-92a4-1be077995e1f.png)

- Name
    - My Auth0 App（他の名前でも大丈夫です）
- Choose an application type
    - **Single Page Web Applications**

## Application URIs

ログイン後、どこにリダイレクトするかなどの設定

### settingsタブに移動

![app_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/c2dbe5e2-b6db-4674-aff3-1b322d809712.png)

### Application URIsまでスクロール

![app_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/3c94fc7a-4fbb-4b84-9904-b4dbb1ee9ea9.png)

#### Allowed Callback URLs

ログイン成功後にリダイレクトさせたいURL

http:\//localhost:5173/

※フロントアプリケーションのURL。Vite + Reactで作る前提

#### Allowed Logout URLs

ログアウト後にリダイレクトさせたいURL

http:\//localhost:5173/

※フロントアプリケーションのURL。Vite + Reactで作る前提

#### Allowed Web Origins

Auth0のAPIにアクセスできるドメインを設定する場所

http:\//localhost:5173/

※フロントアプリケーションのURL。Vite + Reactで作る前提

### Save Changesする

以上URLの設定が出来たら **「Save Changes」** して下さい。


## Apiの作成

![api_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/443db8ef-4470-46af-820f-d01afce0f178.png)

### + Create API

![api_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/b36e9557-a143-4903-92fb-03bea7f0cf19.png)

- Name
    - My Auth0 Api
- Identifier
    - my-auth-api-on-apigateway（※）
- JSON Web Token(JWT)Profile
    - Auth0
- JSON Web Token(JWT)Signing Algorithm
    - RS256

※認証をかけたいAPIのドメインを入れるのが慣例。今回ならばAPIGatewayのドメインを本来なら入れたいが、まだ作っていない、かつ、話を簡単にするために、今回は、適当な文字列を入れています。

### PermissionsタブでAdd a Permission

![api_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/211e0b97-9ecc-426a-8a6f-e0f0904fec5f.png)

- Permission
    - delete:like
- Description
    - いいねを取り消す

↓作成するとList of Permissionsに現れる

![api_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/2bfe178f-c822-42a3-92d5-4671406c7870.png)

### RBAC Settingsを有効にする

#### Settingsタブに移動

![api_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/889c454c-bef3-46c3-ae98-648dbd8897ed.png)

#### RBAC Settingsまでスクロール

![api_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/4788c2db-f6bd-4201-b2d7-9fe9c8ee7acf.png)

以下２つをオンにする。

- Enable RBAC
- Add Permissions in the Access Token

#### Save

上記2つをオンにしたら忘れずにSaveする。

---

✅ ここまででAuth0側の設定は完了です！ログイン機能の準備が出来ました！

# フロントアプリ（React）のDL

## GithubからコードをDL

![gh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/e8e167f2-a82f-4ecd-b92c-5a965e0a196e.png)

下記よりDLしてください:point_down:（ブランチ名:3_express_api_permission_check）

https://github.com/JiroYoyogi/auth0-app/tree/3_express_api_permission_check

## Auth.jsxを編集

下記の空文字の部分を埋めたいです。

```jsx
    <Auth0Provider
      domain=""
      clientId=""
      authorizationParams={{
        redirect_uri: window.location.origin,
        audience: "",
      }}
    >
      <App />
    </Auth0Provider>
```

### domeinとclientId

- Applications => My Auth0 Appを選択
- Settingsタブに移動

下図のDomainとClient IDをコピーしてコードにペースト

![react_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/6dd058e1-2ea9-4a82-834a-09d3a464f29b.png)

### audience

- APIs

My Auth0 Apiの「API Audience」の値をコピーしてAuth.jsxのaudienceにペースト

![react_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/baaba002-0487-4fae-a99b-b28c08c3a68c.png)

### 貼り付けた後のイメージ...

私の場合は下のようにセットしました。

```jsx
    <Auth0Provider
      domain="dev-kq7mz3sahxev7c5d.us.auth0.com"
      clientId="TMBk4DmTl8CQP2OLudi5khG2zU6hJoCz"
      authorizationParams={{
        redirect_uri: window.location.origin,
        audience: "my-auth-api-on-apigateway",
      }}
    >
      <App />
    </Auth0Provider>
```

## アプリにログインしてみる

ここまででログイン機能は出来てるので試してみましょう。

### ライブラリをインストール

```
npm install
```

### アプリを立ち上げる

```
npm run dev
```

下記のようにhttp://localhost:5173/ でアプリが立ち上がります。

```
  VITE v6.2.2  ready in 304 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
```

http://localhost:5173/ に飛ぶと下記画面が表示されます。

![react_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/bcdb1800-5ca4-4eb3-b163-e4036bfa272c.png)

### ログインする

![react_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/963ad285-e429-4217-a642-ee1fd9f44055.png)

### Continue with Google

![react_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/324f9e5e-5a41-40d7-b0d8-623f219b18f2.png)

### アカウントと紐付けを許可

![react_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/16d101c3-5141-436f-8dcb-f6b862ac1156.png)

### ログイン出来た！

![react_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/e6fb66c9-3672-4fde-86e0-3cf54edc5972.png)

しかし、「いいね」しようとしても何も起きません。

そりゃそう。

まだ、「いいね」リクエストの送信先のAPIを作成していない。

### ログインするだけならばこれでOK

ログインするだけならばこれでOK！

ここからAPIGatewayをその認証と合わせて作成して行きます！

# AWS SAMプロジェクト（API+Lambda）のDL

完成済みのコードを用意しています。

DLする形で進めます。また、コードには、いいねを増やすなどの関数などが含まれますが、その中身には本稿の目的から逸れるため触れません。

## 手順

- GithubからコードをDL
- sam build
- sam deploy
- フロントからAPIリクエスト
- Authorizer関数は何をしているか？

## GithubからコードをDL

![gh_fix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/d49a4573-c0b1-4be5-8c54-f7fdc01964dd.png)

下記よりDLしてください:point_down:（ブランチ名:qiita）

https://github.com/JiroYoyogi/auth0-api-gateway/tree/qiita

## sam build

コードの変更は特にありません。一旦、そのままビルド＆デプロイします。

動くものを確認したいです。

```
sam build
```

（私の作業環境でビルドしてるイメージ）

![sam_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/40e7edd5-29fd-4be4-a5a7-a92749ff112f.png)

## sam deploy

```
sam deploy --guided
```

色々聞かれるので答えて行く。

- Stack Name
    - auth0-api-gateway-main
- AWS Region
    - ap-northeast-1
- Parameter DynamoTableName
    - counters
- Parameter DynamoKeyName
    - like_counter
- Parameter Auth0Domain
    - My Auth0 AppのDomain（※）
- Parameter OIDCClientId
    - My Auth0 AppのClient ID（※）

あとはEnterでOKです。

※下図のDomainとClient ID
![react_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/5458fcd6-f545-4d77-b4ea-04a9a85e534c.png)


（私の作業環境でのイメージ）

```
Stack Name [sam-app]: auth0-api-gateway-main
AWS Region [us-east-1]: ap-northeast-1
Parameter DynamoTableName []: counters
Parameter DynamoKeyName []: like_counter
Parameter Auth0Domain []: dev-kq7mz3sahxev7c5d.us.auth0.com
Parameter OIDCClientId []: TMBk4DmTl8CQP2OLudi5khG2zU6hJoCz
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: N
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: Y
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: N
Save arguments to configuration file [Y/n]: Y
SAM configuration file [samconfig.toml]: samconfig.toml
SAM configuration environment [default]: default
```

デプロイ成功すると下記のように成功のメッセージとAPIのURLが表示されます。

![sam_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/0d2adbee-8724-4354-81ea-25d6aca3d6c9.png)

---

✅ APIとLambdaをデプロイ出来ました！APIに認証がかかってるかのチェックは「フロントからAPIを叩く」のあとで行います。


## フロントからAPIを叩く

### APIのURLをメモ

上記作成されたAPIのURLをメモしておきます。

### フロントのコードを編集

- src/App.jsxの７行目ぐらい

API_DOMAINをAPIのURL（https:\//APIドメイン/dev）で置き換えて下さい。

```jsx
// イメージ
const API_DOMAIN = "https://zy0t5ravoe.execute-api.ap-northeast-1.amazonaws.com/dev";
```

## いいね数を取得する・いいねする

フロントアプリをリロードして「いいね」するとカウントアップされます。

Lambdaを起動するため、ラグがあるかもしれませんが、気にしないで下さい。

![sam_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/063aca39-c75f-4e54-95fa-c7179e7f43f2.png)

DynamoDBを確認すると、しっかり増えてるのが確認出来ると思います。

![sam_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/30575c50-c384-4fa7-a38b-cd4f35a6fd31.png)

## APIに認証がかかってるかチェックしたい

Consoleに出力されたアクセストークンを出しています。

![access_token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/67f95ef5-d185-4094-a3cc-304cbd2bdd5a.png)

このアクセストークンを使ってCurlする事dチェックします。




アクセストークンとAPIドメインを自分のものに置き換えて試してみて下さい。

```
curl -i -X POST \
  -H "Authorization: Bearer アクセストークン" \
  https://APIドメイン/dev/likes
```

参考までに私は以下のように実行しました。

（成功パターン）

```
curl -i -X POST \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1QZDNZYXpwR3VwSk9nVjlRMVpGWiJ9.eyJpc3MiOiJodHRwczovL2Rldi1rcTdtejNzYWh4ZXY3YzVkLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODc4ODkzNDY1NzQwNjM4NTgwMiIsImF1ZCI6WyJteS1hdXRoLWFwaS1vbi1hcGlnYXRld2F5IiwiaHR0cHM6Ly9kZXYta3E3bXozc2FoeGV2N2M1ZC51cy5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzQ1NjQ1NjM2LCJleHAiOjE3NDU3MzIwMzYsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJUTUJrNERtVGw4Q1FQMk9MdWRpNWtoRzJ6VTZoSm9DeiIsInBlcm1pc3Npb25zIjpbXX0.ivevVtk6zAbepAzNhKcz8SH1QphHfZUg-UZGYZTVKSkCpMdRM7B9RJqE-J7cwFiBguOWr9ivSR8_3bNFe8Ckmps1XLLV0GNWAQJ-KMna70dL4rr2IWaD1lAECFzUFpmbfImy_vAJt5AmKUQHcfhic8WnvsY0G4KtLlVYw-zK8RBr3ydiEort_vFlss6gOqb2ssYzo7ya1OxFADvDZ2jJmNmd2ZAVUDwwthBwELQyh-fwQTT7A9g72uSnqchsq47UjbWg4icQFBPnKtJIBt175FpRP-8L_mc3i6XcCUXhFIKxKipf572ccqbTKqbSPbNgsx7nWf0UcNQXoUkwXlmAlQ" \
  https://zy0t5ravoe.execute-api.ap-northeast-1.amazonaws.com/dev/likes

HTTP/2 200 
content-type: application/json
content-length: 12
date: Sat, 26 Apr 2025 05:34:36 GMT
x-amzn-trace-id: Root=1-680c7069-4c81b9415bae3ce3152f73c9;Parent=0d08df49e5a1b327;Sampled=0;Lineage=2:53d5a100:0
x-amzn-requestid: a1521190-cbed-46c9-b1de-ab417f7a44f8
access-control-allow-origin: *
x-amz-apigw-id: JnaAiHF1NjMEmHQ=
access-control-allow-methods: POST
x-cache: Miss from cloudfront
via: 1.1 abe247adaab2cff314bfe6787604d9ea.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P3
x-amz-cf-id: gdm6rKn8nM1nn1HHUPGR5HLjNgJmOGMNb67iqsXODhrya6db1fjI2w==

{"count":11}       
```

（失敗パターン）

トークンを１文字変更してみる。

```
curl -i -X POST \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1QZDNZYXpwR3VwSk9nVjlRMVpGWiJ9.eyJpc3MiOiJodHRwczovL2Rldi1rcTdtejNzYWh4ZXY3YzVkLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODc4ODkzNDY1NzQwNjM4NTgwMiIsImF1ZCI6WyJteS1hdXRoLWFwaS1vbi1hcGlnYXRld2F5IiwiaHR0cHM6Ly9kZXYta3E3bXozc2FoeGV2N2M1ZC51cy5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzQ1NjQ1NjM2LCJleHAiOjE3NDU3MzIwMzYsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJUTUJrNERtVGw4Q1FQMk9MdWRpNWtoRzJ6VTZoSm9DeiIsInBlcm1pc3Npb25zIjpbXX0.ivevVtk6zAbepAzNhKcz8SH1QphHfZUg-UZGYZTVKSkCpMdRM7B9RJqE-J7cwFiBguOWr9ivSR8_3bNFe8Ckmps1XLLV0GNWAQJ-KMna70dL4rr2IWaD1lAECFzUFpmbfImy_vAJt5AmKUQHcfhic8WnvsY0G4KtLlVYw-zK8RBr3ydiEort_vFlss6gOqb2ssYzo7ya1OxFADvDZ2jJmNmd2ZAVUDwwthBwELQyh-fwQTT7A9g72uSnqchsq47UjbWg4icQFBPnKtJIBt175FpRP-8L_mc3i6XcCUXhFIKxKipf572ccqbTKqbSPbNgsx7nWf0UcNQXoUkwXlmAl" \ 
  https://zy0t5ravoe.execute-api.ap-northeast-1.amazonaws.com/dev/likes
HTTP/2 403 
content-type: application/json
content-length: 82
date: Sat, 26 Apr 2025 05:34:58 GMT
x-amz-apigw-id: JnaEXE7WNjMEIMg=
x-amzn-requestid: 3d2f8cfd-f445-4a6f-ae97-82437585f23c
x-amzn-errortype: AccessDeniedException
x-cache: Error from cloudfront
via: 1.1 880e379e1a56b21426587959131bb422.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P3
x-amz-cf-id: 0jkDmaSYQ6QFXEOld4iuBWUPUUFOUDpfkEmB3_S1jG357uIvDIQQaw==

{"Message":"User is not authorized to access this resource with an explicit deny"}
```

---

✅ APIに認証がかかってることを確認出来ました！続いて認可の話に進んで行きます！

## いいねを取り消したい（数を減らしたい） 

フロントの画面に戻ります。

現状だと:broken_heart:を押しても数が減りません。

開発者ツールを開くとエラーが出てるのを確認出来ると思います。

![gold_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/2abc9c5e-8a3b-471e-855a-db84f716f876.png)

なぜか？

ログインしてるユーザーに**権限**が無いからです。

## Authorizer関数は何をしているか？

先に、公式ブログで実装を紹介してくれています。本稿のAuthorizerの実装は、このブログを元にして自分なりに噛み砕いたもとなっています。

https://auth0.com/blog/protecting-rest-apis-behind-aws-api-gateway/

まず、リクエストを通すか通さないかは以下で決まります。

- Authorizer関数はIAMポリシーをレスポンスする
- APIパスのarnに対してAllowポリシーならば許可
- APIパスのarnに対してDenyポリシーならば拒否

### Authorizer関数が何をするか？

アクセストークンを受け取り、トークン検証する。結果に応じてAllow、もしくは、Denyポリシーを返す。

本稿のコード（src/authorizer.mjs）ではトークン検証をどのようにしてるか？

次の式が成り立てばトークンは正しいと言えます。

```
JWTの署名部 ÷ 公開鍵 = JWTのヘッダー + JWTのペイロード × sha256

// ÷とか×はあくまでイメージです。
```

実際のコードで見ると以下です。やってることは次のようなこと。

- トークンをヘッダー・ペイロード・署名部に分割
- 公開鍵を取得
- これらを使って上記の式が成り立つか検証（crypt.verify）

```js:src/authorizer.mjs（抜粋）
  const [headerB64, payloadB64, signatureB64] = token.split(".");
  const header = JSON.parse(base64UrlDecode(headerB64));
  // 使用している鍵のID
  const kid = header.kid;
  // JWKsと照らして公開鍵を特定
  const key = jwks.keys.find((key) => key.kid === kid);
  if (!key) {
    return generatePolicy(methodArn, "Deny");
  }
  // 証明書から公開鍵を作成
  const publicKey = crypto.createPublicKey({
    key: `-----BEGIN CERTIFICATE-----\n${key.x5c[0]}\n-----END CERTIFICATE-----`,
    format: "pem",
  });
  const data = `${headerB64}.${payloadB64}`;
  const signature = Buffer.from(signatureB64, "base64");
  /**
   * 1. data を sha256 でハッシュ
   * 2. signature を publicKey で復号
   * 3. 両方を比較して、一致していれば true
   */
  const sig_valid = crypto.verify(
    // ★デジタル署名の検証を行う関数
    "sha256",
    new TextEncoder().encode(data), // ハッシュ対象のデータ、文字列 → バイト列
    publicKey, // 検証に使う公開鍵
    signature // 検証対象の署名
  );
```

crypto.verifyの結果がtrueならば、さらにトークン内のpermissionのチェックなどに進みます。

### いいねの取り消しが何故出来なかったか？

いいねの取り消しのリクエストだった場合は、以下のように、permissionsにdelete:likeがあるかどうかのチェックを入れてるからです。

そして、現状ではdelete:likeがトークンの中に入っていません。

```js:src/authorizer.mjs（抜粋）
  // いいねの取り消しの場合はpermissionsをチェックする
  if (resourcePath.startsWith("like") && httpMethod === "DELETE") {
    if(!payload["permissions"].includes("delete:like")) {
      return generatePolicy(methodArn, "Deny");
    }
  }
```

どうしたら、トークンにdelete:likeを含められるかと言う事をここからして行きます！

# Role（権限）の操作

## 方針

- Roleを作成する
- RoleにPermissionを紐付ける
- ユーザーにRoleをくっ付ける

## Roleを作成する

### User management => Roles

![role_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/96d98d24-da92-4b4e-9d7c-64b66720955b.png)

### + Create Role

![role_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/ddfb8267-6f6c-4fc2-a9ef-041fb4971cc6.png)

- Name
    - gold_member
- Description
    - ゴールド会員

## RoleにPermissionを紐付ける

### Permissinsタブを開く

![role_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/3f72bb8c-35f5-4d63-bd09-ef0a64d9a9c8.png)

### Add Permissions

![role_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/0adf1195-e8a1-435e-a3d3-a8ea4fc7de01.png)

- My Auth0 Apiを選ぶ
- delete:likeにチェック
- Add Permissionsボタンをクリック

## ユーザーにRoleをくっ付ける

### User Management => Users => ログインしてるユーザーを選択

Rolesタブを開く

![role_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/e5d288e3-3f76-4b9d-9af9-9d558a467a2c.png)

### Assign Roles

gold_memberを選んで「Assign」する

![role_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/34d085ea-cca1-4d37-a727-1718e88e1a63.png)

## いいねを取り消す

アプリに戻り、リロード、アクセストークンを取得し直す。

その上で、:broken_heart:すると、今度はいいねの数が減っていきます。

![role_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/ebb0c384-87fa-4362-b8fe-f49e211bffbc.png)

## アクセストークンの中身を確認

コンソールにアクセストークンを出力してます。

delete:likeのPermissionがある事を確認出来ます。

![token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/0f66c295-b49c-4cbb-9dfe-83d9d18d1efb.png)

もし「アクセストークンにPermissionの項目が無いぞ」と言う方は「Enable RBAC」が有効になってるかチェックして下さい。

My Auth0 APiを作成した際に下記のように設定した部分です。

![api_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/bec0e476-4dc1-4015-b410-ac7c1dd0a661.png)

---

✅ ここまででAPIGatewayをAuth0で認証認可する実装が出来ました！

ここからは、先ほど手動で行ったユーザーへの権限の追加をLambdaから出来るようにして行きます。

# LambdaからRole（権限）の追加・削除する

先ほどアカウント画面で直接ユーザーにロールを追加しました。

これをLambdaで実行出来るようにしていきます。

LambdaからManagement APIを操作することで可能です。

## Management APIとは？

Auth0のアカウントを作成るとデフォルトである「Auth0 Management API（System API）」を使う事で、アカウント画面で行うような操作をAPIリクエストで行えます。

ユーザーにRoleを追加するという事も可能です。

![m2m_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/0d1c0302-1443-4ea8-9b33-f6fe2e7dcc10.png)

## Management APIのドキュメント

本稿で使用するAPI操作のドキュメントは下記です。

- [Management APIアクセストークンの取得](https://auth0.com/docs/ja-jp/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production)
- [ユーザーにロールを追加](https://auth0.com/docs/api/management/v2/users/post-user-roles)
- [ユーザーからロールを削除](https://auth0.com/docs/api/management/v2/users/delete-user-roles)

## Management APIのアクセストークンを取得したい

Auth0 Management API（System API）を操作するためには、専用のアクセストークンが必要です。

そのアクセストークンをLambda内で取得するには「M2M Application」を作る必要があります。

M2M ApplicationはSystem APIのトークン発行するプログラムみたいなイメージです。

流れとしては以下の感じです。

M2M Applicationを作成
↓
Lambda内でM2M Application使用しアクセストークンを取得
↓
Auth0 Management API（System API）を操作

## M2M Applicationを作成

![m2m_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/b1bc2ba9-658b-4407-9f6a-7b5862a3a909.png)

### + Create Application

![m2m_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/0e9a674c-b28b-489a-bcd2-8a0c7c73bb65.png)

- Name
    - My Auth0 M2m App（※他の名前でもOK）
- Choose an application type
    - **Machine to Machine Applications** 

### APIとPermissionsの紐付け

![m2m_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/1ca4e9b4-6f25-4918-ac97-7e71dfa98304.png)

- 「Auth0 Management API」を選択
- 「update:users」にチェック

LambdaからMy Auth0 M2m Appを使うとPermissonsに「update:users」が入ったアクセストークンを取得出来るようになります。

### コードの変更

AWS SAMプロジェクトのtemplate.yamlに２箇所追記します。

### Parametersを置き換える

元々DLしたものに３つ追加しています。置き換えて下さい。

```yaml
Parameters:
  DynamoTableName:
    Type: String
  DynamoKeyName:
    Type: String
  Auth0Domain:
    Type: String
  OIDCClientId: # React => API
    Type: String
  GoldMemberRoleId:
    Type: String
  M2MClientId: # Lambda => システムAPI
    Type: String
  M2MClientSecret:
    Type: String
```

### Lambdaの環境変数追加

上で受け取ったパラメーターをLambda内で使えるように環境変数を追加しています。該当箇所を置き換えて下さい。

```yaml
Globals:
  Function:
    Timeout: 30
    Environment:
      Variables:
        DYNAMO_TABLE_NAME: !Ref DynamoTableName
        DYNAMO_KEY_NAME: !Ref DynamoKeyName
        AUTH0_DOMAIN: !Ref Auth0Domain
        OIDC_CLIENT_ID: !Ref OIDCClientId
        GOLD_MEMBER_ROLE_ID: !Ref GoldMemberRoleId
        M2M_CLIENT_ID: !Ref M2MClientId
        M2M_CLIENT_SECRET: !Ref M2MClientSecret
```

### ビルド＆デプロイ

下記コマンドでビルドします。

```
sam build
```

下記コマンドでデプロイします。

```
sam deploy --guided
```

新しく追加したパラーメターについて聞かれます。

- GoldMemberRoleId

場所は下図を参考にして下さい。

![m2m_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/b087d0c6-e0c7-4ce7-87f4-092a671e3a66.png)

- M2MClientId
- M2MClientSecret

My Auth0 M2M AppのClient IDとClient Secretを設定して下さい。

![m2m_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/c78adc6d-c7ea-4b2a-bb24-91d479abb559.png)

参考までに私の場合は以下のように設定しました。

```
Stack Name [auth0-api-gateway-main]: 
AWS Region [ap-northeast-1]: 
Parameter DynamoTableName [counters]: 
Parameter DynamoKeyName [like_counter]: 
Parameter Auth0Domain [dev-kq7mz3sahxev7c5d.us.auth0.com]: 
Parameter OIDCClientId [TMBk4DmTl8CQP2OLudi5khG2zU6hJoCz]: 
Parameter GoldMemberRoleId []: rol_kWCbvh1FQcU8RiAK
Parameter M2MClientId []: S2p4XrphEXwIUaugwe1lqe1idnqA4JRb
Parameter M2MClientSecret []: lOO-H9RCpjVJxRGeISYM9pui3o6tsbNKQfb15Ls2KCRAVvrrGk5_oW_szNP3Ot4k
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: 
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: 
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: 
Save arguments to configuration file [Y/n]: 
SAM configuration file [samconfig.toml]: 
SAM configuration environment [default]: 
```

## 動作確認

フロントアプリを立ち上げて「ゴールド会員を止める」します。

画面は変わりませんが、consoleを見るとリクエスト成功のログを確認出来ます。

![fin_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/e92db1ad-96f4-472a-90c8-5e071d081777.png)

**リロード**すると「ゴールド会員になる」に変わっています。

![fin_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/6645d096-6aa0-47e0-811f-cf98889f8115.png)

Auth0でユーザーのRoleを確認すると、先ほど手動で付けたRoleが無くなっていることを確認出来ます。

![fin_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/1cdc0db1-95d9-4f26-87c5-3a37278b23f4.png)

フロントの画面で「ゴールド会員になる」と、Roleが再度現れます。

実装は以上になります。

# おわりに

ここまで書いていながら、私はAuth0をまだ触り始めたばかりです。

とは言え、一旦、自分なりにやりたいと思っていたことが出来る状態になったかな？と思うので、アウトプットとして残したい。そんなモチベーションで書きました！

最後に再喝になりますが、本記事の内容を実演したハンズオン動画も作成しております。

どの画面を操作したら良いか分からないなど、迷ったら記事とあわせてご活用ください。

<iframe width="560" height="315" src="https://www.youtube.com/embed/8_O4Qa1-eJM?si=b5AwKQ7FR-OEF63z" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>



ここまで読んでいただきありがとうございましたー！