2014年8月24日

Cognito を Web サイトで使う

AWS Mobile Development のブログに Cognito を website で使う方法が紹介されていました。
Use Amazon Cognito in your website for simple AWS authentication

いつのまにか Cognito はモバイルだけでなく、他の SDK でも使えるようになっていました。
Cognito のドキュメントには Identity API Reference と Sync API Reference が用意されていました。

Cognito
Identity API Reference
Sync API Reference

Identity API


Identity API は Cognito の認証系を扱う API となります。

たとえばこんなコードでその IdentityPool に保持されている IdentityId を取得できたりします。
public static void main(String[] args) {
  AWSCredentials credentials = new BasicAWSCredentials(
      ACCESS_KEY,
      SECRET_KEY
  );
 
  AmazonCognitoIdentity identityClient = new AmazonCognitoIdentityClient(credentials);

  ListIdentitiesRequest listIdentitiesRequest = new ListIdentitiesRequest()
    .withIdentityPoolId(IDENTITY_POOL_ID).withMaxResults(60);
  ListIdentitiesResult result =  identityClient.listIdentities(listIdentitiesRequest);

  for (IdentityDescription identityDescription : result.getIdentities()) {
   String identityId = identityDescription.getIdentityId();
   System.out.println(identityId);
  }
 }

この IdentityId は GetId API で取得できます。

サンプルはブログにあるとおりです。
// anonymous AWS credentials で初期化します
 AmazonCognitoIdentity identityClient = new AmazonCognitoIdentityClient(
   new AnonymousAWSCredentials());

 // ID の発行をします。この ID はユーザで一意の ID になります
 GetIdRequest idRequest = new GetIdRequest();
 idRequest.setAccountId(AWS_ACCOUNT_ID);
 idRequest.setIdentityPoolId(IDENTITY_POOL_ID);

 // Facebook のセッションキーを設定(設定が無い場合には Unauthenticated になります)
 if (facebookSessionKey != null) {
  Map providerTokens = new HashMap();
  providerTokens.put("graph.facebook.com", facebookSessionKey);
  idRequest.setLogins(providerTokens);
 }

 GetIdResult idResp = identityClient.getId(idRequest);

 String identityId = idResp.getIdentityId();

AnonymousAWSCredentials はソースを見るとシークレットキー・アクセスキーが null となってます。
署名無しで API が使えるのですね。

STS を使用して identityId から OpenIdToken を取得して Cognito に渡す Creential を作成します。
このコードだと Authenticated も Unauthenticated も同じ Role になってしまいますが、実際は mobile のときのように Role 使い分けないといけないです。
// OpenIdToken を取得
 GetOpenIdTokenRequest tokenRequest = new GetOpenIdTokenRequest();
 tokenRequest.setIdentityId(identityId);

 if (facebookSessionKey != null) {
  Map providerTokens = new HashMap();
  providerTokens.put("graph.facebook.com", facebookSessionKey);
  tokenRequest.setLogins(providerTokens);
 }
 GetOpenIdTokenResult tokenResp = identityClient.getOpenIdToken(tokenRequest);

 String openIdToken = tokenResp.getToken();

 // STS を使って role を取得
 AWSSecurityTokenService stsClient = new AWSSecurityTokenServiceClient(
   new AnonymousAWSCredentials());
 AssumeRoleWithWebIdentityRequest stsReq = new AssumeRoleWithWebIdentityRequest();
 stsReq.setRoleArn(AWS_ROLE_ARN);
 stsReq.setWebIdentityToken(openIdToken);
 stsReq.setRoleSessionName("AppTestSession");

 AssumeRoleWithWebIdentityResult stsResp = stsClient.assumeRoleWithWebIdentity(stsReq);
 Credentials stsCredentials = stsResp.getCredentials();

 // 一時 Credential を作成
 AWSSessionCredentials sessionCredentials = new BasicSessionCredentials(
   stsCredentials.getAccessKeyId(),
   stsCredentials.getSecretAccessKey(),
   stsCredentials.getSessionToken());

Sync API


Sync API を使えば Dataset の取得や更新ができるようです。

Credential は前述のコードで作成しています。

前述のブログを参考に作成したところ、Unauthenticated identityId では参照・更新ができました。
Facebook で認証した identityIdに対して認証無しで実行した場合には例外が発生してしまいました。(当たり前といえば当たり前ですが)

// syncClient に AWSSessionCredentials を設定
 AmazonCognitoSync syncClient = new AmazonCognitoSyncClient(sessionCredentials);

 if (identityDescription.getLogins() != null) {
  for (String login : identityDescription.getLogins()) {
   System.out.println(login);
  }
 }

 // データセットを取得
 ListDatasetsRequest listDatasetsRequest = new ListDatasetsRequest()
  .withIdentityPoolId(IDENTITY_POOL_ID).withIdentityId(identityId);
 ListDatasetsResult datasetsResult = syncClient.listDatasets(listDatasetsRequest);
 System.out.println(datasetsResult);

 // レコードを取得
 ListRecordsRequest listRecordsRequest = new ListRecordsRequest()
   .withIdentityPoolId(IDENTITY_POOL_ID)
   .withIdentityId(identityId)
   .withDatasetName("test");
 ListRecordsResult listRecordsResult = syncClient.listRecords(listRecordsRequest);
 System.out.println(listRecordsResult);

 String token = listRecordsResult.getSyncSessionToken();
 
 // レコードの更新
 UpdateRecordsRequest updateRecordsRequest = new UpdateRecordsRequest()
   .withDatasetName("test")
   .withIdentityId(identityId)
   .withIdentityPoolId(IDENTITY_POOL_ID)
   .withSyncSessionToken(token)
   .withRecordPatches(
     new RecordPatch()
       .withKey("test")
       .withValue("value")
       .withOp(Operation.Replace)
       .withSyncCount(listRecordsResult.getDatasetSyncCount()));
 syncClient.updateRecords(updateRecordsRequest);

これでモバイルアプリと Web アプリの連携ができますね!

Amazon Cognito のサンプルを動かす

Amazon Cognito という AWS の ID 管理及びデータ同期のサービスがあります。
このサービスのサンプルを使ってみます。

サンプルソースの取得


サンプルソースはこちらにあります。
https://github.com/awslabs/aws-sdk-android-samples/tree/master/CognitoSyncDemo

こちらから他のサンプルもまとめて取得するとよいでしょう。

README.md に従って Eclipse インポートします。

プロジェクトの libs ディレクトリに mobile SDK の jar ファイルをコピーします。
aws-android-sdk-X.X.X-core.debug.jar
extras/aws-android-sdk-X.X.X-cognito.debug.jar

project target と FacebookSDK のパスを設定します。
https://github.com/awslabs/aws-sdk-android-samples/blob/master/CognitoSyncDemo/project.properties
target には API Level を設定します。

FacebookSDK はこちらにあるものを使いました。Facebook Login を使わない場合でもコンパイルに必要です。
https://github.com/awslabs/aws-sdk-android-samples/tree/master/FacebookSDK

Facebook App の登録

Facebook App の登録をします。

https://developers.facebook.com/apps/から「Create a New App」を押します。
Display Name と カテゴリは適当に入力します。
App ID が発行されます。


Settings から 「+ Add Platform」を押して、Android を選択します。
Package Name、Class Name を設定します。
Package Name: com.amazonaws.cognito.sync.demo
Class Name: com.amazonaws.cognito.sync.demo.MainActivity


Cognito の設定


Cognito の Identity Pool を作ります。
https://console.aws.amazon.com/cognito/

Configure Identity Providers の Facebook App ID に先ほど取得した Facebook のアプリケーション ID を設定します。
ログイン無しでも使えるよにするには「Enable Access to Unauthenticated Identities」にチェックを入れてください。

Role はアプリ側で紐づけるため後で作成しても問題ありませんが、とりあえず作成します。


ソースに各種 ID を設定


CognitoSyncClientManager.java の定数を更新します。
https://github.com/awslabs/aws-sdk-android-samples/blob/master/CognitoSyncDemo/src/com/amazonaws/cognito/sync/demo/CognitoSyncClientManager.java

AWS_ACCOUNT_ID : AWS アカウント ID
IDENTITY_POOL_ID : 作成した Cognito の Identity Pool ID
UNAUTH_ROLE_ARN : 認証なしユーザに割り当てる IAM Role の ARN
AUTH_ROLE_ARN : 認証済みユーザに割り当てる IAM Role の ARN
/**
     * account id and pool id associated with the app
     */
    private static final String AWS_ACCOUNT_ID = "123456789012";
    private static final String IDENTITY_POOL_ID = "us-east-1:00000000-0000-0000-0000-000000000000";
    /**
     * the role arn to be assumed. You can provide a role arn for unauthorized
     * user and one for authorized.
     */
    private static final String UNAUTH_ROLE_ARN = "arn:aws:iam::123456789012:role/Cognito_teketouUnauth_DefaultRole";
    private static final String AUTH_ROLE_ARN = "arn:aws:iam::123456789012:role/Cognito_teketouAuth_DefaultRole";

res/values/strings.xml の facebook_app_id にを設定します。
https://github.com/awslabs/aws-sdk-android-samples/blob/master/CognitoSyncDemo/res/values/strings.xml
<resources>

    <string name="app_name">CognitoSyncDemo</string>
    <string name="facebook_app_id">123456789012345</string>

</resources>

サンプル起動


アプリを起動します。


Facebook Login ボタンで Facebook の認証ページが表示されます。


認証に成功すると、ボタンが消えます。


Cognito のコンソールでも、Facebook の認証がされたことがわかります。


認証ができたので、次は API を実際に実行することろを試したいです。