はじめに

はじめまして。デザイン&エンジニアリング部、入社4年目の浅岡と申します。

皆さん、AWS(Amazon Web Service)というサービスをご存知でしょうか。私は今まで4つのシステム開発プロジェクトに携わってきましたが、そのどのシステムもこのAWSと何らかの形で関わっていました。

世界シェア30%を超える最もメジャーなクラウドコンピューティングサービスと言ってもいいAWSですが、私は未だに苦手意識が抜けないでいます。

今回は、そんな私がAWSのAmazon Cognitoと連携したユーザー登録機能とユーザー削除機能をバックエンドサーバーに作った際の、調べた内容やハマった点を共有したいと思います。

 

前提

今回は、以下のような環境で実装しています。

  • Java(OpenJDK Temurin-11.0.15+10)
  • Spring Boot(2.6.3)
  • AWS SDK for Java(cognitoidentityprovider version 2.17.144)

なお、Amazon Cognitoのユーザープール作成等は終わっているものとします。

 

Amazon Cognitoとは

Amazon CognitoとはAWSのサービスの中の1つですが、公式ドキュメントには以下のように書いてあります。

Amazon Cognitoは、ウェブおよびモバイルアプリの認証、承認、およびユーザー管理機能を提供します。

 

順番に解説していきます。

ウェブアプリやモバイルアプリにおける認証というのは、 ユーザーIDやパスワード等でユーザーを個別に識別する仕組みのことを指します。ユーザーIDとパスワードを入力してログインするという操作ができるのは、この認証という仕組みがあるからです。

次に承認ですが、こちら日本語訳としては「承認」となっていますが原文ではauthorizationとなっており、ソフトウェア開発の文脈では認可と解釈されることが多い単語です。認可というのは、識別されたユーザーにどのような操作を許可するのかを決めることを指します。例えば、「管理者は全ての情報を見ることができるが、通常ユーザーは一部の情報しか見ることができない」といった制御のことです。

最後にユーザー管理機能ですが、こちらはユーザーを新たに追加したり、既に登録されているユーザーの情報を編集したり削除したりという管理機能を指します。他にも、ユーザー登録時に初回パスワードが記載されているメールを自動的に送ってくれたり、パスワードを忘れた際のパスワード再設定メールを自動的に送ってくれたりする機能もあります。

 

まとめると、Amazon Cognitoは「ウェブアプリやモバイルアプリに対して、利用するユーザーを識別する仕組みや、ユーザーができることの制限、ユーザーの管理をする機能」を提供してくれるものという事になります。

つまり、ウェブアプリやモバイルアプリを開発しようとした際にAmazon Cognitoを利用することで「認証、承認、およびユーザー管理機能」に関わる細かい実装せずとも使えるようになるということでもあります。

しかし、Amazon Cognitoへユーザー登録を行うにはAWSマネジメントコンソール上での登録操作が必要でした。この操作を無くすために今回はアプリケーションからAmazon Cognitoへアクセスできるように実装しました。ただ、アプリケーションからAmazon Cognitoへユーザー登録をする際に少し気を付けなければいけない点があります(Eメール確認済み属性をtrueにしなければいけない)。詳しくは後述します。

 

AWS SDK for Java

AWSをプログラムから利用するために、AWS SDK for Javaを使いました。

SDKとはSoftware Development Kit(ソフトウェア開発キット)の略で、プログラムに必要な様々なAPIやツールがパッケージ化されたものを指します。

AWSは色々なプログラミング言語でのSDKを提供してくれていますが、今回はJava用のものを選択しました。

 

実装

ユーザー登録機能の実装

それでは、Amazon Cognitoと連携したユーザー登録機能の実装に入っていきます。今回私は以下のようなコードを実装しました。

なお、本クラス(CognitoUtil)は一般的にサービス層と呼ばれる場所から呼び出されることを想定しています。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import software.amazon.awssdk.services.cognitoidentityprovider.CognitoIdentityProviderClient;
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminCreateUserRequest;
import software.amazon.awssdk.services.cognitoidentityprovider.model.AttributeType;

@Component
public class CognitoUtil {

    private CognitoIdentityProviderClient cognitoClient;

    @Autowired
    public CognitoUtil(
        CognitoIdentityProviderClient cognitoClient
    ) {
        this.cognitoClient = cognitoClient;
    }
    
    public void createUser(String email) {
        AdminCreateUserRequest createUserRequest = AdminCreateUserRequest.builder()
            .userPoolId("[Amazon CognitoのユーザープールID]")
            .username(email)
            .userAttributes(
                AttributeType.builder()
                    .name("email")
                    .value(email)
                    .build(),
                AttributeType.builder()
                    .name("email_verified")
                    .value("true")
                    .build()
            )
            .build();
        cognitoClient.adminCreateUser(createUserRequest);
    }
}

 

上から順に解説していきます。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import software.amazon.awssdk.services.cognitoidentityprovider.CognitoIdentityProviderClient; // ...... (1)
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminCreateUserRequest; // ...... (2)
import software.amazon.awssdk.services.cognitoidentityprovider.model.AttributeType; // ...... (3)

上2行はSpringでDIするためのアノテーションのimportなので解説は割愛します。

(1) CognitoIdentityProviderClient
こちらはAmazon Cognitoにアクセスするためのサービスクライアントです。このクラスのメソッドを呼び出すことでAmazon Cognitoでの各種操作を行うことができます。

(2) AdminCreateUserRequest
Amazon Cognitoにユーザー登録するためにCognitoIdentityProviderClientadminCreateUser()というメソッドを使用するのですが、このメソッドの引数としてAdminCreateUserRequest型の変数を渡します。

(3) AttributeType
作成するユーザーの属性をこのクラスを使って指定することができます。

 

private CognitoIdentityProviderClient cognitoClient;

@Autowired
public CognitoUtil(
    CognitoIdentityProviderClient cognitoClient) {
    this.cognitoClient = cognitoClient;
}

ここでは、DIコンテナに登録されているCognitoIdentityProviderClientクラスのオブジェクトをコンストラクタインジェクションで取得しています。

前述したとおり、CognitoIdentityProviderClientクラスの各種メソッドを呼び出すことでAmazon Cognitoの操作が実現できるのですが、このクラスのオブジェクトを作成する際はbuilder()メソッドを使用して作成する必要があります。

今回は、別のクラスにオブジェクト作成とDIコンテナ登録の実装をする形にしました。作成する部分の実装に関しては後述します。

 

public void createUser(String email) {
    AdminCreateUserRequest createUserRequest = AdminCreateUserRequest.builder() // ...... (1)
        .userPoolId("[Amazon CognitoのユーザープールID]")
        .username(email) // ...... (2)
        .userAttributes( // ...... (3)
            AttributeType.builder()
                .name("email")
                .value(email)
                .build(),
            AttributeType.builder()
                .name("email_verified")
                .value("true")
                .build()
        )
        .build();
    cognitoClient.adminCreateUser(createUserRequest); // ...... (4)
}

このメソッドでユーザー登録を実行しています。

(1) AdminCreateUserRequest型変数の作成
AdminCreateUserRequest型変数はstaticメソッドであるbuilder()メソッドを使用し、Amazon CognitoのユーザープールIDやユーザー名、ユーザーの属性を設定した後build()メソッドで作成します。

(2) .username()
.username()でユーザー名を設定できます。今回はユーザー名としてメールアドレスを使用しようと考えていたためメールアドレスを設定していますが、APIリファレンスにあるようにユーザープール内でユニークかつ128文字までの文字列であれば他の値でも構いません。

(3) .userAttributes()
.userAttributes()で、登録するユーザーの各種属性を設定します。設定にはAttributeTypeを使用します。

注意してほしいのはemail_verifiedの項目です。こちらはAmazon Cognitoの「Eメール確認済み」という項目で、何も指定しないで登録するとfalseになってしまいます。falseの状態ですとEメールの検証が行われていないと判断され、パスワードを忘れた際の再設定メール等、Amazon Cognitoが自動で送ってくれるメールが送られなくなってしまいます。これを知らなかったので再設定メールが送られて来なく、結構ハマりました。

(4) ユーザー登録の実行
(1)~(3)で作成されたAdminCreateUserRequest型の変数をadminCreateUser()メソッドに渡してあげることでAmazon Cognitoにユーザー登録することができます。

尚、パスワードはAmazon Cognito側で自動で発行されますので設定不要です。自動で発行されたパスワードは登録されたメールアドレスに自動で送られますので、ユーザーはそのパスワードを使って初回ログインを行うことができます。

 

ユーザー削除機能の実装

次に、ユーザー削除機能の実装に入ります。

追加でimportするクラスは以下の1つだけです。

import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminDeleteUserRequest;

Amazon Cognitoに登録されているユーザーの削除にはCognitoIdentityProviderClientadminDeleteUser()というメソッドを使用します。このメソッドの引数としてAdminDeleteUserRequest型の変数を渡します。

ユーザー登録を実施するcreateUser()メソッドの下に削除を実行する以下のメソッドを追加しました。

public void deleteUser(String email) {
    AdminDeleteUserRequest deleteUserRequest = AdminDeleteUserRequest.builder()
        .userPoolId("[Amazon CognitoのユーザープールID]")
        .username(email)
        .build();

    cognitoClient.adminDeleteUser(deleteUserRequest);
}

AdminDeleteUserRequest型変数はstaticメソッドであるbuilder()メソッドを使用し、Amazon CognitoのユーザープールIDと削除対象のユーザー名を指定した後build()メソッドで作成します。

ここで指定するユーザー名は、ユーザー登録の際に.username()で指定したものです。今回はメールアドレスをユーザー名として使用しているため、ここでもメールアドレスを指定しています。

 

CognitoIdentityProviderClientの作成とDIコンテナへの登録

今回は、以下のようにCognitoIdentityProviderClientクラスのオブジェクトを作成しDIコンテナへ登録しました。

@Configuration
public class AwsCognitoIdentityProvider {
    @Bean
    public CognitoIdentityProviderClient cognitoClient() {
        return CognitoIdentityProviderClient.builder()
            .defaultsMode(DefaultsMode.STANDARD) // ...... (1)
            .credentialsProvider(DefaultCredentialsProvider.create()) // ...... (2)
            .region(Region.of("[リージョンを設定します (例:ap-northeast-1)]"))
            .build();
    }
}

(1) .defaultsMode()
.defaultsMode()で設定値のデフォルトを設定できます。AWS SDKを使う際にはリトライポリシーや接続タイムアウト等、様々な設定値を設定する事ができますが、一つずつ全て設定していくのがとても大変なので、AWSは推奨値を設定してくれるプリセットを用意してくれています(詳細はこちら)。それがDefaultsModeというenumです。

DefaultsModeSTANDARDは、ほとんどの場合で安全に動作するような最新の推奨デフォルト値が提供されます。

(2) .credentialsProvider()
.credentialsProvider()でAmazon Cognitoにアクセスするために必要な認証情報をどのように検索するか設定できます。DefaultCredentialsProviderは、以下の順序で認証情報を検索してくれます。

  1. Java システムプロパティ -aws.accessKeyIdおよび、aws.secretAccessKey
  2. 環境変数 -AWS_ACCESS_KEY_IDおよびAWS_SECRET_ACCESS_KEY
  3. システムプロパティまたは環境変数からのWebIDトークン資格情報
  4. すべてのAWS SDKとAWS CLIによって共有されるデフォルトの場所(`~/.aws/credentials`)にある認証情報プロファイルファイル
  5. AWS_CONTAINER_CREDENTIALS_RELATIVE_URI環境変数が設定されていて、セキュリティマネージャーがその変数にアクセスする権限を持っている場合、Amazon EC2コンテナサービスを介して配信される認証情報
  6. Amazon EC2メタデータサービスを介して提供されるインスタンスプロファイル認証情報

今回は、OSの環境変数にAWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYを設定しました。

 

まとめ

Amazon Cognitoと連携してユーザー登録と削除を行う機能に絞って紹介してきました。これは所謂バックエンドサーバーでの実装になると思います。

ユーザー登録を行うためのフロー全体の実装のためには、このほかにもAWSマネジメントコンソール上での操作や、フロントエンドの実装等を行う必要があります。本記事は、ユーザー登録機能のバックエンドサーバーでの実装をする際や、どこをフロントエンドでやってどこをバックエンドでやるか悩んだ際の参考にしていただけると幸いです。