Amplify Gen2はユーザ名でログイン可能です

2024.06.27

NTT東日本の中村です。

Amplify Gen1では、認証のログインにユーザ名を指定することができます、 Amplify Gen2では、ユーザ名を指定できないように見えますが、CDKを使えば実現できるようです。

概要

AmplifyのGen1,Gen2のどちらも、ユーザ認証のシステムに、内部でAmazon Cognitoを使用しています。

Amazon Cognitoは、認証のオプションにユーザ名、Eメール、電話番号を選択することができます。 マネジメントコンソールからAmazon Cognitoを作成すると、選択肢からユーザ名を選ぶことができます。

Amplify Gen1でも、amplify add Authを実行した時、選択肢にユーザ名(Username)を選ぶことができます。

Amplify Gen2は、auth/resource.tsの中で認証方法を定義しますが、emailとphoneのみが許可されており、ユーザ名を選ぶことができません。 Cognitoでは選べるが、Amplify Gen2では意図的にユーザ名でのログインが隠されている事が分かります。

ただし、移行ガイドを見ると「CDKで実現可能」と書かれています。 何故隠されているのか?と、どのように実現出来るのか?を調査しました。

Gyazo

調査してみた

ユーザ名の取り扱いについて、こちらで書かれています。

何故隠されているのか

Amazon Cognito では、ユーザー名は不変です。つまり、最初のサインアップ後は、ユーザーはユーザー名を変更できません。一部のアプリケーションでは、これは望ましくない場合があり、その場合はエイリアス属性を使用することをお勧めします。エイリアス属性を使用すると、不変のユーザー名に加えて、変更可能な「優先ユーザー名」を定義できます。

Username属性は不変であり、一度設定すると変更できません。この値をログインに使用すると、リスクが発生する場合がある、という判断を行っているようです。

ただし、Gen1からGen2へのマイグレーションを行う場合、既存ではユーザ名でログインを行っていたシステムも多く、ユーザ名でどうしてもログインしたい、というニーズは存在します。

どの様に実現できるのか

ドキュメントを見ると、CDKのオーバーライドで、usernameAttributes属性からEメール、電話番号を取り除き、空配列で定義することで、ユーザ名でログインができるようです。

amplify/backend.ts

import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource.js";
import { data } from "./data/resource.js";

const backend = defineBackend({
  auth,
  data,
});

const { cfnUserPool } = backend.auth.resources.cfnResources;
// an empty array denotes "email" and "phone_number" cannot be used as a username
cfnUserPool.usernameAttributes = [];

ちなみに、usernameAttributesに、preferred_username等の他のユーザー属性を設定することはできませんでした。

usernameAttributes' failed to satisfy constraint: Member must satisfy constraint: [Member must satisfy enum value set: [phone_number, email]]

適当なログイン画面を用意します。

app/page.tsx

"use client";

import { Authenticator } from '@aws-amplify/ui-react'

import { Amplify } from "aws-amplify";
import outputs from "@/amplify_outputs.json";
import '@aws-amplify/ui-react/styles.css'
Amplify.configure(outputs);

export default function App() {
  return (
    <Authenticator>
      {({ signOut, user }) => (
        <main>
          login successful
        </main>)}
    </Authenticator>
  );
}

ログインに成功しました。

ログイン属性にユーザ名が使用されていることが分かります。

上の画像ではエイリアス属性と書かれていますが、実際には不変のUsername属性が使われており、変更することができません。

aws cognito-idp admin-get-user --user-pool-id ap-northeast-1_hogehoge --username test

{
    "Username": "test",
    "UserAttributes": [
        {
            "Name": "email",
            "Value": "hoge@hoge.jp"
        },
        {
            "Name": "email_verified",
            "Value": "true"
        },
        {
            "Name": "sub",
            "Value": "d7748af8-a081-70c7-0671-2b11432e3b71"
        }
    ],
    "UserCreateDate": "2024-06-26T06:48:48.017000+00:00",
    "UserLastModifiedDate": "2024-06-26T06:49:30.916000+00:00",
    "Enabled": true,
    "UserStatus": "CONFIRMED"
}

ユーザ名で問題なくログインできることも確認できました。

まとめ

Gen2では、ユーザ名とパスワードでのログインを勧めておらず、エイリアス属性のEmailや電話番号を勧めています。ユーザ名は、一度設定すると変更ができない為です。

しかしながら、CDKからオーバーライドを行うことで、ユーザ名でのログインは可能なので、マイグレーション等で必須の対応がある時は検討できる選択肢だと思います。