Amazon Cognitoを独自認証(Developer Authenticated Identities)で利用する


お約束ですが、これは個人のメモ/意見であり、所属する組織を代表するものではありません。

## [Amazon Cognito?](http://aws.amazon.com/cognito/)

Amazon Cognitoは、主にモバイルデバイスやブラウザから直接AWSの各種サービスを使う際に利用される、認証認可のためのサービス。実際にこの機能はIAM Roleとの組み合わせで実現されており、以下の2ステップで動く

1. 認証: Facebookなどのソーシャル系のサービスもしくはOpenID Connect Provider、ユーザー独自の認証サービスでユーザーを認証する。（このときにCognitoIDというものが払い出される。ユーザーごとにユニーク。）
2. 認可: 認証されたユーザーに対して、予め指定されたIAM Roleの権限を認可する。

認証のオプションは以下のとおり

1. ソーシャルサービス
 - Facebookアプリ
 - Google
 - Amazon
2. OpenID Connect Provider
 - OpenID Connectに対応している認証プロバイダなら基本的に接続可能。日本だと[mixi](http://developer.mixi.co.jp/connect/mixi_graph_api/api_auth/)とか？
3. 自前認証サービス(Cognito用語ではDeveloper Authenticated Identities)
 - 完全に自前の認証サービス

## Developer Authenticated Identity

今回はその中でも自前認証サービスを利用することのできるDeveloper Authenticated Identityについて書いていく。大まかな認証認可の流れを絵にすると以下のようなイメージ。
![developerAuthenticatedIdentity.png](https://qiita-image-store.s3.amazonaws.com/0/10292/ced0b607-7398-08d4-72c1-0aaddc6d3900.png "developerAuthenticatedIdentity.png")

ステップは認証と認可に分かれていて
1. 認証: 自前の認証サーバーでユーザーを認証し、OKだったらCognitoのGetOpenIdTokenForDeveloperIdentityというAPIを呼び出し、CognitoIdとOpenIdTokenを取得し、クライアントに返してやる。
2. 認可: クライアントはこれをもとにSTS(Security Token Service)のAssumeRoleWithWebIdentityというAPIを呼び出し、予め関連付けられているIAM Roleの権限を認可されたAccessKey, SecretKey, Tokenを取得する

という感じになる。

## ブラウザ + Javascript SDKをクライアントとして実装する

上の流れを実装してみる。なお、ブラウザ + Javascriptと断りを入れているとおり、iOS SDKやAndroid SDKの場合、認可に利用するクラス名が若干違ったりするので注意。

まずは認証側で自前の認証サーバーの部分。node.jsで実装するならこんなかんじ。

```javascript:authServer.js
var aws = require('aws-sdk');
aws.config.region = 'us-east-1';
var cognitoidentity = new aws.CognitoIdentity();
var identityPoolId = 'YOUR_COGNITO_ID';

//まずは自前の認証
authUser(user,password,function(err,data){
	if(err){doSomeErrorHandling(err)
	}else{
		//次にGetOpenIdTokenForDeveloperIdentityを呼び出す
		var params = {
			IdentityPoolId: identityPoolId,
			Logins: {
				myapp: user //アプリ名とユーザー名(任意の定義)を渡す
			}
		};
		cognitoidentity.getOpenIdTokenForDeveloperIdentity(params,function(err,data){
    		if(err){console.log(err);
    		}else{
    			//クライアントにCognitoIdとOpenIdTokenを返してやる
    			returnResponse(data);
    			//data = {
    			//	IdentityId: 'YOUR_COGNITO_ID',
    			//	Token: 'YOUR_TOKEN'
    			//};
    		}
    	});
	}
});
```

次にブラウザ側はこんなかんじ。

```javascript:client.js
var aws = require('aws-sdk');
aws.config.region = 'us-east-1';
var roleArn = 'YOUR_ROLE_ARN';

//上記のサーバー側のauthUserを呼び出すものとする
authWithMyServer(user,password,function(err,data){
	if(err){doSomeErrorHandling(err)
	}else{
		//返って来たTokenを利用して認可リクエスト。これはなぜか同期型のAPI。
		aws.config.credentials = new aws.WebIdentityCredentials({
           RoleArn: roleArn,
           WebIdentityToken: data.Token
        });
        
        //上記が問題なく終了したらあとは好きなAPIを呼び出せる
        //例えばDynamoDBのScanをしてみる
        //現在Cognitoは東京リージョンにはないが、取得した認可は東京リージョンでも利用可能
        aws.config.region = 'ap-northeast-1';
        var dynamodb = new aws.DynamoDB();
        dynamodb.scan({
            TableName: 'YOUR_TABLE_NAME'
        },function(err,data){
            if(err) console.log(err);
            else console.log(data);
        });
	}
});

```

## あれ？CognitoIdは?

ということでCognitoを使った認証認可のイメージを順を追って見てみたが、実はCognitoIdは取得しただけで利用されていない。これはどこで何に使われているかというと・・・

まずCognitoIdの払い出しについてから。これは先ほど、GetOpenIdTokenForDeveloperIdentityの引数に以下のような値を渡していたが、ここで渡されるuserの値に対してユニークなIDが払い出される。ここで渡す値は任意の文字列なので、自前の認証サービスのユーザーIDやユーザーIDのハッシュ値などをわたしてあげればよいということになる。で、渡されたCognito側はこの値が初めて渡ってくるものであれば、渡ってきたuserの値に対して新規のCognitoIdを関連付けて払い出す。同じuserが次回以降わたってきた際には対応するCognitoIdが返される。

```javascript
		var params = {
			IdentityPoolId: identityPoolId,
			Logins: {
				myapp: user //アプリ名とユーザー名(任意の定義)を渡す
			}
		};
```

で、このCognitoIdが何に使えるかというと・・ Loginsが連想配列になっているのを不思議に思ったひともいると思うのだが、下記のようなイメージで、複数の認証サービスのIDを統合するためのIDとしてCognitoIdを使えますよ・・・という話。

```javascript
"Logins": {
    "graph.facebook.com": "FB_TOKEN",
    "accounts.google.com": "GOOGLE_TOKEN",
    "www.amazon.com": "AMZN_TOKEN",
    "login.mycompany.myapp": "USER_IDENTIFIER"
}
```

あとは、Cognitoというサービス自体が払いだしたID数を下記のようにダッシュボードで数値化してくれるという機能もあったりする。
![CognitoDashboard.png](https://qiita-image-store.s3.amazonaws.com/0/10292/19c8d3ec-b910-e088-3b1c-f36642589d30.png "CognitoDashboard.png")

## まとめ

ということで、このポストではCognito自体についてと、自前認証サービス(Developer Authenticated Identity)の仕組みと実装方法について書いてみました。

## ライセンス

この記事中に記載されているコードスニペットはすべてMITライセンスにて提供されるものとします。