AWSのCognitoの認証・認可関係でつまりに詰まったので、まとめます。
・やりたいこと:
サイトにログイン機能を実装し、ログインしたユーザーがAWS Pollyを呼び出してテキストを読み上げてほしい。
・前提:
amplify authでいい感じに実装してほしかったが、うまくいかなかったので、Cognitoの概念自体を学びなおしました。
1, Cognitoとは?
ログイン機能を簡単に作ってくれます。もっと詳しく書くと、認証をつかさどるユーザープールと、認可をつかさどるidプールに分かれます。つまり、ユーザープールでユーザー名やパスワード、Eメールなどで本人確認を行い、そこで認証したユーザーに、idプールが適切なAWSサービスへのアクセス権限を与えてくれます。
2, ユーザープールの作成
新規のユーザープールを作る際に、ユーザープールとWebアプリの紐づけを行います。amplifyで設定を更新後、アプリのログイン画面からユーザーが作成できることを確認します。
ユーザーの中でも管理者と一般ユーザーというように、group分けして、idプールでgroupごとに与える権限を変えることも可能です。今回は無視。
3, idプールの作成
a, ユーザープールとidプールの紐づけ
idプールの「ID プロバイダー」項目でユーザープールが紐づいていることを確認します。
b, 認証されたアクセスとゲストアクセスの権限設定
ログイン後のユーザーには認証されたアクセス権限を、ログイン前のユーザーにはゲストアクセスの権限を与えます。必ず2種類設定しないと、amplifyで使えなかったのに気づかず、ずっとはまっていました。
それぞれIAMロールを作成するのですが、IAMロールにも「許可ポリシー」と「信頼ポリシー」という2つの種類があり、適切な設定が必要です。
・「許可ポリシー」とは?
その名の通り、AWSサービスへの許可リストのことです。今回はPollyを呼び出せる権限をつけたいので、ゲストユーザーにはCognito identity、ログイン後のユーザーには雑に「AmazonCognitoUnAuthedIdentitiesSessionPolicy」をつけます。(あくまでテスト段階なので、後ほど適切な権限に切り替えます)
・「信頼ポリシー」とは?
雑に言うと、「誰に対して」先ほどの許可ポリシーを付与するのか?という宣言です。今回はCognitoのidプール自体につけるので、以下のようなjsonにします。
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
# 「誰に」許可を与えるか宣言
},
"Action": [
"sts:AssumeRoleWithWebIdentity",
"sts:TagSession"
],
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": {自分のidプールのid}
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
# ログイン後のユーザーのIAMの場合、"authenticated",
ゲストユーザーのIAMの場合、"unauthenticated"
}
}
}]
}
c, ユーザーグループのgroupごとに適切なIAMロールを付与。
今回は対象外のため、割愛
4, 確認
Webアプリでログイン後、Pollyを呼び出せることを確認します。
なんとかできました。AWSサービスは独自の造語が多く、翻訳が大変でした。
5, おまけ
Cognitoの認証・認可機能を使って、ローカルからlambdaなどAWSサービスを呼びます。
const idToken = (await fetchAuthSession()).tokens?.idToken ?? "";
credentials作成に必要なidentity idを呼び出します。cognitoにはidがいろいろあってややこしいのではまりましたが、必要なのはidプールのidentity idでした。amplify公式ドキュメントにある上記のコードで取得可能です。公式↓
https://docs.amplify.aws/react/build-a-backend/auth/manage-user-session/
const userPoolUrl
= 'cognito-idp.'
+ {cognitoのリージョン}
+ '.amazonaws.com/'
+ {ユーザープールのid}
ユーザープールのurlを作ります。
const credentials = fromCognitoIdentityPool({
identityPoolId: {idプールのidentity id},
logins: { // 認証情報。ユーザープールのほか、OAUTH認証もこちらで使用
[userPoolUrl]: idToken.toString(),
},
clientConfig: { region: {クライエントのawsリージョン} },
});
上記で作ったidTokenとuserPoolUrlを使って、credentialsを作ります。
// ローカルで各サービスのクライエントを用意
const lambdaClient = new LambdaClient({
region: {region}, // リージョン
credentials // 上記で作ったcredentials。
})
Clientができました。AWSサービスを呼び出す準備が整いました。
ちなみにcredentialsを省略してClientを作った場合、idプールのゲストユーザーの権限で各サービスを呼び出してます。