Amazon Cognitoのサインインに使えるユーザー属性
Amazon Cognitoは、ユーザーがサインインする際に使う、いわゆるユーザー名(アカウントを特定する一意の識別子、identifier)として使えるユーザー属性について整理する。
デフォルトの構成
まず、ユーザープールのデフォルトの構成について説明する。
ユーザープールを作成する際に、オプションを以下のようにする。
- APIを使う場合: オプションを指定しない
- AWS Management Consoleを使う場合: 「ユーザー名」のみを選択する
すると、ユーザー名の設定がデフォルトとなり、以下のような挙動になる。
- ユーザー名は任意の文字列が使える
- ユーザー名は変更できない
- ユーザー名は他のユーザーと重複できない
- ユーザー名以外の属性でサインインはできない
ユーザー名として使える任意の文字列として、メールアドレスに相当する文字列を使うことも可能なのだが、変更できないのは不便である。
そのためか、ユーザープールを作成する際のオプションで、ユーザー名をを変更できるようにすることができる。
オプションでサインインに使える属性
ユーザープールを作成する際のオプションとして、ユーザーがサインインする際に使える属性を、下記の3つの属性から選ぶことができる。
- phone_number
- preferred_username
これらのうち、1つまたは複数選ぶことができるのが最大の特徴と言える。詳細については後述する。
ユーザー名属性とエイリアス属性
Cognitoでは、ユーザーがサインインする際に使える属性の種類が2つある。
- ユーザー名属性(username attributes)
- エイリアス属性(alias attributes)
ユーザープールを作成する際に、どちらを使うのかを決める必要がある。
それぞれ、利用可能な属性が異なる。
- ユーザー名属性として使える属性
- phone_number
- エイリアス属性として使える属性
- 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をメールアドレスとして登録すると、下記のようになる。
{
"User": {
"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は変更可能である。ユーザーはpreferred_usernameを変更可能なユーザー名として使うことができる。
API実行するときのusername
APIを実行する際にusernameが必要になるケースでは、必ずしもusername属性を使う必要はない。ユーザー名属性や、エイリアス属性として指定されている値を使うことができる。しかし、sub属性は使えそうで使えない。
APIなどで、アカウントを特定するパラメータとして変更可能な値を使うと、意図したのと別のアカウントが取得できてしまう場合があるというのに注意する必要がある。可能な限り、変更不可である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のユーザーは作成されず、既存のユーザーとしてサインインされることになる。
-
デフォルトの設定に引きづられている感があるが、最初からpreferred_usernameとして設定してくれた方が良かったのではないかと個人的には思う。CognitoのUIを使わないのであれば、UUIDを生成してそれをUsernameとして使い、入力された値をpreferred_usernameに設定するという処理にしても良いかもしれない。 ↩︎
-
詳しくは以前に書いた記事のAmazon CognitoとOpenID Connect Providerを連携してアカウントをリンクするを参照して欲しい。 ↩︎
Discussion