ログイン・ログアウト機能をサーバレスアーキテクチャで実装する

##本記事を読む前の注意事項
現在ではAWSにUser Poolsという機能があります。

そちらを利用してログイン・ログアウトを実装したほうが遥かに効率が良いです。[Amazon Cognito User Poolsを使って、webサイトにユーザ認証基盤を作る](http://qiita.com/horike37/items/1d522f66452d3abe1203)をご参照ください

## 概要
[会員登録機能をサーバレスアーキテクチャで実装する](http://qiita.com/horike37/items/6f2575d6a061216fe019) に引き続き、今度はログイン・ログアウトの機能をAWSで構成を組みます。

## アーキテクチャ
![ログイン・ログアウト.png](https://qiita-image-store.s3.amazonaws.com/0/65478/51797913-717a-1ef6-cc92-e267dfb6b2bf.png)

## テーブル構成
DynamoDBでテーブルを構成します。

### Usersテーブル
ユーザ情報を格納するテーブルです。ログインフォームからの情報を付きあわせて認証を行います。

| 項目 | 制約 | 備考 |
|:----------:|:-----------:|:------------:|
| email       |        Hash key |    会員IDとなるメールアドレスを保存     |
| password       |         |    パスワード     |
※実運用ではパスワードは暗号化して保存しましょう。また、登録日などの項目も運用次第で必要になると思います。

### Sessionテーブル
セッション情報を格納するテーブルです。ログインが成功したら発行したセッションIDをこのテーブルに書き込み、ユーザのログイン、ログアウトの状態を管理します。

| 項目 | 制約 | 備考 |
|:----------:|:-----------:|:------------:|
| 　session_id       |        Hash key |    セッションID     |
| email       |         |    Usersテーブルとの付き合わせ用     |
※実運用では、登録日などの項目も持たせて、セッションタイムアウト時間も設けたほうが良いです。

## Lambdaファンクション
###ログイン処理
入力値として、emailとパスワードを受け取ります。
ログインに成功すればセッションIDを返します。webシステムで使う場合はこのセッションIDをcookieに保存して引き回してあげましょう

```js:login.js
var aws = require("aws-sdk");
var doc = require('dynamodb-doc');
var crypto = require('crypto');
var dynamo = new doc.DynamoDB();

exports.handler = function(event, context) {

    if ( typeof event.email === 'undefined' || typeof event.password === 'undefined' ) {
        context.fail("Login failed");
    }
    
    if ( event.email === '' || event.password === '' ) {
        context.fail("Login failed");
    }
console.log(event.email);
    var dynamoRequest = {
        TableName: 'users',
         Key: {
            "email": event.email
        }
    };
    
    dynamo.getItem(dynamoRequest, function (err, res) {
        
        if (err) {
            console.log(err);
            return;
        } else {
            console.log(event.email);
            console.log(res);
            if ( Object.keys(res).length > 0 && res.Item.password === event.password ) {
                var date = new Date() ;
                var session_id = md5_hex(date.getTime() + event.email);
                var dynamoRequest = {
                    TableName: 'session',
                    Item: {
                        'session_id':session_id,
                        'email':event.email
                    }
                };
                
                dynamo.putItem(dynamoRequest, function(err, data){
                    if (err) {
                        console.log(err);
                    } else {
                        context.succeed({session_id:session_id});
                    }
                });
                
            } else {
                context.fail("Login failed");
            }
        }
    });
};

function md5_hex(src) {
    var md5 = crypto.createHash('md5');
    md5.update(src, 'utf8');
    return md5.digest('hex');
}
```
###ログアウト処理
入力値としてセッションIDを渡して、Sessionテーブルからレコードを削除します。

```js:logout.js
var aws = require("aws-sdk");
var doc = require('dynamodb-doc');
var dynamo = new doc.DynamoDB();

exports.handler = function(event, context) {
    var dynamoRequest = {
        TableName: 'session',
         Key: {
            "session_id": event.session_id
        }
    };
    
    dynamo.deleteItem(dynamoRequest, function (err, res) {
        if (err) {
            console.log(err);
            return;
        } else {
            context.succeed({ret:'true'});
        }
    });
}
```

## API Gateway
上記のLambdaファンクションをAPIとして設定してあげます。

## webシステムをフロントとして組み込む
S３においたHTML上でJavaScriptからこれらのAPIを叩いてシステム化を行います。

ログイン処理はこんな感じでAjaxで行います。ちなみにjQueryで書いてます

```js
$("#login").on( "click", function(){
  var data = { 
		email:$("#email").val(),
		password:$("#password").val()
		}
  $.ajax( {
	 url:'<API Gatewayエンドポイント>',
	 method: "POST",
	 crossDomain: true,
	 data: JSON.stringify(data),
	 success: function(response) {
		if ( typeof response.session_id !== 'undefined' ) {
			$.cookie("serverless_website_session_id", response.session_id, {path:"/"});
			location.href = "/mypage";
		} else if ( response.errorMessage === 'Login failed' ) {
			$('#error').text('Login failed');
		}
	 }
});
```

こんな感じで実装が行えました。
<img width="238" alt="スクリーンショット 2016-04-14 8.42.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/65478/cbb22e9e-63ab-8720-10da-133740acda85.png">
<img width="294" alt="スクリーンショット 2016-04-14 8.42.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/65478/b38b079f-5480-c3a1-1460-075249f29cf2.png">