IAM認証(AWS SignatureV4)でAPIGatewayをNode.jsから呼び出す

APIGatewayのAPIを呼び出す際に独自のAPIキーというので認証ができますが、それは推奨されておらず、IAMによる認証が推奨されています。(APIキーはあくまで計測目的で利用せよとのこと）

じゃあIAMで認証ってなんなのってのが実はよくわかっておらず、調べてやってみたのでメモ。

# 参考

+ [Node.jsでAWSのAPIで認証するやつ書いた](http://r7kamura.hatenablog.com/entry/2015/07/31/024304)
+ [AWS Black Belt Tech シリーズ 2015 - Amazon API Gateway](http://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-tech-2015-amazon-api-gateway)
+ [AWS REST リクエストの署名についてメモ](https://siguniang.wordpress.com/2014/11/23/evolution-of-aws-signing-signatures/)

# 結論
+ AWS Signature V4を使って認証をすることでAPIGatewayをIAM認証で呼び出せる
+ 認証はリクエストヘッダにHost、DateまたはX-Amz-Date、Authorizationを追加することで行う。Authorizationにはアクセスキーやシークレットキーなどを使って決められた手順で加工したものを付与する
+ 決められた手順というのが結構面倒なので利用できるライブラリなどがあればそれを利用したほうが良さそう
+ ない場合にはその他言語のライブラリを参考に書くと良さそう

# ApiGatewayのIAM認証とは

以下スライドシェアにあるように実際にはAWS Signature V4ってのを使ってやる必要が有るようです。

[AWS Black Belt Tech シリーズ 2015 - Amazon API Gateway](http://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-tech-2015-amazon-api-gateway)


じゃあ実際にそのAWS Signature V4ってどうやってやるのか、というのは以下の記事が参考になりました。

[AWS REST リクエストの署名についてメモ](https://siguniang.wordpress.com/2014/11/23/evolution-of-aws-signing-signatures/)

概要だけいうと

+ リクエストヘッダに認証情報を付与する
+ 必須なのはHost,x-amz-dateもしくはDate、及びAuthorization。Content-TypeとかContent_Lengthなどはあってもなくても大丈夫
+ AuthorizationにはAWSのアクセスキーやシークレットキーなどの情報を使い、決められた手順で加工したものを付与する

ただ、その決められた手順というのが結構面倒なので（先ほどのリンクで詳細に記載されています）個人的にはライブラリの実装などを見た方が分かりやすいです。。。。

# ライブラリを使ってAPIGatewayにNode.jsからアクセスしてみる

まず、APIGatewayを作って何かしら返すようにします。
以下を参考にモックを返すようにしてみました。

[Amazon API GatewayでMockを定義してみる](http://qiita.com/Keisuke69/items/0852d536110b2fa6e087)

なお、認証はNONEではなく、IAMとしてIAM認証にします。
上記状態でブラウザなどからアクセスできないことを確認します。

次にnpmでNode.jsを使う際にAWS Signature V4を簡易に利用できるライブラリをインストールします。

今回は以下を使わせてもらいました。

[Node.jsでAWSのAPIで認証するやつ書いた](http://r7kamura.hatenablog.com/entry/2015/07/31/02a4304)

```
$npm install --save aws-signer-v4
```

現在、ライブラリではAPIGatewayの呼び出しはできなかったので少し変えます。(その他RESTのAPIは呼び出しができました）

```
$vi node_modules/aws-signer-v4/index.js
```

```lang:index.js
 Sign.prototype._getService = function () {
-  return url.parse(this.url).host.split('.', 2)[0];
+  var host = url.parse(this.url).host.split('.');
+  if(host[1] === 'execute-api') {
+    return host[1];
+  } else {
+    return host[0];
+  }
 };
```

Authorizationの内容を作る際にAWSのどのサービスを利用するか指定する必要があり、APIGatewayの呼び出し(REST APIによるAPIGateaway自体の操作ではない)には **execute-api** と指定する必要があり、それを返却できるようにしています。

次にライブラリを使ってコードを書きます。
なお、以下をコピペして使う場合、下記を実施しておいてください。

+ 環境変数AWS_ACCESS_KEY_IDにアクセスキーを設定しておくこと
+ 環境変数AWS_SECRET_ACCESS_KEYにシークレットキーを設定しておくこと
+ 変数のhost及びpathは適宜自分の環境に合わせること



```lang:apigateway.js
var https = require('https');

var host = 'xxxxx.execute-api.us-east-1.amazonaws.com'
var path = '/prod'

var options = {
  host: host,
  port: 443,
  path: path,
  method: 'GET',
  headers: {
    Date: new Date().toISOString(),
    Host: host
  },
};

// Create a Sign instance
var Sign = require('aws-signer-v4');
var sign = new Sign({
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  body: options.body,
  headers: options.headers,
  method: 'GET',
  region: 'us-east-1',
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  url: 'https://' + host + path
});

options.headers.Authorization = sign.toString();
console.log(sign.toString());

var req = https.request(options, function(res) {
  //console.log("statusCode: ", res.statusCode);
  //console.log("headers: ", res.headers);

  var body = '';
  res.on('data', function(chunk) {
    body += chunk;
  });
  res.on('end', function() {
    console.log(body);
  });
});
req.end();

req.on('error', function(e) {
  console.error(e);
});

```

実行してみます。分かりやすいように追加したAuthorizationヘッダの内容も表示しています。

```
$node apigateway.js
AWS4-HMAC-SHA256 Credential=xxxxx/20151003/us-east-1/execute-api/aws4_request, SignedHeaders=authorization;date;host, Signature=xxxx
{
 [
  {
   "id":1,
   "title":"RESTful Webサービス"
  },
  {
   "id":2,
   "title":"Web API: The Good Parts"
  },
  {
   "id":3,
   "title":"Restlet in Action"
  },
  ]
}
```

無事取得できました！