Claudia.jsでサーバレスアーキテクチャでのOAuth対応を簡単に実装する

[Claudia.js](https://claudiajs.com)はAWS上でAPI GatewayとAWS Lambdaによるサーバレスアーキテクチャを簡単に実現できるNode.js製のフレームワークです。

この際、Facebook, Google, Twitterの用な外部の認証プロバイダから取得したOAuth Access TokenをAPI Gatewayのレイヤーで検証して問題なければ裏のLambdaに投げたいケースが多いと思います。

Claudia.jsを使えばこのような処理が10分もあれば実装できます。

[関連記事]

- [API GatewayとLambdaでOAuth](http://qiita.com/sl2/items/ff1d57a066d08d5b2f93)
  - Claudia.jsを使わない方法で面倒なのでオススメしません。

- [Swift3でFacebook SDKを使う](http://qiita.com/sl2/items/3f1d83b20c3072ba537f) 
  - このように取得したFacebookのAccess Tokenをチェックしたい場合です。

## 準備

#### 1. Claudia.jsをインストールします。

```sh
npm install -g claudia
```
	
####  2. プロジェクトを作成します。
	
```sh
mkdir [Proj Name] && cd [Proj Name]
npm init
npm install --save claudia-api-builder
npm install --save aws-sdk
npm install --save axios
```

## 実装

以下のようなフロントエンドアプリを実装してみます。エラー処理や詳細は割愛します。また、Hello World的なものはCloudia.jsの[Tutorial](https://claudiajs.com/tutorials/index.html)をみた方が手っ取り早いです。

1. フロントエンドアプリがSNSの認証認可APIからOAuthのアクセストークンを取得する。(略)
2. フロントエンドアプリがアクセストークンを付与してAWS API Gatewayにリクエストを投げる。(略)
3. AWS API GatewayがSNSの認証認可APIを用いてアクセストークンを検証する。
4. AWS API GatewayがFaaSの関数を呼び出す。
5. AWS Lambdaの関数がAWS DynamoDBのテーブルからデータを取得してフロントに返す。

### 1. カスタム認証の作成

#### 1. カスタム認証用のLambda関数を作成します。

`authorizer.js`を作成します。Access Token検証APIを呼び出して有効な場合にAPI実行権限を付与しています。

```javascript
var axios = require('axios');
	
//API実行ポリシーの生成
var generatePolicy = function(authToken, methodArn, context) {
	var awsInfo = methodArn.split(':');
	var region = awsInfo[3];
	var accountId = awsInfo[4];
	
	var apiInfo = awsInfo[5].split('/');
	var restApiId = apiInfo[0];
	var stage = apiInfo[1];
	var method = apiInfo[2];
	
	//アクセスさせたいリソース
	var resource = 'arn:aws:execute-api:' + region + ':' + accountId + ':' + restApiId + '/' + stage  +'/' + method + '/*';
	
	return {
		'principalId': authToken.split('-')[0],
		'policyDocument': {
			'Version': '2012-10-17',
			'Statement': [{
				'Effect': 'Allow',
				'Action': ['execute-api:Invoke'],
				'Resource': [resource]
			}]
		}
	};
};
	
var url = "Access TokenのValidateを行うAPIのURL";
	
//Tokenの検証とポリシーの付与
exports.auth = function auth(event, context, cb) {
  axios.get(url, {
    params: {
      access_token: event.authorizationToken //HTTP headerのAuthorizationキーの値
    }
  }).then(function (res) {
    var is_valid = true|false; //Tokenの有効性を検証して結果を格納
    if (is_valid) {
	   cb(null, generatePolicy(event.authorizationToken, event.methodArn));
    } else {
	   cb("Invalid Access Token");
    }
  });
};
```

#### 2. デプロイ

`claudia`コマンドでカスタム認証用のLambda関数をAWSにデプロイします。

```
claudia create --name custom-authorizer --region us-east-1 --handler authorizer.auth --config claudia-auth.json
```

- `--name`: AWS Lambda上の関数名
- `--region`: AWSのリージョン名
- `--handler`: カスタム認証の関数名
- `--config`: Claudia.js用の設定ファイル名(自動作成されるので任意)

```js
{
  "lambda": {
    "role": "custom-authorizer-executor",
    "name": "custom-authorizer",
    "region": "us-east-1"
  }
}
```


### 2. APIとLambda関数の作成

#### 1. APIとLambda関数を作成します。

`app.js`という名前にします。
	
```javascript
var ApiBuilder = require('claudia-api-builder');
var api = new ApiBuilder();
    
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
    
api.registerAuthorizer('apiGatewayAuthorizer', {
  lambdaName: 'custom-authorizer', //AWS Labmda上のカスタム認証の名前
  headerName: 'Authorization' //Tokenを格納するHTTP headerのKey名
});
    
api.get('/news', function (req) {
  return dynamodb.get({
    TableName:'news',
    Key:{ "id": req.headers.id }
  }).promise().then(function(res) {
    return res.Item;
  });
},{ 
  customAuthorizer: 'apiGatewayAuthorizer' 
});
    
module.exports = api;
```

#### 2. デプロイ

`claudia`コマンドでAWSにデプロイします。設定ファイル`claudia.json`が自動で作られます。

```
claudia create --region us-east-1 --api-module app
```

```
{
  "lambda": {
    "role": "myproj-executor",
    "name": "myproj-proj",
    "region": "us-east-1"
  },
  "api": {
    "id": "[PID]",
    "module": "app",
    "url": "[エンドポイントのベースURL]"
  }
}
```


### 3. AWS上の設定

1. AWS DynamoDBのテーブル作成

	`news`テーブルを作成する

		aws dynamodb create-table --table-name news --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

	`id`キーで検索できるようにレコードを追加。

	```sh
	aws dynamodb put-item --table-name news --item '{"id":{"S":"id001"}, "content":{"S":"News content 001"}}'
	aws dynamodb put-item --table-name news --item '{"id":{"S":"id002"}, "content":{"S":"News content 002"}}'
			:
	```
	
2. AWS Lambdaのrole設定

	1. IAM > Roles > [作成したRole名]
	2. Attach Pollicy > `AmazonDynamoDBFullAccess`
		- 強すぎるので実際は適当なPolicyを作成


### 4. API実行

1. [Postman](https://www.getpostman.com)などでHTTPヘッダの`Authorization`キーにAccess Tokenを、`id`キーに存在するidを付与して投げます。

	Claudia.jsでAPIを作成した際にはCORSはデフォルトでONになっていますが制限することもできます。

　　[Controlling Cross-Origin Resource Sharing headers](https://github.com/claudiajs/claudia-api-builder/blob/master/docs/api.md#controlling-cross-origin-resource-sharing-headers)

### 5. アップデート

0. スクリプトを修正します。
1. `claudia`コマンドを実行します。
   
	```
	claudia update --config claudia-auth.json
	claudia update --config claudia.json
	```

## まとめ

Claudia.jsを利用すれば、外部の認証認可APIを用いたサーバレスアーキテクチャを簡単に実装できます。

より詳細な記事は[こちら](http://slowquery.hatenablog.com/entry/2016/12/31/140956)

以上