モバイルファースト室の @rejasupotaro です。
AWS Summit 2014 でAWS Mobile Servicesのアップデートがありましたが、その中でも気になったAmazon Cognitoについて調べてみました。
Amazon Cognitoとは
今どきのモバイルアプリでは、単純に認証してAPIを叩くだけではなく、サービスにログインしていない状態でも一意なIDを持たせてデータを管理したり、ゲスト状態から会員登録をしたときにデータを引き継いだり、サービス間でシングルサインオンを提供したり、デバイス間で設定を共有できるようにするなど、ユーザーがどのデバイスからサービスにアクセスしてもストレスなく使えるようにするために様々な工夫を凝らす必要があります。
Introduction to Amazon Cognito
これらの実装は複雑になりがちで、アプリを作っていると思ったらアカウント管理のしくみ作りに多くの時間を取られていた、ということがあります。
そんな問題を解決するために開発されているのが Amazon Cognito です。
- 一意のアイデンティティの管理
- オフライン機能
- デバイス間での保存と同期
- シームレスなゲストアクセス
上記のような今のモバイルアプリには欠かせない便利な機能を、バックエンドのコードの記述やインフラストラクチャの管理をすることなく、実装・運用をすることができて、モバイルアプリ開発者は優れたアプリケーション体験を作成することに集中できる、とあります。
これは調べずにはいられませんね。
Cognitoを始める
AWSマネジメントコンソールの Services > Cognito > Create New Identity Pool から新しくIdentity Poolを作ることができます。
Amazon Cognitoはまだプレビューなので、今のところはバージニアリージョンしか選択できません。
Identity Poolはデータを保存するコンテナで、1つのアプリに関連付けることも、複数のアプリに関連付けることもできます。Identity Poolの中のDatasetと呼ばれるテーブルに、key-value形式でデータを保存するようになっています。ユーザーごとのDatasetのサイズの上限は20MBです。さらにユーザの1つのDatasetのサイズの上限は1MBになっていて、キーの最大数は1024個になっています。
帯域幅が限られている場合に、何度もリトライしてバッテリーの寿命とデータプランを消費させないために1つのDatasetの上限を1MBに制限しているようです。
次にRoleの設定です。未認証のユーザーとログインユーザーのそれぞれに対してRoleを割り当てることができます。
これによって、S3やDynamoDBなどにユーザー専用の領域を用意してクライアントから直接読み書きさせるなど、AWSリソースへのアクセスを柔軟に制御することができます。
設定はこれで完了です。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-v2 と aws/amazon-cognito-android はGitHubでソースが公開されています。
amazon-cognito-androidを読んでみたところ、下のような構成になっていました。
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の正式リリースを待ちます。