Edited at

APIGatewayのIAM認証付きのAPIをJavascriptから叩く

More than 1 year has passed since last update.


実現したいこと

APIGatewayで認証を"AWS_IAM"に設定したAPIをブラウザからJavascriptで叩きます。これにより、CognitoのUnauthUserとAuthUserでアクセスできるAPIを制御することができます。


やること

分かりやすいように順を追ってやっていきます。


  1. Lambdaで簡単なFunctionを作成

  2. APIGatewayで上記Functionを呼び出すAPIを作成する

  3. 作成したAPIのCORSを有効化する

  4. APIをデプロイし、API用のJavascript SDKを作成し、ダウンロードする

  5. 認証なしでAPIを叩くJavascriptのコードを書く(ここで一度テストし、うまくいくことを確認)

  6. APIの認証を"なし"→"AWS_IAM"に設定する(ここで一度テストし、アクセスが拒否されることを確認)

  7. Cognito Identity Poolを作成

  8. APIの実行を許可するIAMを作成し、UnauthUserのRoleにアタッチする

  9. Cognito UnauthUserのCredentialでAPIを叩くJavascriptのコードを書く(完成)


Lambdaで簡単なFunctionを作成

「Lambda」で"Create Function"> 「Select BluePrint」で"Blank Function"を選択>「Configure triggers」はそのままで"Next"

この画面では、関数の中身は編集しません。"FunctionName"に適当な関数名(myHelloWorldFuncなど)をつけ、下記の"Lambda function handler and role"のところで"Create a custom role"を選択します。

スクリーンショット 2016-12-26 23.48.31.png

すると別タブでIAMが開くので、"新しいIAMロールの作成"を選択して、そのまま右下の"許可"をクリックします。(すると、「lambda_basic_execution」という必要最低限のRoleが生成され、作成中のLambda Functionに適用されます。)

スクリーンショット 2016-12-26 23.49.02.png

Functionの設定画面に戻ったら、"Lambda function handler and role"にさきほどの「lambda_basic_execution」が適用されていることを確認し、右下のOKをクリックします。

これで、実行すると「Hello World」とだけ返してくるLambda関数が完成しました。


APIGatewayで上記Functionを呼び出すAPIを作成する

「APIGateway」で"APIを作成"

スクリーンショット 2016-12-26 23.59.16.png

"アクション">"リソースの作成"

スクリーンショット 2016-12-26 23.59.28.png

ここでは「test」というリソースを作成します。

スクリーンショット 2016-12-26 23.59.48.png

"アクション" > "メソッドの作成"

スクリーンショット 2016-12-27 0.00.08.png

ここでGetメソッドを作成します。(チェックマークをクリックして確定するのを忘れずに)

スクリーンショット 2016-12-27 0.00.19.png

セットアップの画面では、Lambda関数を選択し、さきほど作ったmyHelloWorldFunctionのリージョンを選択して、myHelloWorldFunctionを設定し、"保存"をクリックします。

スクリーンショット 2016-12-27 0.00.42.png

Getメソッドが作成できました。

スクリーンショット 2016-12-27 0.05.49.png


作成したAPIのCORSを有効化する

"アクション" > "CORSの有効化"をクリック

スクリーンショット 2016-12-27 0.09.52.png

メソッドのGETにチェックが入っていることを確認し、それ以外の設定はいじらずに"CORSを有効にして既存のCORSヘッダーを置き換え"をクリック

スクリーンショット 2016-12-27 0.10.06.png

CORSが有効化され、GETメソッドの下にOPTIONメソッドが追加されていればOKです。

スクリーンショット 2016-12-27 0.10.22.png


APIをデプロイし、API用のJavascript SDKを作成し、ダウンロードする

testリソースが選択された状態で、"アクション" > "APIのデプロイ"をクリック

スクリーンショット 2016-12-27 0.15.04.png

ここでは「dev」という名前の新しいステージを作成し、デプロイします。

スクリーンショット 2016-12-27 0.15.21.png

「ステージエディター」で"SDKの生成"から、プラットフォーム"Javascript"を選択し、SDKの生成をクリックします。

スクリーンショット 2016-12-27 0.18.13.png

すると、SDKのファイルがダウンロードできます。


認証なしでAPIを叩くJavascriptのコードを書く(ここで一度テストし、うまくいくことを確認)

スクリーンショット 2016-12-27 0.29.18.png

SDKのZIPを解答したら、中に適当なHTMLファイルを作成し、下記のコードを書きます。(ここではAPIGatewayTest.htmlというファイルを作成しています。)

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="utf-8">
<title>API test</title>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- API用SDK -->
<script type="text/javascript" src="lib/axios/dist/axios.standalone.js"></script>
<script type="text/javascript" src="lib/CryptoJS/rollups/hmac-sha256.js"></script>
<script type="text/javascript" src="lib/CryptoJS/rollups/sha256.js"></script>
<script type="text/javascript" src="lib/CryptoJS/components/hmac.js"></script>
<script type="text/javascript" src="lib/CryptoJS/components/enc-base64.js"></script>
<script type="text/javascript" src="lib/url-template/url-template.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/sigV4Client.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/apiGatewayClient.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/simpleHttpClient.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/utils.js"></script>
<script type="text/javascript" src="apigClient.js"></script>
</head>
<body>
<!-- HTML -->
<button id="btn">ボタン</button>
<!-- Other Script -->
<script type="text/javascript">
var apigClient;
$(function(){
//API Clientを初期化
apigClient = apigClientFactory.newClient();
});
$('#btn').click(function(){
//testリソースのGetメソッドを呼び出すので、.testGet()
apigClient.testGet({}, {}, {})
.then(function(result){
// Add success callback code here.
console.log(result);
}).catch( function(result){
// Add error callback code here.
console.log(result);
});
});
</script>
</body>
</html>

ボタンをクリックすると、APIの呼び出しが成功し、Lambdaから'HelloWorld'の文字列が返ってくることを確認します。

スクリーンショット 2016-12-27 0.33.05.png


APIの認証を"なし"→"AWS_IAM"に設定する(ここで一度テストし、アクセスが拒否されることを確認)

ここで、作成したAPIの"メソッドリクエスト"に記載されているARN(Amazon Resource Name)をコピーしておきます。("arn:aws:execute-api:ap-notheast-xxxxxxxx/*/GET/test"という文字列です。)

作成したAPIのGetメソッドの"メソッドリクエスト"をクリック

スクリーンショット 2016-12-27 0.37.56.png

認証の設定を"AWS_IAM"に変更します。

スクリーンショット 2016-12-27 0.38.22.png

APIを再びDevステージにデプロイします。

スクリーンショット 2016-12-27 0.42.25.png

こうして再びボタンを押してAPIを叩いてみると、403エラーが返ってくることを確認します。認証をAWS_IAMに設定したため、アクセスが拒否されています。(CORSに問題があるというエラー文ですが、問題ありません。)

スクリーンショット 2016-12-27 0.43.58.png


Cognito Identity Poolを作成

「Cognito」で"Manage Federated Identities"をクリック > "Create Identity Pool"をクリック

次の画面では、Identity Poolの名前(ここでは"MyTestIdentity")を入力し、"Enable access to unauthenticated identities"にチェックを入れた上で"Create Pool"をクリックします。

スクリーンショット 2016-12-27 0.47.58.png

次の画面で確認できる"Identity Pool ID"をコピーしておきます。

スクリーンショット 2016-12-27 1.14.44.png


APIの実行を許可するIAMを作成し、UnauthUserのRoleにアタッチする

「IAM」で"ロール"を開くと、さきほど"MyTestIdentity"というIdentity Poolを作成した時にCognito_MyTestIndentityUnauth_Roleという未認証ユーザー用のRoleと、Cognito_MyTestIndentityAuth_Roleという認証済みユーザー用のRoleが2つ作成されているのが確認できます。今回はCognitoの未認証ユーザーに対してAPIへのアクセス権限を与えるので、"Cognito_MyTestIndentity Unauth _Role"をクリックします。

"ロールポリシーの作成"をクリックします。

スクリーンショット 2016-12-27 0.57.36.png

"Policy Generator"にチェックを入れ、"選択"をクリックします。

スクリーンショット 2016-12-27 0.58.15.png

「アクセス許可の編集」では、下記のように設定します。


  • 効果:許可

  • AWSサービス:Amazon APIGateway

  • アクション:Invoke

  • Amazonリソースネーム(ARN):先ほどコピーしたGETメソッドのARN

これらを設定し、"ステートメントを追加"をクリックします。

スクリーンショット 2016-12-27 1.03.20.png

下記のようになっていれば、OKです。"次のステップ"をクリックします。

スクリーンショット 2016-12-27 1.03.31.png

次の確認画面では何もせず、"ポリシーの適用"をクリックします。

スクリーンショット 2016-12-27 1.06.38.png

これで、UnauthUserに対してAPIを実行する許可を与えることができます。


Cognito UnauthUserのCredentialでAPIを叩くJavascriptのコードを書く(完成)

さきほど作成したHTMLファイルを、下記のように編集します。



  • <script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.19.min.js"></script>でAWS SDK for Javascriptを読み込み


  • AWS.config.credentialのパラメーターにさきほどコピーしたIdentity Pool IDを貼り付ける


  • AWS.config.credential.get()によって得られるaccessKeyIdとsecretAccessKeyとsessionToken、及びregionをapigClientに設定して初期化する。

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="utf-8">
<title>API test</title>
<!-- AWS SDK -->
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.19.min.js"></script>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- API用SDK -->
<script type="text/javascript" src="lib/axios/dist/axios.standalone.js"></script>
<script type="text/javascript" src="lib/CryptoJS/rollups/hmac-sha256.js"></script>
<script type="text/javascript" src="lib/CryptoJS/rollups/sha256.js"></script>
<script type="text/javascript" src="lib/CryptoJS/components/hmac.js"></script>
<script type="text/javascript" src="lib/CryptoJS/components/enc-base64.js"></script>
<script type="text/javascript" src="lib/url-template/url-template.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/sigV4Client.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/apiGatewayClient.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/simpleHttpClient.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/utils.js"></script>
<script type="text/javascript" src="apigClient.js"></script>
</head>
<body>
<!-- HTML -->
<button id="btn">ボタン</button>
<!-- Other Script -->
<script type="text/javascript">
var apigClient;
$(function(){
//AWS Credentialの初期化
AWS.config.region = 'ap-northeast-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'ap-northeast-XXXXXXXXXXXXXXXXXXXXXXX' //Identity Pool Idを貼り付ける(ARNではない。)
});
AWS.config.credentials.get(function(err){
if(!err){
//API ClientにaccessKey,secretKey,sessionToken,regionを設定し、初期化します。
apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId,
secretKey: AWS.config.credentials.secretAccessKey,
sessionToken: AWS.config.credentials.sessionToken,//大事
region: 'ap-northeast-1'//大事
});
}else{
console.log(err);
}
});
});
$('#btn').click(function(){
apigClient.testGet({}, {}, {})
.then(function(result){
// Add success callback code here.
console.log(result);
}).catch( function(result){
// Add error callback code here.
console.log(result);
});
});
</script>
</body>
</html>

これでボタンをクリックすると、APIの呼び出しに成功し、Lambdaから'Hello World'の文字列が返ってくることを確認できます。

スクリーンショット 2016-12-27 1.20.01.png


ポイント


  • CORSを有効化したり認証を変更したりなど、APIの設定をいじった後は、APIをデプロイするのを忘れない。

  • APIGatewayで生成したJavascript用のSDKは、超絶面倒くさいSignature V4の署名付きリクエストをやってくれる。

  • apigClientにアクセスキーとシークレットキーを設定する時、sessionTokenとregionはドキュメントではオプションとなっているが、ここを設定しないと動かないので全て設定する。(ここでだいぶハマった。)


参考URL

API Gateway リソースの CORS を有効にする

API 実行アクセス権限の IAM ポリシーの例

API Gateway コンソールを使用して API の SDK を生成する

API Gateway で生成した JavaScript SDK を使用する

aws-api-gateway-client