API Gateway Lambda オーソライザーを試してみた

# 背景・目的
以前、API Gateway のオーソライザー ＋ Cognitoを試してみました。今回はAuth0とAPI Gateway Lambda オーソライザーを試してみます。

https://qiita.com/zumax/items/2d7eb1ffa49441eb988a

# まとめ
下記に特徴をまとめます。

|特徴|説明|
|:--|:--|
|Lambdaオーソライザーとは|APIへのアクセスを制御するために使用する|
|挙動|1. クライアントがAPIメソッドをリクエストすると、API GatewayはLambdaオーソライザーを呼び出す<BR><BR>2.Lambdaオーソライザーは、発信者のIDを入力として受取、IAMポリシーを出力として返す|
|カスタム認証スキームの種類|・リクエストパラメータ<BR>・OAuth<BR>・SAML<BR>・bearer token|
|Lambda オーソライザーのタイプ|・リクエストパラメータベースのLambdaオーソライザー（REQUEST オーソライザー）<BR>・トークンベースのLambdaオーソライザー（TOKEN オーソライザー）|
|JWT|JSON Web Token<BR><BR>JWTはサイズが比較的小さく、URL、POSTパラメタ、HTTPヘッダーから送信でき、迅速に転送される<BR><BR>JWTにはエンティティに必要な情報がすべて含まれる。DBを複数回クエリする必要はない<BR><BR>JWTの受信者は、トークンの検証のためにサーバを呼び出す必要はない|
|SWT、SAMLと比較したときのJWTの利点|・コンパクト<BR>・安全<BR>・一般的<BR>・処理が簡単|
|署名アルゴリズム|アプリやAPI用に発行されたトークンに署名するために使用されるアルゴリズム<BR>署名はJWTの一部である<BR>トークンの送信者が送信者本人であることを確認し、メッセージが途中で改善されていないことを確認するために使用する|
|使用できる署名アルゴリズム|・RS256<BR>・HS256<BR>・PS256|
|Bearer token|・利用可否がトークンの所有のみで決定される<BR>・署名なしトークン<BR>・切符に例えられる（持っていれば使える）|
|PoPトークン|・Proof-of-possession token<BR>・所有 + トークン権利を所有することの証明が必要<BR>・国際線飛行機チケットに例えられる(チケットだけではなく、パスポートが必要)|

# 概要
下記のドキュメントを基に整理します。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html

> Lambda オーソライザー (以前はカスタムオーソライザーと呼ばれていました) は、API へのアクセスを制御するために使用します。クライアントが API の メソッドをリクエストすると、API Gateway は Lambda オーソライザーを呼び出します。Lambda オーソライザーは、発信者の ID を入力として受け取り、IAM ポリシーを出力として返します。

- Lambdaオーソライザーは、APIへのアクセスを制御するために使用する
- クライアントがAPIメソッドをリクエストすると、API GatewayはLambdaオーソライザーを呼び出す
- Lambdaオーソライザーは、発信者のIDを入力として受取、IAMポリシーを出力として返す

> Lambda オーソライザーを使用して、カスタム認証スキームを実装します。スキームでは、リクエストパラメータを使用して、発信者のアイデンティティを判断したり、OAuth や SAML などのベアラートークン認証戦略を使用したりできます。Lambda オーソライザーは、API Gateway REST API コンソール、AWS CLI、または AWS SDK を使用して作成します。

- Lambdaオーソライザーによりカスタム認証スキームを実装する。下記の認証戦略がある
    - リクエストパラメータ
    - OAuth
    - SAML
    - bearer token

## Lambda オーソライザーの認証ワークフロー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6190d4c0-f857-738f-c660-dabb4ec87f75.png)

API Gateway Lambda 認証ワークフロー
1. クライアントは、API Gateway のメソッドを呼び出し、bearer tokenまたは、リクエストパラメータを渡す
1. API Gatewayは、メソッドリクエストがLambdaオーソライザーで設定されているか確認する。存在する場合は、API GatewayはLambda関数を呼び出す
1. Lambda関数は発信者を認証する関数は下記の方法で認証可能
    - OAuthプロバイダーを呼び出し、OAuthアクセストークンを取得
    - SAMLプロバイダーを呼び出し、SAMLアサーションを取得
    - リクエストパラメータに基づき、IAMポリシーを生成
    - データベースから認証情報を取得
1. Lambda関数は、IAMポリシーとプリンシパル識別子を返す
1. API GatewayはIAMポリシーを評価する
    - アクセスが拒否された場合、403 ACCESS_DEINIEDなどのHTTPステータスを返す
    - アクセスが許可された場合、API Gatewayはメソッドを呼び出す
        - 認可のCASHを有効化すると、API Gatewayでポリシーがキャッシュされるので、Lambdaオーソライザー関数は再度呼び出されない


## Lambda オーソライザーのタイプの選択

Lambdaオーソライザーは、二種類ある

- リクエストパラメータベースのLambdaオーソライザー（REQUEST オーソライザー）
    - 発信者 ID をヘッダー、クエリ文字列パラメータ、stageVariables、$context 変数の組み合わせとして受け取る
    - REQUEST オーソライザーを使用して、$context.path や $context.httpMethod コンテキスト変数など、複数の ID ソースからの情報に基づいてきめ細かなポリシーを作成　できる
    - REQUEST オーソライザーの認可のキャッシュを有効にすると、API Gateway は、指定されたすべての ID ソースがリクエスト内に存在することを確認できる
- トークンベースのLambdaオーソライザー（TOKEN オーソライザー）
    - JSON ウェブトークン (JWT) や OAuth トークンなどのベアラートークンで発信者 ID を受け取る
    - 認可のキャッシュを有効にすると、トークンソースに指定されているヘッダー名がキャッシュキーになる
    - トークン検証を使用して RegEx ステートメントを入力できる。API Gateway は、この式に対して入力トークンの初期検証を実行し、検証が成功すると Lambda オーソライザー関数を呼び出す。これにより API への呼び出しを減らすことができる

## JWT
下記を基に整理します。

https://auth0.com/docs/secure/tokens/json-web-tokens

> JSON web token (JWT), pronounced "jot", is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. Again, JWT is a standard, meaning that all JWTs are tokens, but not all tokens are JWTs.

- JSON Web Token
- ジョットとして発音
- RFCで定義

> Because of its relatively small size, a JWT can be sent through a URL, through a POST parameter, or inside an HTTP header, and it is transmitted quickly. A JWT contains all the required information about an entity to avoid querying a database more than once. The recipient of a JWT also does not need to call a server to validate the token.

- JWTはサイズが比較的小さく、URL、POSTパラメタ、HTTPヘッダーから送信でき、迅速に転送される
- JWTにはエンティティに必要な情報がすべて含まれる。DBを複数回クエリする必要はない
- JWTの受信者は、トークンの検証のためにサーバを呼び出す必要はない

### JWTの利点
> There are benefits to using JWTs when compared to simple web tokens (SWTs) and SAML tokens.

- SWT、SAMLと比較したときのJWTの利点

> - More compact: JSON is less verbose than XML, so when it is encoded, a JWT is smaller than a SAML token. This makes JWT a good choice to be passed in HTML and HTTP environments.

- コンパクト
    - JSONはXMLよりも冗長性が低い、そのためエンコードしたときに、SAMLよりも小さくなる
    - JWTはHTMLやHTTP環境で渡すのに適している

> - More secure: JWTs can use a public/private key pair in the form of an X.509 certificate for signing. A JWT can also be symmetrically signed by a shared secret using the HMAC algorithm. And while SAML tokens can use public/private key pairs like JWT, signing XML with XML Digital Signature without introducing obscure security holes is very difficult when compared to the simplicity of signing JSON. Read more about JWT signing algorithms.

- 安全
    - JWTは、署名にX.509証明書の形式で、Public/Privateキーペアを使用できる
    - JWTは、HMACアルゴリズムを使用して共有シークレットで対称的に署名できる
        - （SAMLトークンも、JWTのようにPublic/Privateキーを使用できるが、XMLデジタル署名をXMLに署名することは、JSONに署名するシンプルさと比較すると、困難）

> - More common: JSON parsers are common in most programming languages because they map directly to objects. Conversely, XML doesn't have a natural document-to-object mapping. This makes it easier to work with JWT than SAML assertions.

- 一般的
    - JSONでは、ほとんどのプログラミング言語で一般的
        - (XMLはオブジェクトへの自然なマッピングはない）
    - SAMLアサーションよりもJWTのほうが扱いやすい

> - Easier to process: JWT is used at internet scale. This means that it is easier to process on users' devices, especially mobile.

- 処理が簡単
    - JWTは、インターネット規模で利用
    - ユーザのデバイス、モバイル、モバイルでの処理が簡単

### JWTを使用する
> JWTs can be used in various ways:

JWTは様々な方法で使用できる。

> - Authentication: When a user successfully logs in using their credentials, an ID token is returned. According to the OpenID Connect (OIDC) specs, an ID token is always a JWT.

- 認証
    - ユーザが認証にクレデンシャルを使用してログインに成功したとき、IDトークンが返される
    - ID　トークンは常にJWT

> - Authorization: Once a user is successfully logged in, an application may request to access routes, services, or resources (e.g., APIs) on behalf of that user. To do so, in every request, it must pass an Access Token, which may be in the form of a JWT. Single Sign-on (SSO) widely uses JWT because of the small overhead of the format, and its ability to easily be used across different domains.

- 認可
    - ユーザがログインに成功すると、アプリケーションはユーザに代わってルート、サービス、リソースへのアクセスをリクエストできる
    - すべてのリクエストをアクセストークンを渡す必要がある。
    - アクセストークンはJWTの形式である必要がある
    - SSOでは、オーバヘッドが小さく、異なるドメイン間で簡単にJWTが広く利用されている

> - Information exchange: JWTs are a good way of securely transmitting information between parties because they can be signed, which means you can be certain that the senders are who they say they are. Additionally, the structure of a JWT allows you to verify that the content hasn't been tampered with.

- 情報交換
    - JWTは署名ができるため、当事者間で情報を安全に送信するのに適した方法
        - 送信者が本人であることを確認できる
    - JWTの構造で改ざんされていないことを確認できる

### JWT のセキュリティ
> The information contained within the JSON object can be verified and trusted because it is digitally signed. Although JWTs can also be encrypted to provide secrecy between parties, Auth0-issued JWTs are JSON Web Signatures (JWS), meaning they are signed rather than encrypted. As such, we will focus on signed tokens, which can verify the integrity of the claims contained within them, while encrypted tokens hide those claims from other parties.

- JSONオブジェクトに含まれる情報は、ディジタル署名されているため、検証と信頼できる
- Auth0が発行するJWTはJSON Web署名（JWS）。暗号化ではなく署名されている

> In general, JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA (although Auth0 supports only HMAC and RSA). When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

- 一般的に、JWT はシークレット (HMAC アルゴリズムを使用) または RSA または ECDSA を使用したPublic/Private ペアを使用して署名できる
    -  (ただし、Auth0 は HMAC と RSA のみをサポートしています)
- トークンがPublic/Private ペアを使用して署名される場合、署名により、秘密キーを保持している当事者のみが署名者であることが証明される

> Before a received JWT is used, it should be properly validated using its signature. Note that a successfully validated token only means that the information contained within the token has not been modified by anyone else. This doesn't mean that others weren't able to see the content, which is stored in plain text. Because of this, you should never store sensitive information inside a JWT and should take other steps to ensure that JWTs are not intercepted, such as by sending JWTs only over HTTPS, following best practices, and using only secure and up-to-date libraries.

- 受信したJWTを使用する前に、署名を使用して適切に検証されたトークンの検証に成功したということは、トークンに含まれる情報が他の誰にも変更されていないということだけを意味する
- 他のユーザーがコンテンツを見ることができないという意味ではない
- コンテンツはプレーンテキストで保存される。そのため、JWT内に機密情報を保存しないこと。JWTをHTTPS経由でのみ送信するなど、JWTが傍受されないようにするための他の手順を踏む必要がある

## 署名アルゴリズム
下記のドキュメントを基に整理します。

https://auth0.com/docs/get-started/applications/signing-algorithms

> 署名アルゴリズムは、アプリケーションやAPI用に発行されたトークンに署名するために使用されるアルゴリズムです。署名は
JSON ウェブトークン (JWT)
トークンの送信者がその送信者本人であることを確認し、メッセージが途中で変更されていないことを確認するために使用されます。

- 署名アルゴリズムとは
    - アプリやAPI用に発行されたトークンに署名するために使用されるアルゴリズム
    - 署名はJWTの一部である
        - トークンの送信者が送信者本人であることを確認し、メッセージが途中で改善されていないことを確認するために使用する

> 次の署名アルゴリズムから選択できます。
> - RS256 (SHA-256 を使用した RSA 署名) : 非対称アルゴリズム。つまり、2 つのキーがあります。1 つは公開キー、もう 1 つは秘密にしておく必要がある秘密キーです。Auth0 は署名の生成に使用する秘密キーを保有しており、JWT のコンシューマーはAuth0が提供するメタデータ エンドポイントから公開キーを取得し、それを使用して JWT 署名を検証します。
> - HS256 (SHA-256 を使用した HMAC) : 対称アルゴリズム。秘密に保持する必要がある秘密キーは 1 つだけであり、2 者間で共有されます。署名の生成と検証の両方に同じキーが使用されるため、キーが侵害されないように注意する必要があります。この秘密キー (またはシークレット) は、アプリケーション (クライアント シークレット) または API (署名シークレット) を登録し、HS256 署名アルゴリズムを選択すると作成されます。
> - PS256 (SHA-256 を使用した RSA 署名) : 非対称アルゴリズム。つまり、2 つのキーがあります。1 つは公開キー、もう 1 つは秘密にしておく必要がある秘密キーです。Auth0 は署名の生成に使用する秘密キーを保持し、JWT のコンシューマーは Auth0 が提供するメタデータ エンドポイントから公開キーを取得し、それを使用して JWT 署名を検証します。RS256 とは異なり、同じ JWT ヘッダーとペイロードで毎回異なる署名が生成されます。

- 下記の署名アルゴリズムが使用できる
    - RS256
    - HS256
    - PS256

| |RS256|HS256|PS256|
|:--|:--|:--|:--|
|署名|SHA-256のRSA署名|SHA-256のHMAC署名|SHA-256のRSA署名|
|アルゴリズム|非対称|対象|非対称|
|鍵の保有|Auth0は署名の生成に使用する秘密鍵を保有|秘密鍵を2社間で保有|Auth0は署名の生成に使用する秘密鍵を保有|
|検証|JWTのコンシューマーはAuth0が提供するメタデータエンドポイントから公開鍵を取得し、JWT署名を検証する|共有された秘密鍵を使用して検証する|JWTのコンシューマーはAuth0が提供するメタデータエンドポイントから公開鍵を取得し、JWT署名を検証する※同じJWTヘッダーとペイロードで毎回異なる署名が生成される|


> 最も安全な方法は、RS256 を使用することです。その理由は次のとおりです。
> - RS256 を使用すると、秘密鍵 (Auth0) の所有者だけがトークンに署名でき、公開鍵を使用して誰でもトークンが有効かどうかを確認できます。
> - RS256 を使用すると、秘密鍵が侵害された場合、新しいシークレットを使用してアプリケーションまたは API を再デプロイする必要なく (HS256 を使用する場合は再デプロイする必要があります)、キーのローテーションを実装できます。

- 最も安全な署名アルゴリズムであるRS256を選択しておくのがよい。理由は下記の通り
    - 秘密鍵（Auth0）の所有者だけがトークンに署名可能。公開鍵を使用して誰でもトークンが有効化検証できる
    - 秘密鍵が侵害された場合、新しいシークレットを使用してアプリ、またはAPIを再デプロイする必要なくキーのローテションを実装できる

## Bearer token
下記の記事を基に整理します。

https://ja.wikipedia.org/wiki/Bearer%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3

> Bearerトークンは、セキュリティトークンのうちその利用可否が「トークンの所有」のみで決定されるものである[1]。持参人トークン・署名なしトークンとも呼ばれる。

- 利用可否がトークンの所有のみで決定される
- 署名なしトークン

> Bearerトークンはしばしば切符に例えられる。切符は乗り物への乗車=アクセスを制御するトークンである。切符の利用権利は単純に「切符を持ってきた人=Bearer」に付与される。誰が切符を購入し管理していたかは関係がない。極端な例では拾った切符であっても（切符の権利者でなくても）持ってきた人=Bearerに乗車権利が付与される。このように切符はBearerトークンと同じ性質を持っている。

- 切符に例えられる
- 持っていれば使える

> Bearerトークンの対比としてproof-of-possessionトークン（PoPトークン、「所有の証明」トークン、記名式トークン）が挙げられる。PoPトークンはトークンの所有に追加してトークン権利を所有することの証明を必要とする[1]。Bearerトークンは切符に例えられるが、PoPトークンは国際線飛行機チケットに例えられる。国際線飛行機はチケットを提示するだけではなく、チケットに記された氏名の確認、すなわち権利所有者であることの証明が必要である（パスポートを利用する）。Bearerトークンは単純にトークンの所有のみが求められる点でPoPトークンと異なる。

- Bearerトークンの対比、PoPトークン（Proof-of-possession token）がある
- PoPトークンは、所有 + トークン権利を所有することの証明が必要
- PoPトークンは、国際線飛行機チケットに例えられる
    - チケットだけではなく、パスポートが必要

# 実践
下記の記事を基に試します。

https://auth0.com/docs/customize/integrations/aws/aws-api-gateway-custom-authorizers

## API Gatewayを作成する
### API の作成

1. AWSのマネコンにサインインします
1. API Gatewayに移動します
1. REST APIの「構築」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/500aa9cc-53c9-9214-eb39-3b8f666eb08f.png)

1. 下記を入力し、「APIを作成」をクリックします
    - サンプルAPI
    - APIエンドタイプ：リージョン
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/db427712-a8f7-8485-e8fe-64b3b61fd5ef.png)

1. PetStoreができました。名前をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/2b89ad5d-b808-f6b1-03d4-a5abfc7afc8b.png)

### APIの動作確認
作成した、APIの動作を確認します。

1. ①「POST」＞②「テスト」タブをクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6f4518c0-ac4d-ef83-6e1d-ce14ed54d023.png)

1. リクエスト本文に`{"type": "dog", "price": 249.99}`を入力し、「テスト」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/edc8c7fe-9400-26cd-58f9-f3d49c627c63.png)

1. 成功しました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8a51567a-6f89-e3eb-7dfa-fcb19fe22110.png)

### API のデプロイ
1. 「APIをデプロイ」をクリックします
1. ポップアップが表示されるので下記を入力し、「デプロイ」をクリックします
    - ステージ：新しいステージ
    - ステージ名：任意
    - 説明：任意
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/246f4617-c393-eec7-84f9-60b56b920da2.png)

### APIのテスト
1. ステージ画面が表示されるので、「URLを呼び出す」のリンクをコピーします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/9eea68c5-23fd-59ae-caea-e189d514fd5a.png)

1. ブラウザでコピーしたURLを入力します。表示されました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f9d984d8-400e-0305-163c-f702d71b02ee.png)

1. 下記のURLを入力します
    ```bash
    https://{domain}/{Stage}/pets/1
    ```
1. 下記の結果が返されました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/4293f8ac-3a41-316b-f3ad-00b433a0c16f.png)


## Auth0 APIを作成する

1. Auth0にサインインします
1. ナビゲーションペインで、「Appliactions>APIs」をクリックします
1. 「Create API」をクリックします
1. 下記を入力し、「Create」をクリックします
    - Name:任意
    - Identifier：識別子
    - JWT Profile:Auth0
    - JWT Signing Algorithm:RS256 
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/40ec7aca-806d-5a2a-f910-407a0083e05d.png)
1. 作成後、「Machine to Machine Applications」タブをクリックし、Client Idをメモしておきます

## Lambda オーソライザーを作成する
適切なユーザだけがAPIの背後にあるバックエンドにアクセスできるようにAPIを保護します。

### カスタム承認者を準備する
Auth0発行のトークンをサポートするツールを使用します。

1. サンプルのカスタム承認者をダウンロードします
    ```bash
    $ git clone https://github.com/auth0-samples/jwt-rsa-aws-custom-authorizer
    Cloning into 'jwt-rsa-aws-custom-authorizer'...
    remote: Enumerating objects: 179, done.
    remote: Counting objects: 100% (20/20), done.
    remote: Compressing objects: 100% (4/4), done.
    remote: Total 179 (delta 16), reused 16 (delta 16), pack-reused 159 (from 1)
    Receiving objects: 100% (179/179), 55.29 KiB | 5.03 MiB/s, done.
    Resolving deltas: 100% (97/97), done.
    $
    ```
1. Node.jsパッケージをインストールします
    ```bash 
    $ cd jwt-rsa-aws-custom-authorizer
    $ cat package.json 
    {
      "name": "lambda-auth0-authenticator",
      "version": "0.1.0",
      "description": "An AWS Lambda function to provide a Custom Authenticator for AWS API Gateway that verifies RS* signed tokens",
      "main": "index.js",
      "scripts": {
        "test": "lambda-local --timeout 300 --lambda-path index.js --event-path event.json",
        "bundle": "rm -f custom-authorizer.zip ; zip custom-authorizer.zip -r *.js *.json node_modules/"
      },
      "author": "Jason Haines",
      "license": "Apache-2.0",
      "dependencies": {
        "auth0": "^2.5.0",
        "bluebird": "^3.4.6",
        "dotenv": "^5.0.1",
        "jsonwebtoken": "^8.2.1",
        "jwks-rsa": "^1.1.1"
      },
      "devDependencies": {
        "aws-sdk": "^2.2.48",
        "lambda-local": "^1.4.8"
      },
      "repository": {
        "type": "git",
        "url": "git+https://github.com/auth0-samples/lambda-jwt-rsa-authorizer.git"
      },
      "keywords": [
        "aws",
        "api-gateway",
        "auth0",
        "custom-authorizer",
        "authentication",
        "lambda"
      ],
      "bugs": {
        "url": "https://github.com/auth0-samples/lambda-jwt-rsa-authorizer/issues"
      },
      "homepage": "https://github.com/auth0-samples/lambda-jwt-rsa-authorizer#readme"
    }
    $
    jwt-rsa-aws-custom-authorizer $ ls         
    LICENSE                 README.md               event.json.sample       index.js                lib.js                  package-lock.json       package.json
    $ npm install
    ```

1. .envファイルを作成します。（.env.sampleからコピーします）
    ```bash
    $ cp .env.sample .env
    $
    ```
1. .envファイルについて、下記を更新します
    - JAWKS_URL:    https://{Auth0のドメイン}.jp.auth0.com/.well-known/jwks.json
    - AUDIENCE:     https:\//Auth0作成時のAPI識別子/
    - TOKEN_ISSUER: https://{Auth0のドメイン}.jp.auth0.com/
    ```properties
    $ cat .env
    # copy this file to .env and fill in the <templates>
    JWKS_URI=<url_for_the_jwks_endpoint>
    AUDIENCE=<api_audience>
    TOKEN_ISSUER=<token_issuer>
    $
    ```

### カスタムオーソライザーをローカルでテストする

1. Auth0にサインインし、対象のAPIを選択します
1. 「test」タブをクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/373b57fa-50fb-7543-9afa-133cdf8868b4.png)
1. クライアントの言語ごとに、アクセストークンを取得するコマンドが書かれています。今回はcurlを使用します
1. アクセストークンを取得します
    ```bash
    jwt-rsa-aws-custom-authorizer $ curl --request POST \
      --url https://XXXXXXXXXX.jp.auth0.com/oauth/token \
      --header 'content-type: application/json' \
      --data '{"client_id":"XXXXXXX","client_secret":"XXXXXXXXX","audience":"https://XXXXXXX.execute-api.ap-northeast-1.amazonaws.com/","grant_type":"client_credentials"}'

      {"access_token":"XXXXXXXXX","expires_in":YYYYY,"token_type":"Bearer"}
      $
    ```
1. event.jsonファイルを作成します（envet.json.sampleからコピーします）
    ```bash
    jwt-rsa-aws-custom-authorizer $ cp event.json.sample event.json        
    jwt-rsa-aws-custom-authorizer $ cat event.json
    {
        "type" : "TOKEN",
        "authorizationToken" : "Bearer ACCESS_TOKEN",
        "methodArn":"arn:aws:execute-api:us-east-1:1234567890:apiId/stage/method/resourcePath"
    }
    $
    ```
1. event.jsonの下記の値を置き換えます
    - authorizationToken:ACCESS_TOKEN
    - methodArn:API GatewayのARN

1. テストを実行します。成功しました
    ```bash
    jwt-rsa-aws-custom-authorizer $ npm test
    
    > lambda-auth0-authenticator@0.1.0 test
    > lambda-local --timeout 300 --lambda-path index.js --event-path event.json
    
    info: START RequestId: XXXX
    {
      type: 'TOKEN',
      authorizationToken: 'Bearer XXXXXXXX',
      methodArn: 'arn:aws:execute-api:ap-northeast-1:XXXXXXX:XXXXXXX/XXXXX/GET/pets'
    }
    info: End - Message
    info: ------
    info: {
            "principalId": "XXXXXX@clients",
            "policyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                            {
                                    "Action": "execute-api:Invoke",
                                    "Effect": "Allow",
                                    "Resource": "arn:aws:execute-api:ap-northeast-1:XXXXXX:XXXXXX/XXXXXX/GET/pets"
                            }
                    ]
            },
            "context": {}
    }
    info: ------
    info: Lambda successfully executed in 1645ms.
    $
    ```

## IAMロールを作成する
IAMロールには、Lambda関数を呼び出すために必要な関数があります。
そのため、カスタム認証を設定する前に、API Gatewayがアクセス要求を受信する都度カスタム認証を呼び出すことができるIAMロールを作成します。

1. AWSにサインインし、IAMに移動します
1. ナビゲーションペインで「Role」をクリックします
1. 「ロールの作成」をクリックします
1. ユースケースで「Lambda」を選択し、「次へ」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/654c2a5d-6252-f396-e284-248e8a11b4ef.png)
1. 許可ポリシーで、「AWSLambdaRole」を選択し、「次へ」をクリックします

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/ebad3317-7443-959a-abc9-dcf9afbf0e2b.png)

1. ロール名を入力し、「ロールを作成」をクリックします
1. 作成したIAMロール名をクリックします
1. 「信頼関係」タブ>「信頼ポリシーを編集」をクリックします
1. 下記のポリシーを入力し、「ポリシーを更新」をクリックします
    ```json
    {
    	"Version": "2012-10-17",
    	"Statement": [
    		{
    			"Effect": "Allow",
    			"Principal": {
    				"Service": [
    				    "lambda.amazonaws.com"
    				    ,"apigateway.amazonaws.com"
    				]
    			},
    			"Action": "sts:AssumeRole"
    		}
    	]
    }
    ```

## Lambda関数を作成し、Lambdaオーソライザーをデプロイする
AWSにデプロイします。

### Lambda関数

1. 下記を実行し、AWSにアップロードできるバンドルを作成します
    ```bash
    jwt-rsa-aws-custom-authorizer $ npm run bundle
    jwt-rsa-aws-custom-authorizer $ ls -l custom-authorizer.zip 
    -rw-r--r--  1 XXXXX  XXXX  6639451  8 18 20:46 custom-authorizer.zip
    jwt-rsa-aws-custom-authorizer $
    ```

1. AWSにサインインし、Lambdaに移動します
1. 「関数の作成」をクリックします
1. 下記を入力し、「関数の作成」をクリックします
    - 関数名：任意
    - ランタイム:Node.js
    - IAMロール：作成したIAMロールを指定
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/ed349dab-940c-0163-9e9b-eea075c68e13.png)

### コードのアップロード

1. 作成後、①「コード」タブ、②「アップロード元＞.zipファイル」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1bd4c636-dd4c-b364-915c-cc14c8c5b3d6.png)
1. 作成したファイルを指定し、保存します

### 環境変数

1. ①「設定」タブ＞②「環境変数」＞③「編集」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d6da2a2a-be55-deb6-978e-3ce31af14fa6.png)

1. .envに設定した内容と同様のものを設定します
    - JWKS_URI
    - AUDIENCE
    - TOKEN_ISSUER
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8a61754f-6b9d-4b98-394c-64d28d1ee7f1.png)

### タイムアウト
1. ①「一般設定」＞②タイムアウトを30秒に設定します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5e9c2517-e933-5139-e72a-4f4b8cd2fa67.png)

### テスト
1. 「テスト」タブをクリックします
1. ①「イベントJSON」には、ローカルPCで設定した同内容のコピーし、②「テスト」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/c395927b-a101-bbb8-16ac-16fb219a6731.png)

1. 成功しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/a41dba7a-c097-41ed-48c0-c652b0eac1e6.png)

### API Gatewayカスタム認証を構成する
1. API Gatewayに移動します
1. 作成したAPIを選択します
1. ナビゲーションペインで「オーソライザー」をクリックします
1. 「オーソライザーを作成」をクリックします
1. 下記を入力し、「オーソライザーを作成」をクリックします
    - オーソライザー名：任意
    - オーソライザーのタイプ：Lambda
    - Lambda関数：作成したLambda
    - Lambda呼び出しロール：作成したIAMロールのARN
    - Lambdaイベントペイロード：トークン
    - トークンのソース：Authorization
    - トークンの検証：^Bearer [-0-9a-zA-z\.]*$
    - TTL:3600
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/af84039b-77be-4883-b4cf-43ebba140737.png)
1. 作成したオーソライザーをクリックします
1. ①トークンの値に、event.jsonに指定した値を指定し、②「オーソライザーをテスト」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/92719373-ac7c-bc60-3809-c67e8df00b32.png)

1. 成功しました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/0de982a7-c521-2851-a10c-10915880bbf2.png)

## カスタム認証を使用してAPIを保護する
### カスタム認証を使用するようにAPI Gatewayリソースを構成する
1. API Gatewayを開きます
1. `/pets/GET`をクリックしました
1. 「メソッドリクエスト」をクリックします
1. 「編集」をクリックします
1. 認可に、作成したオーソライザーを指定し、APIキーのチェックを外し、「保存」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8a8c3680-13b8-c9b4-a387-bd86c8073aa5.png)

### APIをデプロイする
1. 「APIをデプロイ」をクリックします
1. ポップアップが表示されるので、下記を入力し、「デプロイ」をクリックします
    - ステージ：新しいステージ
    - ステージ名：任意
    - デプロイの説明：任意
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/93df4b52-cc41-6378-a9c6-e1d6bb495fb0.png)

## デプロイしたAPI Gatewayをテストする
1. デプロイしたAPIのURLをブラウザに指定します。表示されました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/881ce464-0109-422e-847a-84f7f3438985.png)

# 考察
今回は、API Gateway でLambdaオーソライザーとAuth0を使用しての認証を試しました。
全体の雰囲気は、つかめたので次回以降は、API Gatewayのバックエンドを用意して試してみたいと思います。


# 参考
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html

https://auth0.com/docs/customize/integrations/aws/aws-api-gateway-custom-authorizers#create-an-auth0-api

https://qiita.com/zumax/items/2d7eb1ffa49441eb988a

https://qiita.com/zumax/items/9da91f63a07b2d1df3b9

https://auth0.com/docs/secure/tokens/json-web-tokens

https://auth0.com/docs/get-started/applications/signing-algorithms