Open9

Amazon Cognitoのサインインに使えるユーザー属性の設定とその挙動について

muramasa64muramasa64

Amazon Cognitoはサインインに使える識別子の選択肢がある

AWS Management Consoleから作成しようとすると「Cognito ユーザープールのサインインオプション」として、いくつかの選択肢が出てくる。

Cognito ユーザープールのサインインオプション

これは、ユーザープールを作成する際に設定し、途中で変更できない。このスクリーンショットでは、「ユーザー名」のみ選択した状態になっている。この場合にのみ「ユーザー名の要件」のオプションが表示される。

実は、チェックする選択肢の数によっても異なる挙動になる、らしいので、どうなるのかを検証してみたい。

muramasa64muramasa64

ユーザー名として使える属性

Cognitoでは、サインインに利用可能な属性として「ユーザー名属性」と「エイリアス属性」の2種類の属性を定義している。

ユーザー名属性とエイリアス属性の違いは、ドキュメントに下記のように整理されている。

  • ユーザーが複数の属性を使ってサインアップまたはサインイン可能にするには、エイリアス属性を使う
    • メールアドレス(email)、電話番号(phone_number)、優先ユーザー名(preferred_username)
  • エイリアス属性にemailとphone_numberを使う場合は、ユーザーはそれらを所有していることを確認する必要がある
    • サインアップする際に、メールまたは電話に対して確認コードが送られてくる方式
  • エイリアス属性にemailとphone_numberを使う場合は、サインアップ時には他のユーザーが同じ値を使っていたとしても、エラーにはならない
    • つまり、エイリアス属性の場合、同じemailまたはphone_numberを登録することができる
  • エイリアス属性は登録後に変更できる

つまり、ユーザー名属性の場合は、エイリアス属性と違って、下記のような特徴がある。

  • ユーザー名属性以外の属性値でサインインできない
  • 重複した値を登録することができない(サインアップできない)
  • サインインするのに、emailやphone_numberの所有権の確認は不要
  • ユーザー名属性がemailとphone_numberの両方が有効になっている場合、サインインに使う識別子をemailからphone_numberに変更できない
    • emailやphone_number自体を変更することはできる

これをみても、何をどうしたらどうなるのかが、よくわからないので、実際に試してみることにする。

muramasa64muramasa64

ユーザープールを作成するAPIはどうなっているのか

AWS Management Consoleでユーザープールを作成しようとすると、何をどう選択するとどうなるのかがとても分かりづらいので、APIの仕様を調べてみる。この場合、AWS CLIのドキュメントがわかりやすい。

https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/create-user-pool.html

関連するオプションは--username-attributes--alias-attributesである。

--username-attributes

説明には、下記のように書いてある。

Specifies whether a user can use an email address or phone number as a username when they sign up.

つまり、ユーザーを作成する際に、ユーザー名としてemailまたはphone_numberのどちらかを使う設定ということになる。有効な値は、emailまたはphone_numberとある。つまり、ユーザー名属性にはpreferred_usernameは使えない、ということだ。

AWS Management Consoleでは、ユーザー名のみを指定することができたので、これは直感に反する。それについては一旦脇においておく。

--alias-attributes

説明には、下記のように書いてある。

Attributes supported as an alias for this user pool. Possible values: phone_number , email , or preferred_username .

phone_numberとemailとpreferred_usernameが使える、と書いてある。

AWS Management Consoleでの指定は、こちらの方のような気がする。

muramasa64muramasa64

AWS CLIでユーザープールを作成してみる

では、実際にAWS CLIを使ってユーザープールを作成してみることにする。とりあえず全部入りにしたらどうなるか。

aws cognito-idp create-user-pool --pool-name usernametest --username-attributes email --alias-attributes phone_number preferred_username

しかしこれは、エラーになる。

An error occurred (InvalidParameterException) when calling the CreateUserPool operation: Only one of the aliasAttributes or usernameAttributes can be set in a User pool.

つまり、--username-attributes--alias-attributesは同時に指定できない。なるほど。

では、気を取り直して、ユーザー名属性だけにしてみよう。

aws cognito-idp create-user-pool --pool-name usernametest --username-attributes email

結果が返された。

{
    "UserPool": {
        "Id": "us-west-2_xxxxxxxxxxx",
        "Name": "usernametest",
        "Policies": {
            "PasswordPolicy": {
                "MinimumLength": 8,
                "RequireUppercase": true,
                "RequireLowercase": true,
                "RequireNumbers": true,
                "RequireSymbols": true,
                "TemporaryPasswordValidityDays": 7
            }
        },
        "DeletionProtection": "INACTIVE",
        "LambdaConfig": {},
        "LastModifiedDate": "2024-01-25T13:38:33.287000+09:00",
        "CreationDate": "2024-01-25T13:38:33.287000+09:00",
        "SchemaAttributes": [
            {
                "Name": "sub",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": false,
                "Required": true,
                "StringAttributeConstraints": {
                    "MinLength": "1",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "name",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "given_name",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "family_name",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "middle_name",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "nickname",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "preferred_username",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "profile",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "picture",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "website",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "email",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "email_verified",
                "AttributeDataType": "Boolean",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false
            },
            {
                "Name": "gender",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "birthdate",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "10",
                    "MaxLength": "10"
                }
            },
            {
                "Name": "zoneinfo",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "locale",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "phone_number",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "phone_number_verified",
                "AttributeDataType": "Boolean",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false
            },
            {
                "Name": "address",
                "AttributeDataType": "String",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "StringAttributeConstraints": {
                    "MinLength": "0",
                    "MaxLength": "2048"
                }
            },
            {
                "Name": "updated_at",
                "AttributeDataType": "Number",
                "DeveloperOnlyAttribute": false,
                "Mutable": true,
                "Required": false,
                "NumberAttributeConstraints": {
                    "MinValue": "0"
                }
            }
        ],
        "UsernameAttributes": [
            "email"
        ],
        "VerificationMessageTemplate": {
            "DefaultEmailOption": "CONFIRM_WITH_CODE"
        },
        "UserAttributeUpdateSettings": {
            "AttributesRequireVerificationBeforeUpdate": []
        },
        "MfaConfiguration": "OFF",
        "EstimatedNumberOfUsers": 0,
        "EmailConfiguration": {
            "EmailSendingAccount": "COGNITO_DEFAULT"
        },
        "AdminCreateUserConfig": {
            "AllowAdminCreateUserOnly": false,
            "UnusedAccountValidityDays": 7
        },
        "Arn": "arn:aws:cognito-idp:us-west-2:966281585925:userpool/us-west-2_ocZLX8XJd",
        "AccountRecoverySetting": {
            "RecoveryMechanisms": [
                {
                    "Priority": 1,
                    "Name": "verified_email"
                },
                {
                    "Priority": 2,
                    "Name": "verified_phone_number"
                }
            ]
        }
    }
}

UsernameAttributesがemailになっている。他に気になる点としては、email属性がMutable: trueになっていることだ。ユーザー名をメールアドレスにしても、email属性とは関係ないらしい。

muramasa64muramasa64

ユーザーを作成してみる

では、ユーザーを作成してみる。あえてユーザー名をメールアドレスではないものにしてみる。

aws cognito-idp admin-create-user --user-pool-id $USER_POOL_ID --username foo

当然エラーになる。

An error occurred (InvalidParameterException) when calling the AdminCreateUser operation: Username should be an email.

ちゃんと作成してみる。

aws cognito-idp admin-create-user --user-pool-id $USER_POOL_ID --username foo@example.com

結果が返ってきた。

```json
{
    "User": {
        "Username": "990e8246-8f3b-489d-83fa-665da23bf781",
        "Attributes": [
            {
                "Name": "sub",
                "Value": "990e8246-8f3b-489d-83fa-665da23bf781"
            },
            {
                "Name": "email",
                "Value": "foo@example.com"
            }
        ],
        "UserCreateDate": "2024-01-25T13:42:46.263000+09:00",
        "UserLastModifiedDate": "2024-01-25T13:42:46.263000+09:00",
        "Enabled": true,
        "UserStatus": "FORCE_CHANGE_PASSWORD"
    }
}

どうやら、Username属性はメールアドレスではなく、自動生成されたUUIDになるらしい。

muramasa64muramasa64

見えてきたこと

ここまでで見えてきたこと。

  • ユーザープール作成時のパラメータとして、ユーザー名属性とエイリアス属性は排他的である
    • つまり、ユーザープールの設定として、ユーザーの認証方式としてユーザー名属性を使うか、エイリアス属性を使うか、どちらかを選択する必要がある
  • ユーザーを作成したときに、--usernameのパラメータとして渡した値が、User.Usernameになるわけではない
    • 内部的にはUUIDが自動生成されて割り振られる。こと値は変更できない。
muramasa64muramasa64

Usernameの扱われ方

AWS CLIを使ってユーザーを操作する際には、--usernameを指定する。例えば、ユーザーの情報を取得する場合はこうなる。

aws cognito-idp admin-get-user --user-pool-id $USER_POOL_ID --username foo@example.com

メールアドレスがユーザー名属性と指定されているので、メールアドレスを使うことができる。一方で、内部的なusernameとして発行されたUUIDを使うこともできる。

aws cognito-idp admin-get-user --user-pool-id $USER_POOL_ID --username 990e8246-8f3b-489d-83fa-665da23bf781

これは、どちらも同じ結果を返す。このことから、ユーザープールはユーザー名属性として設定された属性値を、ユーザー名としてみなす、という挙動になっているということがわかる。

ユーザー名属性がemailの場合に、下記のようにメールアドレスを変更できる。

❯ aws cognito-idp admin-update-user-attributes --user-pool-id $USER_POOL_ID --username foo@example.com --user-attributes "Name=email,Value=bar@example.com"

そして、もちろん、情報を取得する場合は、変更後のメールアドレスが使える。

muramasa64muramasa64

エイリアス属性を使ったユーザープール

エイリアス属性を使ったユーザープールを作成する。

❯ aws cognito-idp create-user-pool --pool-name usernametest --alias-attributes email phone_number preferred_username