👤

Amazon Cognitoのサインインに使えるユーザー属性

2024/01/25に公開

Amazon Cognitoは、ユーザーがサインインする際に使う、いわゆるユーザー名(アカウントを特定する一意の識別子、identifier)として使えるユーザー属性について整理する。

デフォルトの構成

まず、ユーザープールのデフォルトの構成について説明する。

APIではオプションを指定せずに、AWS Management Consoleではユーザー名のみ選択した状態でユーザープールを作成すると、ユーザーがサインインに使えるユーザー名は、以下のようになる。

  • ユーザー名は任意の文字列が使える
  • ユーザー名は変更できない
  • ユーザー名は他のユーザーと重複できない
  • ユーザー名以外の属性でサインインはできない

任意の文字列として、メールアドレスに相当する文字列を使うことも可能なのだが、変更できないのは不便であるためか、ユーザープールを作成する際のオプションで、ユーザー名をを変更できるようにすることができる。

オプションでサインインに使える属性

ユーザープールを作成する際にオプションとして、ユーザーがサインインする際に使える属性を、下記の3つの属性から選ぶことができる。

  • email
  • phone_number
  • preferred_username

これらのうち、1つまたは複数選ぶことができるのが最大の特徴と言える。詳細については後述する。

この設定は作成後は変更できないことに注意する。

ユーザー名属性とエイリアス属性

Cognitoでは、ユーザーがサインインする際に使える属性の種類が2つある。

  • ユーザー名属性(username attributes)
  • エイリアス属性(alias attributes)

ユーザープールを作成する際に、どちらを使うのかを決める必要がある。

それぞれ、利用可能な属性が異なる。

  • ユーザー名属性として使える属性
    • email
    • phone_number
  • エイリアス属性として使える属性
    • email
    • phone_number
    • preferred_username

それぞれ、利用可能な属性は1つまたは複数となる。つまり、ユーザー名属性として使える属性は、emailのみ、phone_numberのみ、emailとphone_numberの両方が選択できる。また、エイリアス属性でも同様で、それぞれの属性のみか、複数の属性の組み合わせが選択できる。

ユーザー名属性とエイリアス属性の違い

ユーザー名属性とエイリアス属性は、以下のような特徴の違いがある。

一意性

  • ユーザー名属性は、ユーザープール全体で一意である必要がある
  • エイリアス属性は属性によって一意性が異なる
    • emailとphone_numberは一意である必要はない
    • preferred_usernameは一意である必要がある

ユーザー名属性は、サインインに使える属性がユーザーごとに1つしかないため、一意である必要がある。ユーザーを作成するタイミングで値が重複しているとエラーになる。

一方で、エイリアス属性の場合、ユーザーを作成するタイミングでは一意である必要はないため、登録することができる。その代わり、emailまたはphone_numberでサインインしたい場合は、必ず所有しているかどうかの検証が必要になる。

検証のタイミングで、すでに他のユーザーが同じ値で検証済みになっている場合は、検証が失敗するようになっている。つまり、emailまたはphone_numberは、検証済みのものについては一意である。

ユーザー名の扱われ方

Cognitoは、内部的にusernameという属性を持つ。この値は、ユーザー作成時に設定され、後から変更はできない。

ユーザーを作成する際に、ユーザー名を指定する。この時、emailまたはphone_numberを指定した場合は、username属性に入る値は、入力された値ではなく、自動生成されたUUIDの値となる。入力された値は、それぞれの属性値として保存される。

例えば、usernameをメールアドレスとして登録すると、下記のようになる。

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

一方で、preferred_usernameが有効な状態で、メールアドレスや電話番号に相当しない値をusernameとして登録すると、下記のようになる。

{
    "User": {
        "Username": "foo",
        "Attributes": [
            {
                "Name": "sub",
                "Value": "49834ed1-5101-4c39-ab40-65a922aed885"
            }
        ],
        "UserCreateDate": "2024-01-25T15:41:25.667000+09:00",
        "UserLastModifiedDate": "2024-01-25T15:41:25.667000+09:00",
        "Enabled": true,
        "UserStatus": "FORCE_CHANGE_PASSWORD"
    }
}

内部的なUsernameには、入力した値がそのまま入る。一方で、OIDCで使われるsubの値は、UUIDが自動的に発行される。subは変更不可である。

Usernameは変更不可なのだが、もしサインインに使うユーザー名を変更したい場合は、preferred_usernameを設定すると、サインインする際にユーザー名として使うことができる。

API実行するときのusername

APIを実行する際にusernameが必要になるケースでは、必ずしもusername属性を使う必要はない。ユーザー名属性や、エイリアス属性として指定されている値を使うことができる。sub属性は使えそうで使えない。

アカウントを特定する際に変更可能な値を使うと、意図したのと別のアカウントが取得できてしまう場合があるというのに注意する必要がある。可能な限り、username属性を使うのがいいだろう。

AWS Management Consaleでユーザープールを作成する場合の注意

AWS Management Consoleでユーザープールを作成する場合は、デフォルト設定になるのか、オプションとしてユーザー名属性になるのか、エイリアス属性になるのかが分かりづらい。

下記は、設定をする箇所のスクリーンショット。

ここで「ユーザー名」のみを選択すると、オプションを指定しないデフォルトの状態になる。AWS CLIでcreate-usel-poolを実行する際に、--username-attributesまたは--alias-attributesを指定しないのと同じになる。

「Eメール」または「電話番号」のみを選択するか、「Eメール」と「電話番号」の2つを選択すると、「ユーザー名属性」としてemailまたはphone_numberが使われる状態となる。AWS CLIでは、--username-attributesを指定したのと同じになる。

「ユーザー名」と他の値の組み合わせを選択すると、「エイリアス属性」という扱いになる。AWS CLIでは、--alias-attributesを指定したのと同じになる。

どれを選択したのかによって挙動が変わってしまうが、それについての細かい説明がない状態なので、実際に試してみないとわからない。しかも、選択肢によって細かい挙動が異なってしまう。

ユーザープールの作成をする際は、オプションの設定による挙動の違いをちゃんと理解し、よく検討してからどのオプションにするのかを決めたほうが良いだろう。個人的には、APIを使って作成した方が曖昧性がなくて良いと思う。

どの設定が良いのか

色々なユースケースがあるので、どれが良いというのは一概には言えない。個人的には、電話番号でサインインするというユースケースはあまり思いつかないので、Eメールだけで良いのではないかと思うが、電話番号を除外する積極的な理由もない。

ただ、いずれのユースケースでも、デフォルトの設定だけは避けた方が良いと思う。理由は以下の通り。

ユーザー名が変更できないのは不便

デフォルトの設定だと、ユーザー名は作成したときに任意に決められるものの、その後変更できないのはやはり不便だと思う。任意の文字列が利用できるため、メールアドレスのようなものも使えるが、メールアドレスとしては扱われないので、混乱が生じる可能性が高い。

一方で、オプション設定でユーザー名属性またはエイリアス属性としてメールアドレスや電話番号を登録すれば、それをサインインに使えて、かつ、変更することができるので実用性が高い。

identifierは自動生成で推測可能性が低い値が良い

CognitoのUsernameは、変更不可で一意であるという、いわゆるIDとして使われる値である。一般的に、identifierとして使われる値は、将来に渡って変更されることがなく、かつ、推測可能性が低い方が良いのではないかと、個人的には思う(それが一般的かどうかはわからない)。そして、その値はユーザーが意識する必要はないとも思う。

ユーザー名属性にするか、エイリアス属性として「ユーザー名」を使わないような構成であれば、Usernameの値は自動的に生成されたUUIDが利用されることになる。

ただし、エイリアス属性でユーザー名も有効にした場合に、ユーザーを作成する際にユーザー名として使う値を意のユーザ名にした場合は、Username属性の値はUUIDにはならず、入力された値が使われることになる[1]

任意のユーザーを使わせる理由がないなら、ユーザー名属性としてemailとphone_numberを使う、というのが良い気がする。

補足 フェデレーションユーザーの場合

ところで、これまではCognitoに直接作成されるユーザーのUsername属性について解説をしてきた。フェデレーションユーザーの場合について補足する。

フェデレーションユーザーは、事前にCognitoにユーザーを作成するのではなく、初回のサインイン時に自動的にユーザープールに作成される。このときのUsernameの値は、自動生成されるUUIDに、プレフィックスとして外部のプロバイダー名が付与される。例えば、外部プロバイダー名がoktaだった場合は、okta_5c529477-45d7-4391-9262-72b6b65c8163のような形式となる。

なお、事前にCognitoにユーザーを作成しておきつつ、外部プロバイダーからのユーザーをリンクすることもできる[2]。この場合は、上記のようなUsernameのユーザーは作成されず、既存のユーザーとしてサインインされることになる。

脚注
  1. デフォルトの設定に引きづられている感があるが、最初からpreferred_usernameとして設定してくれた方が良かったのではないかと個人的には思う。CognitoのUIを使わないのであれば、UUIDを生成してそれをUsernameとして使い、入力された値をpreferred_usernameに設定するという処理にしても良いかもしれない。 ↩︎

  2. 詳しくは以前に書いた記事のAmazon CognitoとOpenID Connect Providerを連携してアカウントをリンクするを参照して欲しい。 ↩︎

Discussion