こんな人に読んでほしい!
- サインインオプションを変更したい人
-
amplify add auth
でサインインオプションを仮決めしたけど、後で変更する方法とその工数が気になる人 - amplifyでauthを作り直したい人
なぜAuthを作り直すのか?
サインインオプションを変えたかっただけなのに...
なぜAuthの設定を作り直すことにしたのか?
それは、メールアドレスでサインインしたかったからです。
既存の認証設定ではサインインオプションが「ユーザー名」になっていたため、メールアドレスでのサインインができませんでした。
ところが、Amazon Cognito ではサインインオプションを変更することができないと公式ドキュメントよりわかりました。
ユーザープールを作成した後に、この設定を変更することはできません。以下の設定を変更する場合は、新しいユーザープールまたはアプリクライアントを作成する必要があります。
サインインオプションの変更はできない...だと!?
やりたかったことは2つ
- 商用環境でも既に使用しているユーザープールをそのまま維持し、既存のユーザーと設定を保持したい
- 新しいユーザープールを作成し、それをAmplifyに紐付けたい
いくつかの問題に直面しましたが、無事にやりたいことを実現することができました。
今回はその経験をもとに、困ったことや解決方法を共有します。
予想外だった課題
Amplifyで認証設定(auth)を新しいものに切り替える際に直面した困難と、そこで気づいたことを3つ紹介します。
amplify add auth
は既存のauthがあると実行できない
既存のauthが紐づけられている場合、以下のようにamplify add auth
コマンドが効きません。
$ amplify add auth
⚠️ Auth has already been added to this project. To update run amplify update auth.
amplifyが複数個のauthの紐付けに対応していたら楽だったんですが、今紐づいているauthをamplify remove auth
する必要があります。
amplify remove auth
は、「リソースを消すか?」の質問にyesでなければ実行されない
amplify add auth
で作成したauthを切り離す場合を前提としています。
amplify import auth
で外部のauthを取り込んだ場合、この問題は発生しません。
既存authをamplifyから切り離そうとすると
$ amplify remove auth
? Are you sure you want to delete the resource? This action deletes all files related to this resource from the backend directory.
「ソースを削除してもよろしいですか?」と確認されます。
既存のアプリがすでに商用で使われている場合、現行のAuthをそのまま削除するわけにはいきません。しかし、ここで「No」と答えるとamplify remove auth
の処理は中断されてしまいます
そのため、「リソースを消すか」の質問でyesと答えつつ、リソースそのものが消されない工夫をする必要があります
amplify remove auth
はauthとの依存関係が残っていると実行できない
amplify remove auth
は、少しでもauthに依存しているリソースが残っていると以下のようにエラーが発生します。
例1: analyticsがauthに依存しているためエラー
$ amplify remove auth
🛑 Auth cannot be removed because the analytics category depends on it
Resolution: Run `amplify remove analytics` first, then retry removing auth
Learn more at: https://docs.amplify.aws/cli/project/troubleshooting/
例2: userPoolGroupsがauth(userPool)に依存しているためエラー
$ amplify remove auth
🛑 Resource cannot be removed because it has a dependency on another resource
Dependency: Cognito-UserPool-Groups - userPoolGroups. Remove the dependency first.
例3: Lambdaがauthに依存しているためエラー
$ amplify remove auth
🛑 Resource cannot be removed because it has a dependency on another resource
Dependency: Lambda - hogefunctionName. Remove the dependency first.
例4: AppSyncがauthに依存しているためエラー
$ amplify remove auth
🛑 Resource cannot be removed because it has a dependency on another resource
Dependency: AppSync - viteproject. Remove the dependency first.
そのため、amplify remove auth
を実行する前に、auth に依存しているリソースを全て切り離す必要があります
Amplifyで新authに切り替える
ここでは、実際に課題を解決するために行った具体的な手順について説明します。
リソース削除を阻止する
削除ポリシーについて
- 削除ポリシーとは
CloudFormationにおいてリソースを削除する際に、そのリソースが意図せず削除されないように保護するための設定のこと
Amplifyでは、リソース(例えば、auth)を作成する際に、CloudFormationを使用します。通常、リソースを削除する際は、以下の手順を踏みます:
-
amplify remove ~
コマンドで、削除したいリソースの設定を削除する -
amplify push
を実行して、リソースの削除をCloudFormationに適用させる
ただし、Cloudformationでリソースを削除する前に削除ポリシーを設定しておくことで、リソース本体が削除されるのを防ぐことができます
authへの削除ポリシーの付与
削除ポリシーを設定する基本的な方法は、CloudFormationテンプレートファイルに "DeletionPolicy": "Retain"
を追加することです。(例↓)
"Resources":{
"UserPool": {
"Type": "AWS::Cognito::UserPool",
"DeletionPolicy": "Retain", // ←⭐️ここ
}
}
しかし、Amplifyでauthフォルダ内のcloudformation-template.json
に同じ方法で記述しましたが、うまくいきませんでした。
理由は、amplify/backend/auth
フォルダ内を見ると、build
フォルダの中にcloudformation-template.json
が存在するためです。
build
フォルダはAmplifyによって自動生成・上書きされるため、手動で変更しても変更内容は適用されません。
そのため、Cognitoリソースを上書きするために次のコマンドを使用します
$ amplify override auth
- 結果
build外にoverride.tsファイルが追加されました。
ここに⭐️のコードを追加します
import { AmplifyAuthCognitoStackTemplate, AmplifyProjectInfo } from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyAuthCognitoStackTemplate,
amplifyProjectInfo: AmplifyProjectInfo) {
resources.userPool.addOverride('DeletionPolicy', 'Retain') // ⭐️1行追加
}
-
UserPool以外にも残しておきたいリソースがある場合の削除ポリシーの書き方
-
build/hoge-cloudformation-template.json
を開き、対象リソースを確認して残すものを選ぶ - 削除ポリシー設定
userPoolClientの削除を阻止する場合は、
resources.userPoolClient.addOverride('DeletionPolicy', 'Retain');
のように設定する
-
削除ポリシーの設定を付与したら
amplify push
を実行して、変更をCloudFormationに適用させます。
適用されたことの確認
削除ポリシーが適用されたかを確認するためには、build
フォルダ内にあるcloudformation-template.json
を確認するのが簡単です。以下のように削除ポリシーが追加されていれば、設定が正しく適用されています。
"UserPool": {
"Type": "AWS::Cognito::UserPool",
"Properties": {},
"DeletionPolicy": "Retain" ←⭐️適用された
},
cloufgormation-templateの中身は、AWSマネジメントコンソールのCloudFormationからも確認できます。
これで、削除ポリシーがリソースに追加され、amplify remove auth
を実行してもリソースが削除されないようになりました。
開発環境と商用環境がある場合、amplify remove auth
を実行する前に商用環境にも削除ポリシーを適用させてください。
理由は削除ポリシー後のamplify remove auth
の実行で、削除ポリシーが適用されたファイルごと削除されてしまうからです。
authとの依存関係をなくす
Lambda
$ amplify update function
? Select the Lambda function you want to update hogefunctionName
? Which setting do you want to update? Resource access permissions
AppSync
この依存を断ち切るためにはamplify update api
コマンドで、以下でCognito User Pool以外を選ぶ必要があります。
しかし、このコマンドは schema.graphql に @auth
が記述されている場合、正常に実行できません。
- そのため、まず
@auth
の部分を取り除きます
type Todo @model
- @auth(rules: [
- { allow: public, operations: [read] },
- { allow: owner, ownerField: "owner", operations: [create, update, delete] }
- ]) {
+ {
id: ID!
name: String!
owner: String
}
- その後
amplify update api
でGraphQLの依存先を、CognitoからAPI Keyに変更します。
$ amplify update api
? Select from one of the below mentioned services: GraphQL
? Select a setting to edit Authorization modes
? Choose the default authorization type for the API API key
✔ Enter a description for the API key: ·
✔ After how many days from now the API key should expire (1-365): · 7
? Configure additional auth types? No
この設定変更により、Cognitoの依存関係が解除され、API Keyを利用した認証に変更されます
※ この変更はGraphQLをauthから引き離すための暫定的な措置です。authの作り直しと紐付けが終われば再度Authorization modesを「Cognito User Pool」に戻します。
Analytics
amplify update analytics
コマンドで、Pinpoint(Analytics)とauthの依存関係を断ち切れるか試してみましたが、残念ながら以下のように、設定変更の機能が提供されていませんでした
amplify update analytics
? Select from one of the below mentioned services: Amazon Pinpoint
🛑 Update functionality not available for this service
そのため、removeしました。
$ amplify remove analyics
過去の分析データを消したくない場合は、先に削除ポリシーを付与する必要がありますので注意してください。
その他
function, api, analytics, userPoolGroups以外の部分でauthを参照している箇所があるとエラーになることがあります。
自分の場合はAPI GatewayでCognitoユーザープールをオーソライザーとして使用しており、APIのoverride.tsの部分でuserpoolIdを参照していたためエラーになりました
'Fn::GetAtt': ['authresourcename', 'Outputs.UserPoolId'],
auth周り参照部分はコメントアウトしておきます
amplify remove auth
の実行
$ amplify remove auth
? Are you sure you want to delete the resource? This action deletes all files related to this resource fr
om the backend directory. Yes
✅ Successfully removed resource
無事消えました。
やったこれでauthを追加することができます。
新しいauthの紐付け
amplify import auth
で既存のauthを紐付ける
既存のCognito User Poolを新しいAmplifyプロジェクトに紐付ける場合、amplify import auth
コマンドを使用します。このコマンドを実行することで、既存の認証設定を新しいAmplifyプロジェクトにインポートできます。
$ amplify import auth
Using service: Cognito, provided by: awscloudformation
? What type of auth resource do you want to import? …
❯ Cognito User Pool and Identity Pool
Cognito User Pool only
? Select the User Pool you want to import: …
hogehoge11111111_userpool_11111111-dev (ap-northeast-1_iiiiiiiii)
hogehoge22222222_userpool_22222222-dev (ap-northeast-1_ooooooooo)
...userpoolがあるだけ表示されます。 ⭐️1つ選ぶ!
✔ Select a Web client to import: · hogehogeWebClient
✔ Select a Native client to import: hogehogeNativeClient
amplify add auth
で新規を作成・紐づける
amplify add auth
コマンドを使って、新規の認証リソースを作成することもできます。この方法では、対話形式で様々な認証オプションを選択することができます。
色々選べますので、一例を載せます
$ amplify add auth
Do you want to use the default authentication and security configuration? (Use arrow keys)
❯ Default configuration
Default configuration with Social Provider (Federation)
Manual configuration
I want to learn more.
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in? (Use arrow keys)
Username
❯ Email
Phone Number
Email or Phone Number
I want to learn more.
Do you want to configure advanced settings? No, I am done.
✅ Successfully added auth resource viteproject11111111 locally
新しいauthに対して、依存先を向ける
以下のリソースが新しいAuth設定を参照するように向け直します:
- Lambda関数
- AppSync(GraphQL API)
- Analytics(Pinpoint)
- その他のauthをGetAttで参照しているリソース(例: override.ts)
上記のauthへの依存をなくすの逆の操作になるため、ここでは詳細を省略します。
ざっくりというと、amplify update
コマンドを使用するか、override.tsなどでauthを参照している設定ファイルを編集するなどの操作を行います
amplify push
で新しいAuth設定を適用
お疲れ様でした。
amplify push
します!
これでAmplifyで新authが適用され、サインアップ時には、新ユーザプールが使用されます!
Cognitoユーザーの移行
新しいユーザープールを作成するだけでは、旧ユーザープールに存在していたユーザー情報が新しいプールには含まれていません。そのため、既存のユーザーはサインインできないため、ユーザー移行を行う必要があります
ユーザー移行の方法として、主に2つの方法があります。
- ユーザー移行の Lambda トリガーを使用したユーザーのインポート
- CSV ファイルからユーザープールにユーザーをインポートする
この記事では、 CSV ファイルからユーザープールにユーザーをインポートする方法 のみを取り上げます。
また、これらの方法だけでは対応できない認証情報の移行について、特にサードパーティの認証情報の移行についても説明します。
CSV ファイルからユーザープールにユーザーをインポートする
※ CognitoをCSV ファイル化する方法については記事内で取り上げません。
-
できること
- 標準ユーザ属性の移行
-
できないこと
- パスワードの移行
- カスタムユーザー属性や、フェデレーションユーザー(例えば、GoogleやFacebook、LINEなどでサインインするユーザー)を既存のCognitoユーザープロファイルにリンクすること
- 元のsubを引き継ぐ
実際にやってみた
- 準備物
- 移行するために必要な準備物は、ユーザー情報が入ったCSVファイルです。以下はその例です:
cognito:username,name,given_name,family_name,middle_name,nickname,preferred_username,profile,picture,website,email,email_verified,gender,birthdate,zoneinfo,locale,phone_number,phone_number_verified,address,updated_at,cognito:mfa_enabled
johndoe@example.com,,John,Doe,,,,,,,johndoe@example.com,TRUE,,02/01/1985,,,+12345550100,TRUE,123 Any Street,,FALSE
janeroe@example.com,,Jane,Roe,,,,,,,janeroe@example.com,TRUE,,01/01/1985,,,+12345550199,TRUE,100 Main Street,,FALSE
csvの中身は公式ドキュメントに従っていますが、そのまま使用すると次のようなエラーが発生しました
[FAILED] Line Number 2 - Username should be an email.
おそらく、サインインオプションが「email」を想定したサンプルではないため、エラーが発生したと思われます。そのため、cognito:usernameの部分を適切に変更しました
- 次に、Cognitoマネジメントコンソールを使ってユーザーをインポートします
- 「ユーザー」タブを選択し、左側のメニューから「インポートジョブを作成」を選び、CSVファイルをアップロードします
しばらく待つと、ユーザーが新しいユーザープールに作成されていることが確認できました。これで簡単に移行が完了です!
Cognitoではパスワードのインポートはサポートされていません。移行後、ユーザーは最初にサインインした際にパスワードを変更する必要があります。
画像にもある通り、移行後のユーザーは「パスワードのリセットが必要」な状態になります。つまり移行後すぐにsignInメソッドでログインすることはできません。
resetPassword メソッドを使用して、ユーザーにパスワードのリセットを促す必要があります。
サードパーティの認証情報の移行
※ 次のような場合に必要です
- 移行元のUser Pool で、Google、Facebook、LINE などの外部認証サービスを利用しているユーザーがいる場合
- 既存の外部認証サービスを使用したサインインを新しいUser Poolでも継続させたい場合
Cognitoでは、サードパーティの認証情報(LINEやFacebookのユーザーIDなど)は、ユーザープールの identities
として管理されていますが、この部分は、「CSVファイルからのインポート」では移行できません。
(例)LINEログインを利用して Cognito にサインインできるユーザーの情報
そのため、サードパーティ認証情報を既存のCognitoユーザーにリンクさせるためには、AdminLinkProviderForUser メソッドを使用する必要があります。
以下は、Cognito ユーザーに LINE の認証情報をリンクさせる Python のコード例です
cognito_client = boto3.client('cognito-idp')
destination_user = {
'ProviderName': 'Cognito',
'ProviderAttributeValue': cognito_username # サードパーティ認証情報を追加したいCognitoユーザのusername
}
source_user = {
'ProviderName': 'LINE', # サードパーティ名
'ProviderAttributeName': 'Cognito_Subject',
'ProviderAttributeValue': line_user_id # サードパーティでのユーザID
}
try:
response = cognito_client.admin_link_provider_for_user(
UserPoolId=USERPOOL_ID,
DestinationUser=destination_user,
SourceUser=source_user
)
except:
# 省略
認証メソッドの注意点と変更点
Amplify を使用したフロントエンド側での認証処理についての内容です。
usernameとしてemailを渡して認証メソッドを使う
signIn
, signUp
, confirmSignUp
, resetPassword
など、さまざまな認証メソッドでusername
としてemail
を使用する必要があります。
これに関する具体的なコード紹介します。
signInメソッド
import { signIn } from 'aws-amplify/auth';
// ↓ ① ❌ エラー発生
await signIn({ email, password });
// ↓ ② ⭕️ 使用可能
await signIn({ username: email, password });
// ↓ ③ ⭕️ 使用可能
const handleSignIn = (username: string, password: string) => {
signIn({ username, password });
};
handleSignIn(email, password); // 引数に email を渡す
- signInに限らず、ほかの認証メソッドでもusernameとしてemailを渡す形で機能します
ただし、例外もあります。
サインインオプションに username と email の両方を設定した場合、サインアップで以下のエラーが発生することがあります。
Error: Username cannot be of email format, since user pool is configured for email alias.
この問題の詳細な対処法は本記事では触れませんが、注意が必要です。
username:email
としても、usernameがemailの値で登録されるわけではない
signUp メソッドでの動作についてです
signUpメソッド
サインインオプションが email
のみに設定されている場合、signUp メソッドで username: email
を使っても、ユーザー名(username
)として email
がそのまま登録されるわけではありません。
// サインインオプション:emailのみ
await signUp({
username: email,
password,
options:{
userAttributes: {
email,
},
autoSignIn: {
enabled: true,
},
}
});
-
結果
username = email
で登録されると思っている方もいるかもしれませんが、実際にはusername
として uuidが自動的に生成されます。
最後に
多くの試行錯誤を経て、「auth(ユーザープール)の再構築と切り替え」と「ユーザ移行」を無事に行うことができました。記事の目次を見てもわかるように、サインインオプションを変更したいだけだったのですが思った以上に大変な作業でした。
そのため、これからサインインオプションを決定しようとしている方は、ぜひ慎重に決めていただきたいと思います。また、変更せざるを得ない状況の方には、少しでも参考になれば幸いです。
読んでいただき、ありがとうございました!