JKになりたい

何か書きたいことを書きます。主にWeb方面の技術系記事が多いかも。

CognitoIDPool+CognitoUserPool+外部IDProviderで独自認証とSNS認証を実装し、アカウントに対してDynamoDBの行レベルアクセス制限をかける

内容

メールアドレス+パスワード認証、Facebook認証によりサインインし、AWSサービスの一時的なアクセス権限を取得する。

それを使って、DynamoDBにアクセスしてデータを取得する。

ただし、DynamoDBには自分のIDに該当するデータしか取得することはできないように制限する。

以上を実現しました。

用語

CognitoIDプール(フェデレーティッドアイデンティティ

ユーザに対してユニークなIDを生成する。

このIDにより、権限が制限された一時的なAWS認証情報を取得して、別のAWSサービスにアクセスすることができる

CognitoUserプール

ユーザのサインイン、サインアップなどの機能を提供する。

多要素認証や二段階認証、プロフィール管理など便利な機能が多い。

IDプール外部IDプロバイダー(今回はFacebook

CognitoIDプールから得るユニークなIDをFacebookなどのソーシャルアカウント認証に基づいて取得するもの

サンプルアプリのリポジトリ

実装したコードは以下に置いてあります。

Info.plistにあるFacebookAppID/FacebookDisplayNameとConstant.swiftにある定数は自分の環境のものに書き換えてください。

github.com

ロジックフロー

具体的な実装に関してはリポジトリを参照してください。

(1) LoggedInViewControllerが生成される

storyBoardからrootViewControllerに指定してあるLoggedInViewControllerがまずはインスタンス化されて画面に表示されます


(2) LoggedInViewControllerのviewDidLoadで「CognitoClient.sharedInstance.credentialProvider.getIdentityId()」がcallされる

refreshメソッドの中の「CognitoClient.sharedInstance.fetchId()」は内部で.credentialProvider.getIdentityIdをcallしています。


(3)MyIdProbiderの「logins」がcallされる

.credentialProvider.getIdentityIdがcallされると、内部でAWSIdentityProviderManagerの.loginsがcallされます。

ここではじめて、どのアカウントでもサインインしていない事が発覚します。(サインインしている場合はIDが返されます)


(4)ログイン画面が表示される

どのアカウントでもサインインしていない場合、loginsの下の方にある「.userpool.currentUser()?.getDetails()」がcallされます。

これにより、サインインしていない場合は「AWSCognitoIdentityInteractiveAuthenticationDelegateのstartPasswordAuthentication()」がcallされます。

AWSCognitoIdentityInteractiveAuthenticationDelegateは今回AppDelegate内で準拠しています。

デリゲートの設定はCognitoClient.swiftから行なっていますので参照してみてください。


(5)ログインに成功するとdismissし、LoggedInViewControllerにIDが表示される

ログインに成功すると、AWSCognitoIdentityPasswordAuthenticationのdidCompleteStepWithErrorがcallされます。

この中でdismissとrefreshをしています。

ざっくりとした流れはこんな感じです。

LoggedInViewControllerのfetchボタンをタップすると、データが存在する場合は値を取得できるかと思います。

DynamoDBの行レベルアクセス制限を実現するためのIAMの設定

ドキュメント: Amazon DynamoDB: Amazon CognitoID に基づいた DynamoDB への行レベルのアクセスを許可する - AWS Identity and Access Management

IAM Management Consoleへ行き、新規でポリシーを作ります。名前は何でも構いません。

JSONタブを選択し、ドキュメントに書かれているように以下のJSONを記載し、作成します。
<TABLE-NAME>は書き換える)

ACCOUNTNUMBERはアカウント設定に書かれているアカウントIDの事です。

{
     "Version": "2012-10-17",
     "Statement": [
         {
             "Effect": "Allow",
             "Action": [
                 "dynamodb:DeleteItem",
                 "dynamodb:GetItem",
                 "dynamodb:PutItem",
                 "dynamodb:Query",
                 "dynamodb:UpdateItem"
             ],
             "Resource": [
                 "arn:aws:dynamodb:<REGION>:<ACCOUNTNUMBER>:table/<TABLE-NAME>"
             ],
             "Condition": {
                 "ForAllValues:StringEquals": {
                     "dynamodb:LeadingKeys": [
                         "${cognito-identity.amazonaws.com:sub}"
                     ]
                 }
             }
         }
     ]
 }

このポリシーをアタッチしていきます。

CognitoIDプールを作成したときに、「Cognito_****Auth_Role」といったロールが作成されていると思います。(デフォルト)

そこに先ほど作ったポリシーをアタッチします。


次に、DynamoDBにテーブルを作成します。この時テーブルの名前を先ほどポリシーを作る時に<TABLE-NAME>で指定した名前にすることを忘れないでください。

そして、パーティションキーにCognitoIDプールから得られるユーザのIDを入れます。これでユーザは自分のIDの行しか読み込むことができなくなります。

(サンプルアプリをそのまま動かすには、パーティションキーの名前を「userid」にして、その他のカラムは全てstring型にしておいてください)