91
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS Amplify × Cognito】サインインオプション変更で大苦戦! Authを作り直すプロセスまとめ

Last updated at Posted at 2024-12-14

こんな人に読んでほしい!

  • サインインオプションを変更したい人
  • amplify add auth でサインインオプションを仮決めしたけど、後で変更する方法とその工数が気になる人
  • amplifyでauthを作り直したい人

なぜAuthを作り直すのか?

サインインオプションを変えたかっただけなのに...

なぜAuthの設定を作り直すことにしたのか?
それは、メールアドレスでサインインしたかったからです。
既存の認証設定ではサインインオプションが「ユーザー名」になっていたため、メールアドレスでのサインインができませんでした。
スクリーンショット 2024-12-11 17.57.02.png

ところが、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を使用します。通常、リソースを削除する際は、以下の手順を踏みます:

  1. amplify remove ~コマンドで、削除したいリソースの設定を削除する
  2. amplify pushを実行して、リソースの削除をCloudFormationに適用させる

ただし、Cloudformationでリソースを削除する前に削除ポリシーを設定しておくことで、リソース本体が削除されるのを防ぐことができます

authへの削除ポリシーの付与

削除ポリシーを設定する基本的な方法は、CloudFormationテンプレートファイルに "DeletionPolicy": "Retain" を追加することです。(例↓)

cloudformation-template.json
"Resources":{
   "UserPool": {
     "Type": "AWS::Cognito::UserPool",
     "DeletionPolicy": "Retain", // ←⭐️ここ
   }
}

しかし、Amplifyでauthフォルダ内のcloudformation-template.jsonに同じ方法で記述しましたが、うまくいきませんでした。

理由は、amplify/backend/auth フォルダ内を見ると、build フォルダの中にcloudformation-template.json が存在するためです。

スクリーンショット 2024-12-07 0.45.05.png

build フォルダはAmplifyによって自動生成・上書きされるため、手動で変更しても変更内容は適用されません。

そのため、Cognitoリソースを上書きするために次のコマンドを使用します

$ amplify override auth

スクリーンショット 2024-12-15 0.01.40.png

  • 結果
    build外にoverride.tsファイルが追加されました。

ここに⭐️のコードを追加します

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以外にも残しておきたいリソースがある場合の削除ポリシーの書き方
    1. build/hoge-cloudformation-template.jsonを開き、対象リソースを確認して残すものを選ぶ
    2. 削除ポリシー設定
      userPoolClientの削除を阻止する場合は、
      resources.userPoolClient.addOverride('DeletionPolicy', 'Retain');のように設定する

削除ポリシーの設定を付与したら
amplify pushを実行して、変更をCloudFormationに適用させます。

適用されたことの確認

削除ポリシーが適用されたかを確認するためには、buildフォルダ内にあるcloudformation-template.jsonを確認するのが簡単です。以下のように削除ポリシーが追加されていれば、設定が正しく適用されています。

auth/authhoge/build/hogehoge-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

スクリーンショット 2024-12-07 12.19.51.png

AppSync

この依存を断ち切るためにはamplify update apiコマンドで、以下でCognito User Pool以外を選ぶ必要があります。

スクリーンショット 2024-12-07 14.46.31.png

しかし、このコマンドは schema.graphql に @auth が記述されている場合、正常に実行できません。

  • そのため、まず @auth の部分を取り除きます
amplify/backent/api/hoge/schema.graphql
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を参照していたためエラーになりました

override.ts
'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

スクリーンショット 2024-12-07 15.25.21.png

無事消えました。
やったこれで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つの方法があります。

  1. ユーザー移行の Lambda トリガーを使用したユーザーのインポート
  2. CSV ファイルからユーザープールにユーザーをインポートする

この記事では、 CSV ファイルからユーザープールにユーザーをインポートする方法 のみを取り上げます。

また、これらの方法だけでは対応できない認証情報の移行について、特にサードパーティの認証情報の移行についても説明します。

CSV ファイルからユーザープールにユーザーをインポートする

※ CognitoをCSV ファイル化する方法については記事内で取り上げません。

  • できること

    • 標準ユーザ属性の移行
  • できないこと

    • パスワードの移行
    • カスタムユーザー属性や、フェデレーションユーザー(例えば、GoogleやFacebook、LINEなどでサインインするユーザー)を既存のCognitoユーザープロファイルにリンクすること
    • 元のsubを引き継ぐ

実際にやってみた

  • 準備物
    • 移行するために必要な準備物は、ユーザー情報が入ったCSVファイルです。以下はその例です:
test.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ファイルをアップロードします

スクリーンショット 2024-12-08 23.57.11.png

しばらく待つと、ユーザーが新しいユーザープールに作成されていることが確認できました。これで簡単に移行が完了です!

Cognitoではパスワードのインポートはサポートされていません。移行後、ユーザーは最初にサインインした際にパスワードを変更する必要があります。

画像にもある通り、移行後のユーザーは「パスワードのリセットが必要」な状態になります。つまり移行後すぐにsignInメソッドでログインすることはできません。
resetPassword メソッドを使用して、ユーザーにパスワードのリセットを促す必要があります。

サードパーティの認証情報の移行

※ 次のような場合に必要です

  • 移行元のUser Pool で、Google、Facebook、LINE などの外部認証サービスを利用しているユーザーがいる場合
  • 既存の外部認証サービスを使用したサインインを新しいUser Poolでも継続させたい場合

Cognitoでは、サードパーティの認証情報(LINEやFacebookのユーザーIDなど)は、ユーザープールの identitiesとして管理されていますが、この部分は、「CSVファイルからのインポート」では移行できません。

(例)LINEログインを利用して Cognito にサインインできるユーザーの情報
スクリーンショット 2024-12-11 17.44.42.png

スクリーンショット 2024-12-11 17.46.51.png

そのため、サードパーティ認証情報を既存のCognitoユーザーにリンクさせるためには、AdminLinkProviderForUser メソッドを使用する必要があります。

以下は、Cognito ユーザーに LINE の認証情報をリンクさせる Python のコード例です

index.py
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 の両方を設定した場合、サインアップで以下のエラーが発生することがあります。
スクリーンショット 2024-12-11 16.22.33.png

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が自動的に生成されます。

スクリーンショット 2024-12-08 22.07.17.png

最後に

多くの試行錯誤を経て、「auth(ユーザープール)の再構築と切り替え」と「ユーザ移行」を無事に行うことができました。記事の目次を見てもわかるように、サインインオプションを変更したいだけだったのですが思った以上に大変な作業でした。

そのため、これからサインインオプションを決定しようとしている方は、ぜひ慎重に決めていただきたいと思います。また、変更せざるを得ない状況の方には、少しでも参考になれば幸いです。
読んでいただき、ありがとうございました!

91
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
91
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?