カスタムオーソライザーを使用してAWS API Gatewayエンドポイントをセキュリティ保護する
AWS Lambda関数(カスタムオーソライザーを含む)を作成・構成してAPIエンドポイントを保護し、認可フローを実装します。これにより、ユーザーはAuth0からAPIへのアクセス取得に必要なアクセストークンを取得できるようになります。
詳細は、AWS API Gatewayにログインして、「AWS Lambdaの概要」をご覧ください。
API GatewayはLambda関数の前にサービスレイヤーを追加することで、セキュリティを拡張し、入力・出力メッセージの変換を管理し、スロットリングや監査などの機能を提供して、Lambdaの機能を拡張します。サーバーレスアプローチにより、スケールアウトやフォールトトレランスなどの懸念事項がコードを実行するコンピュートサービスの責任となるため、運用上の課題が簡素化されます。
カスタムオーソライザーは以下を行います。
APIへのアクセス要求の
authorization
ヘッダーを介してアクセストークンが渡されたことを確認する。JWKSエンドポイントを介して取得した公開鍵を使用して、アクセストークンのRS256署名を検証する。
アクセストークンに必要な発行者
iss
とオーディエンスaud
クレームがあることを確認する。
以下の手順でカスタムオーソライザーを使用します。
署名アルゴリズムの詳細については、「署名アルゴリズム」を参照してください。JWKSの使用に関する詳細は、「JSON Web Key Sets」をご覧ください。
API Gatewayのカスタムオーソライザーの仕組み
Amazonによると、API Gatewayのカスタムオーソライザーは、「OAuthやSAMLなどのベアラートークン認証戦略を使用してAPIへのアクセスを制御するために提供するLambda関数」です。
誰か(または何らかのプログラム)がAPIを呼び出そうとするたびに、API GatewayはそのAPI用に構成されたカスタムオーソライザーがあるかどうかを確認します。
APIのカスタムオーソライザーがある場合は、API Gatewayがカスタムオーソライザーを呼び出し、受け取った要求ヘッダーから抽出した認可トークンを提供します。
JWT検証を含むさまざまなタイプの認可戦略を実装して、要求を認可するIAMポリシーを返すことができます。返されたポリシーが無効か、アクセス許可が拒否された場合は、API呼び出しは失敗です。
ポリシーが有効な場合は、APIが返されたポリシーをキャッシュし、それを受信トークンと関連付けて、現在および後続の要求に使用します。ポリシーがキャッシュされる時間は構成できます。デフォルト値は300
秒で、キャッシュの最大の長さは3600
秒です(この値は0に設定してキャッシュを無効化することも可能です)。
詳細は、AmazonのDeveloper Guideにある「What is Amazon API Gateway?(Amazon API Gatewayとは?)」を参照してください。JWT検証の詳しい情報は、当社の「JSON Web Token」をお読みください。
前提条件
AWSアカウントにサインアップする必要があります。これにより、API GatewayとLambdaを含む、AWS機能へのアクセスが付与されます。新規メンバーは全員、12か月無料プランでAWSへのアクセスを得ることができます。
Auth0 APIを作成する
正常に認可されたアプリケーションによって使用されるAPIを構成します。
[Auth0 Dashboard]>[Applications(アプリケーション)]>[APIs(API)]に移動して、[Create API(APIの作成)]を選択します。
以下のフィールドに値を入力して、[Create(作成)]を選択します。
フィールド 説明 Name(名前) APIのフレンドリー名。Auth0 APIのリストに表示される名前です。 Identifier(識別子) APIの一意の識別子。この識別子をURL https://your-api-gateway
のような形式にすることをお勧めします。JSON Web Token Profile(JSON Webトークンのプロファイル) OAuth2.0のトークンプロファイルまたはダイアレクト(アクセストークン用)。詳細については、「アクセストークンのプロファイル」をお読みください 署名アルゴリズム 発行されたアクセストークンを署名するために、Auth0で使用したいアルゴリズム。詳細については、「署名アルゴリズム」を参照してください。
新たに作成されたAPIの詳細を確認するには、[Settings(設定)]ビューをご覧ください。

APIを作成すると、APIと共に使用するM2Mアプリケーションも作成されます。このアプリケーションは、[Machine to Machine Application(M2Mアプリケーション)]ビューの[Authorized(認可済み)]に表示されます。クライアントIDをメモしてください。このチュートリアルのパート3で使用します。
AWS API Gateway APIをインポートして導入する
この手順では以下を行います。
APIをAPI Gatewayにインポートする
APIインポートをテストする
任意のフロントエンドアプリケーションで使用できるようにAPIを導入する
API導入をテストする
Pets APIをインポートして構成する
AWSアカウントにログインし、上部のナビゲーションバーにある[Services(サービス)]ドロップダウンから[API Gateway]コンソールに移動します。
すでにAPIを作成している場合は、API Gatewayコンソールに移動して[Create API(APIの作成)]をクリックします。[Create new API(APIの新規作成)]フォームで[Example API(サンプルAPI)]を作成することもできます。初めてAPI GatewayでAPIを作成する場合は、以下の画面が表示されます。[Get Started(はじめる)]をクリックして続けます。
API Gatewayに歓迎するポップアップメッセージが表示されます。[OK]をクリックして続行します。
[Create new API(APIの新規作成)]フォームでは、[Example API(サンプルAPI)]がデフォルトで選択されており、エディターに定義されたサンプルAPIがあります。本チュートリアルでは以後このAPIを使用するので、[Import(インポート)]をクリックしてAPI作成プロセスを開始します。
インポートが終わったら、APIが作成され、提供されたデータが読み込まれたことを示すメッセージが表示されます。APIにはすでにメソッド(
GET
とPOST
)が関連付けられています。リソースツリーでメソッド名をクリックすると、メソッドの詳細表示やその構成の変更、メソッドの呼び出しテストが行えます。
APIをテストする
APIをテストするには、/pets
のPOSTをクリックします。すると、[Method Execution(メソッドの実行)]画面が表示され、POST
メソッドの構造と動作の概要を確認できます。
[Method Request(メソッド要求)]と[Method Response(メソッド応答)]:APIとフロントエンドのインターフェイス
[Integration Request(統合要求)]と[Integration Response(統合応答)]:APIとバックエンドのインターフェイス
この領域を使用してAPIをテストできます。
(ページ中程の[Client(クライアント)]にある)[Test(テスト)]をクリックします。
/pets - POST - Method Test
ページにリダイレクトされます。ページの下までスクロールし、以下のスニペットを[Request Body(要求本文)]として入力します。要求本文は、データベースに追加したいペットの属性とそのペットの費用を示します。{"type": "dog", "price": 249.99}
Was this helpful?
/[Test(テスト)]をクリックして続行します。ページの右側にテストの結果が表示されます。
APIを導入する
先ほど完了したテストは、API Gatewayコンソールを用いて行われました。APIを別のアプリケーションと使用するには、APIをステージに導入する必要があります。
[Actions(アクション)]メニューで[Deploy API(APIの導入)]を選択します。
以下の値を入力して、[Deploy(導入)]をクリックします。
パラメーター 値 Deployment stage(導入ステージ) [New Stage]
を選択Stage name(ステージ名) ステージの名前を指定します Stage description(ステージの説明) ステージの説明を入力します Deployment description(デプロイメントの説明) APIデプロイメントの説明を入力します
導入をテストする
APIが正常に導入されたら、[Test Stage Editor(テストステージエディター)]にリダイレクトされます。この時点で、APIが正しく導入されているかをテストすることができます。
[Test Stage Editor(テストステージエディター)]画面の上部には、Invoke URL(呼び出しURL)の青いバナーが表示されます。これは、APIの
GET
エンドポイントを呼び出すために使用するURLです。リンクをクリックしてブラウザーでGET / method
要求を送信します。これにより、以下のような成功応答が返されるはずです。[Stages(ステージ)]ページで、[Test(テスト)]のツリーを展開します。
/pets/{petId}
のGETをクリックします。画面上部の青いバナーにInvoke URL(呼び出しURL)が表示されます。その末尾部分の
{petID}
は、パス変数を表します。この変数を1
に置き換え、ブラウザーを使って新しいURLに移動します。以下のJSONペイロードのHTTP 200要求を受信するはずです。{ "id": 1, "type": "dog", "price": 249.99 }
Was this helpful?
/
カスタムオーソライザーを作成する
以上の手順で、API Gatewayによって管理された完全に機能するAPIができたので、次に、適切な認可を得たユーザーだけがAPIのバックエンドにアクセスできるようにAPIを保護します。
API Gatewayのカスタム要求オーソライザーを使用して、OAuth 2.0やSAMLなどのベアラートークン認可戦略でAPIを認可します。各受信要求で以下が行われます。
API Gatewayが、カスタムオーソライザーが適切に構成されていることを確認します。
API Gatewayが、認可トークンを使用してカスタムオーソライザー(Lambda関数)を呼び出します。
認可トークンが有効な場合は、カスタムオーソライザーが適切なAWSのIDおよびアクセス管理(IAM)ポリシーを返します。
API Gatewayが、手順3で返されたポリシーを使用して要求を認可します。
カスタムオーソライザーを準備する
Auth0発行トークンをサポートするサンプルのカスタムオーソライザーをダウンロードできます。その後、自身の環境でカスタムオーソライザーが動作するようにファイルをカスタマイズする必要があります。
ダウンロードしたサンプルファイル入りのフォルダーを任意の場所で解凍し、コマンドラインを使用してフォルダーに移動します。
サンプルフォルダー内で
npm install
を実行し、導入に必要なNode.jsパッケージをインストールします。後の手順でAWSにアップロードするバンドルにこれらのファイルを含める必要があります。.env
ファイルを使ってローカル環境を構成します。cp .env.sample .env
を使って.env.sample
ファイルをコピー(すると同時に名前を.env
に変更)できます。以下の項目を変更します。たとえば、パラメーター 値 TOKEN_ISSUER
トークンの発行者。Auth0がトークン発行者の場合は、 https://{yourDomain}/
を使用する。末尾のスラッシュを必ず付けてください。JWKS_URI
JWKSエンドポイントのURL。Auth0がトークン発行者の場合は、 https://{yourDomain}/.well-known/jwks.json
を使用するAUDIENCE
上の「Auth0 APIを作成する」セクションで作成したAPIのidentifierの値。 .env
ファイルのテキストは、完了すると以下のようになります。JWKS_URI=https://{yourDomain}/.well-known/jwks.json AUDIENCE=https://your-api-gateway TOKEN_ISSUER=https://{yourDomain}/
Was this helpful?
/
カスタムオーソライザーをローカルでテストする
有効なJWTアクセストークンを取得する取得方法は複数あり、選択する方法はアプリケーションの種類、信頼レベル、または全体的なエンドユーザーのエクスペリエンスに応じます。詳細は、「アクセストークンを取得する」をお読みください。
APIのテストトークンを取得するには、[Auth0 Dashboard]>[Applications(アプリケーション)]>[APIs(API)]に移動して、APIを選び、[Test(テスト)]を選択します。
トークンを含む
event.json
ファイルをローカルに作成します。サンプルファイルをコピーできます(cp event.json.sample event.json
を実行します)。ACCESS_TOKEN
を自身のJWTトークンに置き換え、methodArn
をAPIのGET
メソッドの適切なARN値に置き換えます。
methodArn
を取得するには以下を行います。
API Gatewayコンソールで[PetStore]APIを開きます。
左側のナビゲーションで[Resources(リソース)]を選択します。
中央の[Resources(リソース)]パネルでリソースツリーを展開します。
/pets
の下の[GET]を選択します。[Method Request(メソッド要求)]欄に[ARN]が表示されます。
npm test
を使用してテストを実行します。
lambda-localパッケージを使用して、トークンを用いてカスタムオーソライザーをテストします。テストに成功すると、以下のように表示されます。
Message
------
{
"principalId": "C8npTEMVnBrILsBTI91MOh6dfuZbPVAU@clients",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:us-east-1:1234567890:apiId/stage/method/resourcePath"
}
]
},
"context": {
"scope": "FULL_LIST_OF_SCOPES"
}
}
Was this helpful?
Effect
の値がAllow
の場合は、オーソライザーがAPI Gatewayへの呼び出しを許可していることを表します。
詳細は、NPMの「Lambda-local」を参照してください。
IAMロールを作成する
IAMロールにはLambda関数を呼び出すために必要なアクセス許可があります。カスタムオーソライザーに取りかかる前に、API Gatewayがアクセス要求を受け取るたびにカスタムオーソライザーを呼び出すことのできるIAMロールを作成する必要があります。
AWSにログインし、[IAM Console(IAMコンソール)]に移動します。左側のナビゲーションで[Roles(ロール)]を選択します。
[Create new role(ロールの新規作成)]を選択します。
[AWS service(AWSサービス)]で[AWS Lambda]行を選び、[Next:Permissions(次へ:アクセス許可)]を選択します。
[Attach permissions policy(アクセス許可ポリシーのアタッチ)]画面で、[AWSLambdaRole]を選択します。フィルターを使用してオプションのリストを絞り込むことができます。[Next:Tags(次へ:タグ)]、[Next:Review(次へ:確認)]の順に選択して続行します。
[Review(確認)]画面で、
Auth0Integration
などの[Role name(ロール名)]を入力します。他のフィールドはそのままにします。[Create role(ロールの作成)]を選択します。AWSがロールを作成し終えたら、IAMの[Roles(ロール)]ページにリダイレクトされます。新しいロールを選択します。
先ほど作成したロールの[Summary(サマリー)]ページで[Trust relationships(信頼関係)]ビューを選択します。
[Edit trust relationship(信頼関係の編集)]を選択して、[Policy Document(ポリシードキュメント)]フィールドに以下のJSONスニペットを入力します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "apigateway.amazonaws.com", "lambda.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] }
Was this helpful?
/[Update Trust Polic(信頼ポリシーの更新)]をクリックします。
[Summary(サマリー)]ページにリダイレクトで戻されます。後で使用するために[Role ARN(ロールARN)]値をコピーしておきます。
Lambda関数を作成してカスタムオーソライザーを導入する
以上の手順で、自身の環境用にカスタムオーソライザーを構成し、動作することをテストしたので、次はこれをAWSに導入しましょう。
npm run bundle
を実行して、AWSにアップロードできるバンドルを作成します。これにより、AWS Lambdaが必要とするソース、構成、ノードモジュールを含むcustom-authorizer.zip
バンドルが生成されます。Lambdaコンソールに移動して、[Create function(関数の作成)]をクリックします。
[Select blueprint(ブループリントの選択)]ページで[Author from scratch(一から作成)]をクリックして空の関数を作成します。[Basic information(基本的な情報)]で以下のパラメーターの値を入力します。
パラメーター 値 Name(名前) Lambda関数の名前。例: jwtRsaCustomAuthorizer
Description(説明) Lambda関数の説明(任意) Runtime(ランタイム) Node.js 10.x
を選択[Create Function(関数の作成)]をクリックして続行します。
関数の[Configuration(設定)]ページで[Function Code(関数コード)]セクションまで下にスクロールします。
[Code entry type(コード入力タイプ)]で[Upload a .ZIP file(.ZIPファイルのアップロード)]を選択します。
[Upload(アップロード)]をクリックして、先ほど作成した
custom-authorizer.zip
バンドルを選択します。次に、以下の3つの[Environment variables(環境変数)]を作成します。この情報は
.env
ファイルの情報と同じであることに注意してください。パラメーター 値 TOKEN_ISSUER
トークンの発行者。Auth0がトークン発行者の場合は、 https://{yourDomain}/
を使用するJWKS_URI
JWKSエンドポイントのURL。Auth0がトークン発行者の場合は、 https://{yourDomain}/.well-known/jwks.json
を使用するAUDIENCE
手順1で作成したAPIのidentifierの値。 [Execution role(実行ロール)]セクションで、[Use an existing role(既存ロールを使用)]を選択してから、以前[Existing role(既存ロール)]として作成したIAMロールを選びます。
[Basic settings(基本設定)]で[Timeout(タイムアウト)]を[30]秒に設定します。
[Save(保存)]をクリックします。
作成したLambda関数をテストするため、右上の[Test(テスト)]をクリックします。
event.json
ファイルの内容を[Configure test event(テストイベントの設定)]フォームにコピーします。デフォルトの「Hello World」イベントテンプレートを使用できます。[Create(作成)]をクリックします。
これを選択し、[Test(テスト)]をクリックして実行します。テストに成功したら、[Execution result: succeeded(実行結果:成功)]と表示されます。出力画面を展開すると、ローカルテストに成功したときと似たようなメッセージが表示されるはずです。
API Gatewayのカスタムオーソライザーを構成する
API Gatewayコンソールに戻り、先ほど作成したPetStore APIを開きます。
左側のナビゲーションから[Authorizers(オーソライザー)]を開き、[Create New Authorizer(オーソライザーの新規作成)]を選択してから、以下のパラメーターを設定し、[Create(作成)]をクリックします。
パラメーター 値 名前 jwt-rsa-custom-authorizer
タイプ Lambdaを選択する Lambdaのリージョン 前に作成したLambda関数のリージョンを使用する Lambda関数 jwtRsaCustomAuthorizer
Lambdaの実行ロール 上でコピーしたIAMロールARN Lambdaのイベントペイロード **[Token(トークン)]**を選択する トークンのソース Authorization
トークンの検証 ^Bearer [-0-9a-zA-z\.]*$
TTL(秒) 3600
AWSがオーソライザーを作成し、ページが更新されたら、[Test(テスト)]をクリックし、先ほど使用したAuth0トークン(
Bearer ey...
)を入力することで、オーソライザーをテストします。テストに成功すると、以下のような応答が表示されます。
カスタムオーソライザーを使用してAPIを保護する
APIのエンドポイントを保護する方法については、Amazon API GatewayのDeveloper Guideにある、「Use API Gateway Lambda Authorizers(API Gateway Lambdaオーソライザーを使用する)」の記事を参照してください。
カスタムオーソライザーを使用するためにAPI Gatewayリソースを構成する
AWSにログインし、[API Gateway Console(API Gatewayコンソール)]に移動します。
本チュートリアルの手順2で作成した[PetStore] APIを開きます。中央にある[Resource(リソース)]ツリーの
/pets
リソースで、[GET]メソッドを選択します。[Method Request(メソッド要求)]を選択します。
[Settings(設定)]で[Authorization(認可)]の右にある鉛筆手順3で作成した
jwt-rsa-custom-authorizer
カスタムオーソライザーを選択します。チェックマークアイコンをクリックして、選んだカスタムオーソライザーを保存します。[API Key Required(APIキーを必須にする)]フィールドが
false
になっていることを確認します。
APIを導入する
変更内容を公開するため、APIを導入します。
[Actions(アクション)]メニューで[Deploy API(APIの導入)]を選択します。
以下の値を入力して、[Deploy(導入)]をクリックします。
パラメーター 値 Deployment stage(導入ステージ) [New Stage]
を選択Stage name(ステージ名) ステージの名前を指定します Stage description(ステージの説明) ステージの説明を入力します Deployment description(デプロイメントの説明) APIデプロイメントの説明を入力します
成功したら、[Test Stage Editor(テストステージエディター)]にリダイレクトされます。導入をテストする際に必要になるので、上部の青いバナーに表示されているInvoke URL(呼び出しURL)をメモします。
導入をテストする
導入をテストするため、先ほどの手順でメモしたInvoke URL(呼び出しURL)にGET
呼び出しを行います。このテストに失敗した場合は、JWTアクセストークンを正しく取得していることを確認してください。
詳細は、「アクセストークンを取得する」でご覧ください。
curl --request GET \
--url https://%7ByourInvokeUrl%7D/pets
Was this helpful?
var client = new RestClient("https://%7ByourInvokeUrl%7D/pets");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
Was this helpful?
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://%7ByourInvokeUrl%7D/pets"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
Was this helpful?
HttpResponse<String> response = Unirest.get("https://%7ByourInvokeUrl%7D/pets")
.asString();
Was this helpful?
var axios = require("axios").default;
var options = {method: 'GET', url: 'https://%7ByourInvokeUrl%7D/pets'};
axios.request(options).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.error(error);
});
Was this helpful?
#import <Foundation/Foundation.h>
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://%7ByourInvokeUrl%7D/pets"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"%@", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(@"%@", httpResponse);
}
}];
[dataTask resume];
Was this helpful?
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://%7ByourInvokeUrl%7D/pets",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
Was this helpful?
import http.client
conn = http.client.HTTPSConnection("")
conn.request("GET", "%7ByourInvokeUrl%7D/pets")
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
Was this helpful?
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://%7ByourInvokeUrl%7D/pets")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body
Was this helpful?
import Foundation
let request = NSMutableURLRequest(url: NSURL(string: "https://%7ByourInvokeUrl%7D/pets")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Was this helpful?