読者です 読者をやめる 読者になる 読者になる

Amazon Cognitoについて - AWSが提案するモバイル時代のアカウント管理

モバイルファースト室の @rejasupotaro です。

AWS Summit 2014 でAWS Mobile Servicesのアップデートがありましたが、その中でも気になったAmazon Cognitoについて調べてみました。

Amazon Cognitoとは

今どきのモバイルアプリでは、単純に認証してAPIを叩くだけではなく、サービスにログインしていない状態でも一意なIDを持たせてデータを管理したり、ゲスト状態から会員登録をしたときにデータを引き継いだり、サービス間でシングルサインオンを提供したり、デバイス間で設定を共有できるようにするなど、ユーザーがどのデバイスからサービスにアクセスしてもストレスなく使えるようにするために様々な工夫を凝らす必要があります。

f:id:rejasupotaro:20140728232824p:plain

Introduction to Amazon Cognito

これらの実装は複雑になりがちで、アプリを作っていると思ったらアカウント管理のしくみ作りに多くの時間を取られていた、ということがあります。

そんな問題を解決するために開発されているのが Amazon Cognito です。

  • 一意のアイデンティティの管理
  • オフライン機能
  • デバイス間での保存と同期
  • シームレスなゲストアクセス

上記のような今のモバイルアプリには欠かせない便利な機能を、バックエンドのコードの記述やインフラストラクチャの管理をすることなく、実装・運用をすることができて、モバイルアプリ開発者は優れたアプリケーション体験を作成することに集中できる、とあります。

これは調べずにはいられませんね。

Cognitoを始める

AWSマネジメントコンソールの Services > Cognito > Create New Identity Pool から新しくIdentity Poolを作ることができます。

Amazon Cognitoはまだプレビューなので、今のところはバージニアリージョンしか選択できません。

f:id:rejasupotaro:20140729083938p:plain

Identity Poolはデータを保存するコンテナで、1つのアプリに関連付けることも、複数のアプリに関連付けることもできます。Identity Poolの中のDatasetと呼ばれるテーブルに、key-value形式でデータを保存するようになっています。ユーザーごとのDatasetのサイズの上限は20MBです。さらにユーザの1つのDatasetのサイズの上限は1MBになっていて、キーの最大数は1024個になっています。

帯域幅が限られている場合に、何度もリトライしてバッテリーの寿命とデータプランを消費させないために1つのDatasetの上限を1MBに制限しているようです。

次にRoleの設定です。未認証のユーザーとログインユーザーのそれぞれに対してRoleを割り当てることができます。

f:id:rejasupotaro:20140728232529p:plain

これによって、S3やDynamoDBなどにユーザー専用の領域を用意してクライアントから直接読み書きさせるなど、AWSリソースへのアクセスを柔軟に制御することができます。

f:id:rejasupotaro:20140728232532p:plain

設定はこれで完了です。Amazon Cognito APIを直接叩くこともできますが、せっかくなのでAmazon Mobile SDKを使います。 Download Starter Code (Android and iOS) から自分のaccountId、identityPoolId、unauthRoleArn、authRoleArnが組み込まれたサンプルコードをダウンロードすることができます。

AndroidからCognitoを使ってみる

設定完了ページに自分のaccountId、identityPoolId、unauthRoleArn、authRoleArnが組み込まれたCogniteCredentialsProviderの初期化のコードがあるので、コピペします。

CognitoCredentialsProvider cognitoProvider = new CognitoCredentialsProvider(
            context,
            "xxxxxxxxxx",
            "us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX",
            "arn:aws:iam::XXXXXXXXXX:role/YourRoleName",
            "arn:aws:iam::XXXXXXXXXX:role/YourRoleName");

書き込む値が決まっているときには、保守性の向上や静的解析を活かすためにDatasetの操作クラスを作って、そこからデータを操作するのが定石ですが、一旦動かしてみるだけなので文字列をベタ書きしていきます。

CognitoSyncClient syncClient = new DefaultCognitoSyncClient(context, COGNITO_POOL_ID, cognitoProvider);
profileDataset = syncClient.openOrCreateDataset("profile");

書き込むときは Dataset#put で読み込むときは Dataset#get です。putするとAndroidではローカルキャッシュのSQLiteに保存されて Dataset#synchronize でサーバーと同期します。

profileDataset.put("name", name);
String name = profileDataset.get("name");

データの同期

Dataset#synchronize を呼ぶとリモートの変更差分がpullされて、それからデバイスでの変更差分をサーバにpushするしくみになっています。

dataset.syncronize(syncCallback);

pullした際にローカルキャッシュと比較して、コンフリクトが起こったときはSyncCallbackでハンドリングできます。SyncCallbackの以下のメソッドをオーバーライドします。

  • onSuccess
  • onFailure
  • onConflict
  • onDatasetDeleted
  • onDatasetsMerged
private Dataset.SyncCallback syncCallback = new Dataset.SyncCallback() {
        @Override
        public void onSuccess(Dataset dataset, List<Record> records) {
            // Recordはkey、value、lastModifiedBy、deviceLastModifiedDate、lastModifiedDate、syncCountを持っています
            }
        }

        @Override
        public boolean onConflict(Dataset dataset, List<SyncConflict> syncConflicts) {
        // SyncConflictはgetLocalRecordとgetRemoteRecordを持っていて
        // 手動でRecordを比較してどちらを採用するか選ぶこともできます
        return false;
    }

    ...
};

onConflictはデフォルトでは最新の変更を採用するようになっています。

SDKのCognitoの実装

aws/aws-sdk-android-v2aws/amazon-cognito-android はGitHubでソースが公開されています。

amazon-cognito-androidを読んでみたところ、下のような構成になっていました。

f:id:rejasupotaro:20140728232508p:plain

CognitoCredentialsProviderがidentityId 、accessKey 、secretKey、sessionTokenなどの認証情報をSharedPreferencesで管理しています。

SQLiteLocalStorageではdatasetsテーブルとrecordsテーブルを管理しています。 Dataset#synchronized が呼ばれるとRemoteDataStorageとLocalStorageがmergeされて、AmazonCognitoSyncServiceClient経由で送信されます。RemoteDataStorageとLocalStorageはinternalパッケージの中にあって、開発者はどこに保存するかということを意識せずにコードを書くことができます。

必要なパーミッションに ACCESS_NETWORK_STATE があったので追ってみたら Dataset#synchronizeOnConnectivity というメソッドは、同期が要求されたときにオフラインだった場合に、再びネットワークに接続されるまで処理をペンディングにして、再接続された際に同期を実行するようになっていました。 これによってデバイスがオンラインかオフラインかに関わらずデータを保存することができます。

まとめ

Amazon Cognitoはアカウント管理の複雑な処理を肩代わりさせるのに有用なサービスだと思います。

今のところログインプロバイダがFacebookとGoogleとAmazonのみなので、任意のログインプロバイダが使えるようになったり、課金状態にあるユーザーにのみ閲覧権限を与えるというようなアクセス制御をするために細かくRoleの設定ができるようになったりすると、さらに便利になるかなと思います。

Amazon Cognitoはまだプレビューなので、期待しながらAmazon Mobile Servicesの正式リリースを待ちます。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/