LoginSignup
4
0

More than 1 year has passed since last update.

Cognito Identity Pool を用いて、AWS SDK v3 の認証を行う。

Last updated at Posted at 2023-01-30

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 で差がないので記載しません。

概要図

image.png

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 を取得することが必要です。

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 はその利用法について情報が不足している段階とは思いますが、インスタンスとして局所化できたり、コードサイズが小さくなるなどのメリットが多いため、 人柱 積極的に利用していきたいです。

以上

4
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0