LoginSignup
85
85

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-26

実現したいこと

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

85
85
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
85
85