Level 400 / AWS SDK v3 + Cognito Identity Pool + IAM Role + Permission
概要
この記事は、AWS SDK を v2 から v3 へ移行する際の方法を記載します。
要件上の問題から、 AWS Amplify は利用していません。
クライアントサイドで Cognito UserPool で認証し、 Cognito Identity Pool で認可して AWS Credential を発行し、AWS SDK を直接操作しています。
そのため、API Gateway 側で認可するなどの サーバーサイドで実行する際は別の方法になります。
また、 IdToken の取得方法は v2/v3 で差がないので記載しません。
- see also
概要図
TL;DR
DynamoDB の Client を生成しているのは、今回必要だったため。
任意のクライアントで同様の方法です。
Identity ID が不要な場合。
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity"
const region = 'ap-northeast-1'
const dynamodb = new DynamoDBClient({
region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region }),
identityPoolId: '{identity pool id}',
logins: {
[userPoolId]: user.getIdToken().getJwtToken()
}
})
})
Identity ID が必要な場合
import { CognitoIdentityClient, GetIdCommand } from "@aws-sdk/client-cognito-identity"
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { fromCognitoIdentity } from "@aws-sdk/credential-provider-cognito-identity"
const region = 'ap-northeast-1'
const cognito = new CognitoIdentityClient({region})
const identity = await cognito.send(new GetIdCommand({
IdentityPoolId: '{identity pool id}',
Logins: {
[userPoolId]: user.getIdToken().getJwtToken()
}
}))
if (!identity.IdentityId) {
throw new Error('Auth error')
}
const dynamodb = new DynamoDBClient({
region,
credentials: fromCognitoIdentity({
client: new CognitoIdentityClient({ region }),
identityId: identity.IdentityId,
logins: {
[userPoolId]: user.getIdToken().getJwtToken()
}
})
})
AWS SDK v3 の Credentials
Credentials の取り扱いについて
AWS-SDK v2 / v3 の大きな変更点に、Credentials の取り扱いの変更があると考えます。
v2 の場合、AWS SDK の config を変更することにより、 region や credentials を変更していました。
サンプル:
import * as AWS from 'aws-sdk'
AWS.config.region = 'ap-northeast-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials(...);
v3 の場合は、 Client をインスタンス化する際に、credentials に Provider を渡すことにより認証します。
これは、今回私が行っているような複数の認証情報を利用する場合に、そのスコープを局所化するのにとても有意です。
サンプル
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { fromCognitoIdentity } from "@aws-sdk/credential-provider-cognito-identity"
const dynamodb = new DynamoDBClient({
region,
credentials: fromCognitoIdentity(...)
})
Provider は用途に合わせて複数のものが用意されています。
これらの Provider を credentials に渡すことにより、認証が行なえます。
今までのように、Credentials の値を、開発者がやり取りする必要もなくなり、そこもメリットと考えています。
-
Providers
- fromCognitoIdentity()
- fromCognitoIdentityPool()
- fromTemporaryCredentials()
- fromWebToken()
- fromContainerMetadata()
- fromInstanceMetadata()
- fromIni()
- fromEnv()
- fromProcess()
- fromTokenFile()
- fromSSO()
- fromNodeProviderChain()
-
see also
Cognito Identity Pool の取り扱い。
Cognito Identity Pool で単純に Credentials を利用する場合、 fromCognitoIdentityPool
を利用するだけです。
こちらは、Web 上で多くのサンプルが見つかるため、特にここでは詳細は記載しません。
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity"
const region = 'ap-northeast-1'
const dynamodb = new DynamoDBClient({
region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region }),
identityPoolId: '{identity pool id}',
logins: {
[userPoolId]: user.getIdToken().getJwtToken()
}
})
})
ここで問題になるのは、 プログラムがユーザーの IdentityID を必要とする場合です。
たとえば、S3 や DynamoDB にユーザー自身の Cognito Identity Pool IdentityID の領域に書き込む場合は、自身の IdentityID を必要とします。
具体的には、IAM Role に ${cognito-identity.amazonaws.com:sub}
を利用する場合で、DynamoDB では以下のようなロールが該当します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
],
"Resource": [
"arn:aws:dynamodb:*:123456789012:table/MyTable"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
}
}
}
]
}
この場合、fromCognitoIdentityPool ではユーザー自身の IdentityID を取得できないため、先に自身の IdentityID を GetID で取得し、その後 fromCognitoIdentity で Credentials を取得することが必要です。
- see also
import { CognitoIdentityClient, GetIdCommand } from "@aws-sdk/client-cognito-identity"
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { fromCognitoIdentity } from "@aws-sdk/credential-provider-cognito-identity"
const region = 'ap-northeast-1'
const cognito = new CognitoIdentityClient({region})
const identity = await cognito.send(new GetIdCommand({
IdentityPoolId: '{identity pool id}',
Logins: {
[userPoolId]: user.getIdToken().getJwtToken()
}
}))
if (!identity.IdentityId) {
throw new Error('Auth error')
}
const dynamodb = new DynamoDBClient({
region,
credentials: fromCognitoIdentity({
client: new CognitoIdentityClient({ region }),
identityId: identity.IdentityId,
logins: {
[userPoolId]: user.getIdToken().getJwtToken()
}
})
})
この場合、 identity.IdentityId
としてユーザーの IdentityId が取得できるため、あとは通常通りアイテムの操作が可能となります。
雑記
この情報ですが、日本語はさておき、英語にもほとんど情報が出ていません。
今回の要件として Amplify が利用できず、また、AWS SDK v3 を利用したため、公式のサンプルもアップデートがされておらず参考になりませんでした。
複数の情報を総合し、開示されている API などから導いたのが上記の利用方法となります。
まだまだ、 AWS SDK v3 はその利用法について情報が不足している段階とは思いますが、インスタンスとして局所化できたり、コードサイズが小さくなるなどのメリットが多いため、 人柱 積極的に利用していきたいです。
以上