HAPPY HACKING Oouchi's BLOG

PSE(ポンコツエンジニア)の技術ブログ

CognitoユーザープールをCFnで構築し、TypeScriptでユーザーを追加する

f:id:ooooouchi:20200525235755p:plain

はじめに

AWS Cognitoは簡単に認証基盤を作成することができます。
環境構築からサインアップまでの流れは結構な頻度で実装することになるため個人的な覚書として投稿します。

aws.amazon.com

本題

環境構築

環境構築はCloudFormationを使用します。

ユーザープールとついでにアイデンティティプールも作成しておきます。

AWSTemplateFormatVersion: 2010-09-09
Description: Cognito Identity Pool and User Pool.
Parameters:
  Env:
    Type: String
    AllowedValues:
      - prod
      - stg
      - dev
  SystemName:
    Type: String
    Default: Sample
Resources:
  # ユーザープール
  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: !Join ["-", [!Ref Env, !Ref SystemName, UserPool]]
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: true
        UnusedAccountValidityDays: 7
      EmailConfiguration:
        EmailSendingAccount: COGNITO_DEFAULT
      Policies:
        PasswordPolicy:
          MinimumLength: 8
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: false
          RequireUppercase: true
      Schema:
        - Name: sub
          AttributeDataType: String
          DeveloperOnlyAttribute: false
          Mutable: true
          Required: false
          StringAttributeConstraints:
            MinLength: 1
            MaxLength: 256
      AutoVerifiedAttributes:
        - email
      UsernameAttributes:
        - email
      MfaConfiguration: "OFF"
      AccountRecoverySetting:
        RecoveryMechanisms:
          - Priority: 1
            Name: verified_email
      UsernameConfiguration:
        CaseSensitive: false
  UserPoolClient:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      ClientName:
        "Fn::Join":
          - ""
          - - Ref: "AWS::StackName"
            - Users-client
      GenerateSecret: false
      RefreshTokenValidity: 30
      PreventUserExistenceErrors: ENABLED
      UserPoolId:
        Ref: UserPool
  # アイデンティティプール
  IdentityPool:
    Type: AWS::Cognito::IdentityPool
    Properties:
      AllowUnauthenticatedIdentities: false
      IdentityPoolName: !Join ["-", [!Ref Env, !Ref SystemName, IdPool]]
      CognitoIdentityProviders:
        - ClientId:
            Ref: UserPoolClient
          ProviderName:
            Fn::Join:
              - ""
              - - cognito-idp.
                - Ref: "AWS::Region"
                - .amazonaws.com/
                - Ref: UserPool
  UnauthenticatedPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - mobileanalytics:PutEvents
              - cognito-sync:*
            Resource:
              - "*"
  UnauthenticatedRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "sts:AssumeRoleWithWebIdentity"
            Principal:
              Federated: cognito-identity.amazonaws.com
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud":
                  Ref: IdentityPool
              ForAnyValue:StringLike:
                "cognito-identity.amazonaws.com:amr": unauthenticated
      ManagedPolicyArns:
        - Ref: UnauthenticatedPolicy
  AuthenticatedPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - mobileanalytics:PutEvents
              - cognito-sync:*
              - cognito-identity:*
            Resource:
              - "*"
  AuthenticatedRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "sts:AssumeRoleWithWebIdentity"
            Principal:
              Federated: cognito-identity.amazonaws.com
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud":
                  Ref: IdentityPool
              ForAnyValue:StringLike:
                "cognito-identity.amazonaws.com:amr": authenticated
      ManagedPolicyArns:
        - Ref: AuthenticatedPolicy
  RoleAttachment:
    Type: AWS::Cognito::IdentityPoolRoleAttachment
    Properties:
      IdentityPoolId:
        Ref: IdentityPool
      Roles:
        unauthenticated:
          Fn::GetAtt:
            - UnauthenticatedRole
            - Arn
        authenticated:
          Fn::GetAtt:
            - AuthenticatedRole
            - Arn

作成が完了しました。

まだユーザーは一人もいないことが確認できます。

f:id:ooooouchi:20200525234629p:plain

ユーザー作成処理実装

Lambda index.ts

import { CognitoIdentityServiceProvider } from "aws-sdk";
export const handler = (event: any) => {
  const cognitoidentityserviceprovider = new CognitoIdentityServiceProvider();
  const params: CognitoIdentityServiceProvider.AdminCreateUserRequest = {
    UserPoolId: "UserPoolId", // 作成したユーザープールID
    Username: "hoge@example.co.jp", // 作成するユーザー名(メールアドレス)
    DesiredDeliveryMediums: [
      "EMAIL", 
    ],

    TemporaryPassword: "P@ssw0rd1", // 初期パスワード
    UserAttributes: [
      {
        Name: "email",
        Value: "hoge@example.co.jp", // 作成するユーザー名(メールアドレス)
      },
      {
        Name: "email_verified", // メールアドレスを検証済にする
        Value: "true",
      },
      /* more items */
    ],
  };
  cognitoidentityserviceprovider.adminCreateUser(params, function (err, data) {
    if (err) console.log(err, err.stack);
    // an error occurred
    else console.log(data); // successful response
  });
};

参考

Class: AWS.CognitoIdentityServiceProvider

実行結果

実行はSAM Localを使用しています。

START RequestId: ae977e8c-e6db-1662-439e-001dcc8d9fdd Version: $LATEST
2020-05-23T11:47:51.507Z        ae977e8c-e6db-1662-439e-001dcc8d9fdd    INFO    {
  User: {
    Username: '57ab5b2c-fd08-4073-8776-924ab3680d8c',
    Attributes: [ [Object], [Object], [Object] ],
    UserCreateDate: 2020-05-23T11:47:51.272Z,
    UserLastModifiedDate: 2020-05-23T11:47:51.272Z,
    Enabled: true,
    UserStatus: 'FORCE_CHANGE_PASSWORD'
  }
}
END RequestId: ae977e8c-e6db-1662-439e-001dcc8d9fdd

ユーザーが作成されていることが確認できました。

f:id:ooooouchi:20200525234657p:plain

少し待つとYour temporary passwordという件名で、ユーザー名と初期パスワードが書かれたメールが送信されます。

おわりに

簡易検証用のCFnやコードを持っているといろいろなところで役に立ちますね。
この記事もどこかで役に立てば光栄です。