Amazon CognitoのUser Groupを利用した権限管理について本気出して考えてみた。


Amazon CognitoのUser Groupを利用した権限管理について本気出して考えてみた。

パーソルプロセス&テクノロジー株式会社のAdvent Calendar 19日目の記事です。

Azureの記事が多い中ですが、堂々とAWSの記事を書いていこうと思います。
今回は何番煎じかわかりませんが、Amazon Cognitoを利用して役職や階級の概念を持つサービス内の権限管理を行います。
順を追って構築していくので、記事を読み終わった際に 権限管理が可能な環境が構築できる状態 になっていただければ幸いです。
あくまで『権限管理を行う』が目的です。個々の解説は適当なので、公式のドキュメントを合わせて読んでみてください。
不明点あれば質問いただければ返答します(正しい答えが返ってくるとは言っていない。)

最初に

Cognitoは、認証認可、またユーザーの管理なども兼ね備えたフルマネージドなサービスです。
GoogleやFaceBookを利用したソーシャルログインも実現することもできます。

今回使う機能

その他AWSサービス(今回IAMの確認にAPIを利用します。)

今回使用するIAM(かさばるので最後に明記します。)

Unauth_Role(Cognito_testUnauth_Role)
 認証されていないユーザーとしてIdPool作成時に作成できます。
Auth_Role(Cognito_testauth_Role)
 認証されたユーザーとしてIdPool作成時に作成できます。
God_Role(CognitoGroup_God_Role)
 APIを叩く権限を与えます。

設定のイメージ

どこにも認証を受けていないユーザー → Unauth_Roleを付与
Cognitoによる認証をうけたが、Groupなどには所属していないユーザー → Auth_Roleを付与
Cognitoによる認証をうけ、God_Groupに所属しているユーザー → God_Roleを付与

設定手順

順を追って設定していきます。
言及のない設定は全てデフォルトで作成します。

1. API作成

Authの確認を行いたいのでMockですがIAMによる認可をかけます。

デプロイ後、発行されるURLをブラウザで叩いてみて、権限が足りないよというエラーが表示されるとAPIの設定は完了です。

2. UserPool作成

今回IdPoolとの紐付けにアプリクライアントが必要なのでクライアントIDを発行します。

3. IdPool作成

急にスペースを許容するFormに適当に名前を入力し、 認証されていないID にチェックを入れます。IdPoolの設定は後から編集が効くものばかりなので必要な項目だけ設定します。
続いて認証プロバイダーの項目を開き、 ユーザープールID・アプリクライアントID に先ほど作成したUserPoolの情報を入力しプールの作成を完了させます。

Identify the IAM roles to use with your new identity pool
ここでは作成したIdPoolで付与するRoleを作成するページです。
詳細を開くとこんな感じになっています。すでに作成してあるRoleを選択することも可能ですが、今回は自動生成されたものをそのまま利用するので、許可を選択しましょう。

これでIdPoolとPoolで認証を受けたユーザーに対するRoleの作成が完了しました。
ダッシュボードを開くと認証されたユーザーの数や認証に利用しているプロバイダーの情報が表示されています。

次に右上、IdPoolの編集を選択します。
認証プロバイダーの項目を開くと、 認証されたロールの選択 という項目が増えています。
ここでは認証を受けたユーザー(UserPoolでサインアップを行ったユーザー)に付与するロールを選択することができます。
今回は トークンからRoleを選択する。 を選択し、変更の保存を行います。

これでIdPoolの設定は完了です。

4. 最後にGod_Roleと付与するGroupを作成します

エンティティの種類はウェブIDを選択します。

手順に沿って作成することでIAMRoleにIDPoolとの信頼関係を持たせることができます。

作成後、ロールの詳細が以下のようになっていれば完了です。

次にUserpoolにGroupを作成しましょう。
IAMロールは上で作成したGod_Roleを設定します。

これで一通りの設定は完了しました。

動作確認

ここから動作確認をしていきます。
APIGatewayのIAM認証付きのAPIをJavascriptから叩く
Cognitoユーザープールを使ってみました
このあたりのデモアプリや手順を拝借します。

1.ログインし、Credentialsを確認する


うまく情報を取得することが出来ていますね。

2.APIを叩いてみる

失敗しました。
ログを確認するとこのユーザーが持っているRoleはただのAuth_Roleだからということがわかります。

3.ユーザーをグループに追加し、God_Roleを付与する

$ aws cognito-idp admin-add-user-to-group \
--user-pool-id ${userPoolId} \
--username testuser \
--group-name God

ユーザーをグループに追加します。
ここまでコンソールでやっていたのに、捻りを出すために急にCLIで追加してみます。

4.再度APIを叩いてみる

叩けましたね。
ここでIdTokenを確認してみましょう。

公式のDocumentから読み解くと

cognito:preferred_role クレームはロール ARN です。
cognito:roles クレームは、許可されたロール ARN のセットを含むカンマ区切りの文字列です。

つまり、 cognito:preferred_role の値が現在セットされているRoleというわけになります。
今回はtestuserに対してGod_Roleが付与されているかつ、IAM認可をかけたAPIをキックすることが出来たので、正しい状態であることがわかります。
詳しくは本記事の参考資料のロールベースアクセスコントロールの『トークンを使用したユーザーへのロールの割り当て』を読んでください。

Token分解

Cognitoから受け取る各種TokenはBase64でデコードすることが可能です。
もし上手く権限が付与できていない、権限は付与できているけどなぜか拒否される場合は実際にデコードしてユーザーが持つ情報を確認すると、問題の解決に繋がります。

echo {$IdToken} | cut -d'.' -f 2 | base64 -d

最後に

少し長くなってしまいましたが、基本の設定・動作確認は以上になります。
実運用で使う際には恐らく複数グループを作成することにもなるし、一人のユーザーを複数グループに所属させるケースもあると思います。
この記事の内容でも、当然のように私も丸2,3日程度は頭を抱えつつドキュメントとにらめっこをしていました。
認証周りは難しく感じますが、コード上で管理するよりもセキュアにかつ変更も容易であることから、Cognitoで権限回りを一括管理させることが出来ると、アプリケーションの中身もスッキリするんじゃないかなと思います。

実際に理解が進んでくると、意外とシンプルだなと思ったりするのでめげずにドキュメントを見渡してみてください。

以下参考資料になります。

今回使用したIAMPolicy

Cognito_testUnauth_Role(認証されていないユーザーとしてIdPool作成時に作成できます。)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Cognito_testauth_Role(認証されたユーザーとしてIdPool作成時に作成できます。)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "cognito-identity:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

CognitoGroup_God_Role(APIを叩く権限を与えます)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "execute-api:Invoke"
            ],
            "Resource": [
                "arn:aws:execute-api:ap-northeast-1:{$AWSccountID}:{$APIgatewayID}:/*"
            ]
        }
    ]
}

参考資料

Cognitoのサインイン時に取得できる、IDトークン・アクセストークン・更新トークンを理解する
APIGatewayのIAM認証付きのAPIをJavascriptから叩く
Cognitoユーザープールを使ってみました
ロールベースアクセスコントロール
Amazon Cognito グループ、およびきめ細かなロールベースのアクセス制御