AWS Cognitoを使用したWebおよびiOSアプリのユーザー認証(パート1)
公開: 2022-03-10何年にもわたって、私は少なくとも3つのユーザー管理システムをゼロから構築してきました。 アプローチの多くは定型文に基づくことができますが、特定のクライアント向けにカスタマイズする必要のある重要な項目が常にいくつかあります。 これは、ユーザー管理、認証、および承認サービスのカテゴリ全体がこのニーズを満たすために生まれたという十分な懸念事項です。 Auth0のようなサービスには、開発者が統合できるユーザーおよびID管理に基づくソリューション全体があります。
この機能を提供するサービスの1つは、Amazon Web Services(AWS)のCognitoです。 Cognitoは、ユーザーが作成したWebおよびモバイルアプリケーションにサインアップしてサインインできるようにするためのツールです。 この機能に加えて、ユーザーデータをオフラインで保存することもでき、このデータの同期を提供します。 Amazonが述べているように、「Amazon Cognitoを使用すると、ユーザー管理、認証、デバイス間の同期を処理するソリューションの構築、保護、スケーリングについて心配することなく、優れたアプリエクスペリエンスの作成に集中できます。」
カルーセルを過小評価している
カルーセルは、何年にもわたって得た悪い評判に値するものではありません。 それらは非常に効果的であり、多くの形とサイズで提供されることが証明できます。 関連記事を読む→
昨年、AmazonはCognitoサービスにカスタムユーザープールを追加しました。 この機能は、ほとんどのユースケースに適合するために必要な柔軟性を備えた、完全でカスタマイズ可能なクロスプラットフォームのユーザー管理システムを実現するために私や他の開発者が必要とするものを提供します。 その理由を理解するには、ユーザー管理とは何か、そしてそれがどのような問題を解決するのかを簡単に調べる必要があります。
この記事では、私たちのニーズに合わせてユーザープールを構成するプロセスの大部分を説明します。 次に、このユーザープールをiOSアプリケーションと統合し、ユーザーがログインしてユーザーアカウントに関連付けられた属性を取得できるようにします。 最終的には、限定されたデモアプリケーションが用意されますが、ユーザー管理のコアを処理するアプリケーションです。 さらに、これが実施された後、これをかなり深く理解するフォローアップ記事があります。
ユーザー管理から何が必要ですか?
モバイルアプリまたはウェブアプリをお持ちの場合、ユーザー管理に関して正確に何が必要ですか? ユーザーログインはおそらく最初に思い浮かぶことですが、そこで止まることはできません。 ほとんどのWebおよびモバイルアプリのユースケースで機能する柔軟なユーザー管理システムが必要な場合は、次の機能が必要になります。
- ユーザー名とパスワードのログイン。
- 安全なパスワードのハッシュと保存。
- パスワードの変更。
- パスワードポリシーと検証。
- ユーザーのライフサイクルトリガー(ウェルカムメール、さようならメールなど)。
- ユーザー属性(名、姓など)。
- ユーザーごとに必要な構成とオプションの属性。
- 忘れたパスワードの処理。
- SMSによる電話番号の検証。
- メールによる確認。
- 権限に基づくエンドポイントへのAPIアクセス。
- モバイルデバイス上のアクセストークンの安全な保管。
- モバイルデバイスのユーザー属性のオフラインストレージ。
- オンライン状態とオフライン状態のユーザー属性の同期。
- 多要素認証。
ユーザー管理は最初はログインシステムのように見えるかもしれませんが、システムがほとんどのユースケースを処理するのに十分な柔軟性を備えているためには、機能がそれをはるかに超えている必要があります。 これは明らかに、ユーザー名とパスワードだけではありません。
ここでは、セキュリティという1つの追加項目を呼び出す必要があります。 ユーザー管理システムの要件の1つは、システム全体のセキュリティを継続的に評価する必要があることです。 多くのカスタムユーザー管理システムには、単に修正されていない脆弱性があります。 昨年、Dropbox、Dailymotion、Twitter、Yahooなどの企業のユーザー管理システムのセキュリティ違反が発生しました。 カスタムソリューションを構築することを選択した場合は、システムを保護するためのフックにいます。
AmazonCognitoを入力してください
Amazon Cognitoは、柔軟でスケーラブルなユーザー管理システムをWebおよびモバイルアプリケーションに統合できるようにするマネージドサービスです。 Cognitoは、サービスを利用する2つの異なる方法を提供します。Facebookなどのソーシャルネットワークを介したログインを可能にするフェデレーションIDと、特定のアプリまたはアプリケーションスイートの完全にカスタムなユーザー管理機能を提供するユーザープールです。
フェデレーションIDは、ユーザーがFacebook(またはGoogle、Amazonなど)でログインできるようにする場合に最適ですが、これは、ユーザー管理プロセスの一部がFacebookにアウトソーシングされることを意味します。 これは許容できる場合もありますが、ユーザーは自分のFacebookアカウントをアプリケーションに接続したくない場合があります。 さらに、ユーザーのライフサイクルの多くを直接管理したい場合があります。このため、フェデレーションIDはそれほど柔軟ではありません。 今日の記事では、ほとんどのユースケースに適合する堅牢なユーザー管理プラットフォームに必要な柔軟性を提供するユーザープールに焦点を当てます。 このようにして、ほとんどすべてのプロジェクトで使用できるアプローチが得られます。
これはAWSサービスであるため、Cognitoを使用することには他にも利点があります。 CognitoはAPIGatewayと統合して、Cognitoログインから返されたトークンに基づいてAPIアクセスを承認するための簡単な方法を提供できます。 さらに、モバイルアプリケーションに他のAWSサービスをすでに利用している場合は、ユーザープールをAWSクレデンシャルのIDプロバイダーとして使用できます。
他のAWSサービスと同様に、コストがかかります。 Cognitoの料金は、月間アクティブユーザー(MAU)に基づいています。 ほとんどの開発者にとって素晴らしいニュースは、カスタムユーザープールを使用する場合、50,000MAUを上限とする無期限の無料利用枠があることです。 大規模なアプリケーションを使用している場合、これにより、多数のユーザーがユーザー管理の新しいアプローチを試験的に使用できるようになります。 ただし、5万人を超えることのない経験をお持ちの方も多いのではないでしょうか。 この場合、コアユーザー管理はほとんど無料になります。 これに対する唯一の例外は、Lambda、SNS、S3など、ユーザー管理プロセスの一部として利用する他のAWSサービスです。
ユーザープールの作成
ユーザープールをモバイルアプリケーションに統合する最初のステップは、Cognitoユーザープールを作成することです。 これにより、サンプルアプリケーションにプラグインするために必要な構成値が得られます。 新しいユーザープールを作成するには、AmazonのCognitoコンソールで提供されるウィザードを実行します。
ユーザープールを作成するプロセスを見ていきましょう。 これは時間のかかるプロセスであることを警告する必要があります。 多くの点で、これは柔軟性のある領域を示しているので良いことです。 しかし、あなたは一杯のコーヒーを手に入れて、これのためにバックルを締めたいでしょう。
1.名前
ユーザープールを作成する最初のステップでは、ユーザープールの名前を設定し、ユーザープールを作成するためのアプローチを選択します。 デフォルトを確認するか、設定を「ステップスルー」することができます。 ユーザープールがどのように構成されているかについて十分な知識が必要なため、[設定のステップスルー]オプションを選択します。
2.属性
属性の構成には少し考える必要があります。 ユーザープールごとに、システムに保存される属性と必要な属性を決定する必要があります。 システムは必要な値を適用するため、将来的にこれを変更することはできません。 ここでの最善のアプローチは、必要に応じて、ここで本当に重要な値のみをマークすることです。 さらに、ユーザーが自分の電子メールアドレスでログインできるようにする場合は、必ずそのアドレスをエイリアスとしてマークしてください。
カスタム値を含める場合は、ここでも行う必要があります。 各カスタム値には、タイプ、オプションの検証ルール、および可変(変更可能)または非可変(変更不可)のオプションがあります。 25個のカスタム属性のハード制限があります。
最後に、ここでユーザー名について指摘する必要があります。 各ユーザーのユーザー名の値は不変(変更不可)です。 これは、ほとんどの場合、この値を自動的に生成することが理にかなっていることを意味します。 これが、「優先ユーザー名」の値が存在する理由です。 ユーザーが編集できるユーザー名値を使用できるようにする場合は、「優先ユーザー名」属性をエイリアスとしてマークするだけです。 ユーザーが自分のメールアドレスでログインするだけの場合は、必ず「email」属性を必須とエイリアスの両方としてマークしてください。
デモアプリケーションでは、「メール」、「名」、「家系の名前」を必須にすることにしました。
3.ポリシー
属性を構成した後、アカウントのポリシーを構成できるようになります。 構成する最初のポリシーはパスワードポリシーです。 このポリシーでは、長さと、数字、特殊文字、大文字、小文字のいずれが必要かを構成できます。 このポリシーは、ユーザーが入力するパスワードと、管理者がユーザーに割り当てるパスワードの両方に適用されます。
次のポリシーは、ユーザーのサインアップに関連しています。 パブリックアプリケーションの場合、ユーザーが自分でサインアップできるようにする必要があります。 ただし、アプリケーションの種類によっては、サインアップを制限し、システムを招待制にすることをお勧めします。 さらに、これらの招待状が使用されていない場合に期限切れになるまでの時間を構成する必要があります。
デモアプリケーションでは、ユーザーが自分でサインアップできないようにすることを除いて、デフォルト値のみを使用することを選択しました。 これらの値を設定したら、検証に進むことができます。
4.検証
検証ステップでは、多要素認証、および電子メールと電話による検証を設定できます。 この機能はコンソールで比較的簡単に設定できますが、電話番号を確認するか、多要素認証を使用する場合は、AWSSNSの費用の増加をリクエストする必要があることに注意してください。
デモアプリケーションでは、デフォルト値のみを使用することを選択しました。
5.メッセージのカスタマイズ
この手順では、ユーザープールが送信する電子メールメッセージとSMSメッセージ、および「差出人」と「返信先」の電子メールアドレスをカスタマイズできます。 デモアプリケーションの目的のために、ここではデフォルト値のままにして続行します。
6.タグ
AWSを初めて使用する場合は、タグを指定する必要がない場合があります。 ただし、組織でAWSを定期的に使用している場合、タグは支出を分析し、IAMでアクセス許可を割り当てる方法を提供します。 たとえば、一部の組織では、環境(開発、ステージング、本番)ごとおよびプロジェクトごとにタグを指定します。
このステップで何を入力しても、デモアプリケーションには影響しません。
7.デバイス
次のステップでは、ユーザープールがユーザーのデバイスを記憶するかどうかを定義できます。 これは、特定のアカウントがログインしているデバイスを確認できる追加のセキュリティ手順です。 多要素認証(MFA)を利用している場合、これには追加の価値があります。 デバイスが記憶されている場合は、ログインするたびにMFAトークンを要求しないように選択できます。
デモアプリケーションの目的で、値を「常に」に設定することを選択しました。
8.アプリクライアント
ユーザープールを使用するアプリケーション(iOSアプリケーション、Webアプリケーション、Androidアプリケーションなど)ごとに、アプリを作成する必要があります。 ただし、ユーザープールの作成後に戻ってこれらを作成できるため、まだこれらすべてを追加する必要はありません。
各アプリケーションには、構成可能ないくつかの値があります。 このデモアプリケーションでは、アプリに名前を付けてから、デフォルト値のままにします。 次に、各アプリが読み取りおよび書き込みできるユーザー属性を構成できます。
メールアドレス、家系の名前、名前がすべてアプリケーションで読み取りおよび書き込み可能である限り、この手順で任意の値を設定できます。 続行する前に、必ず「アプリクライアントの作成」オプションをクリックしてください。
9.トリガー
トリガーを使用すると、Lambda関数を使用して、ユーザーのライフサイクルプロセスを完全にカスタマイズできます。 たとえば、会社のドメインのメールアドレスを持つユーザーのみがサインアップできるようにする場合は、「プレサインアップ」トリガーにLambda関数を追加して、この検証を実行し、次のようなサインアップリクエストを拒否できます。合格しません。
デモアプリケーションでは、トリガーを追加しません。
10.レビュー
これは長くて骨の折れるプロセスのように思えたかもしれません。 ただし、ユーザープールを作成する各ステップには、ソリューションがより多くのユースケースに適合することを可能にする柔軟性があることに注意してください。 そして今、あなたが聞くのを待っていたニュースのために:これは最後のステップです。
設定を確認して、デモアプリケーション用に正しく構成されていることを確認してください。 この画面から、前の設定に戻って編集することができます。 ユーザープールが作成されると、一部の構成値(必須属性など)は変更できません。
新しいユーザープールを作成したら、AWS SDK foriOSを使用してそれらをサンプルiOSアプリケーションに統合することができます。
ユーザープール用のiOSアプリケーションのセットアップ
Cognitoと統合して、ユーザーがログイン、ログアウト、姓名を入力し、パスワードを設定できるようにするサンプルiOSアプリケーションを作成しました。 この最初のデモでは、ユーザーのサインアップは含まれていません。そのため、Cognitoのコンソールを使用して、テスト用の新しいユーザーを追加しました。
このアプリケーションのコードは、私のGitHubリポジトリにあります。
依存関係の構成
このアプリケーションは、依存関係を管理するためにCocoaPodsを使用します。 この時点での依存関係は、Cognitoユーザープールに関連するAWS iOSSDKの特定の部分のみです。
(CocoaPodsの完全な説明はこの記事の範囲を超えていますが、この概念が初めての場合は、CocoaPodsのWebサイトのリソースが起動して実行するのに役立ちます。)
このアプリケーションのPodfileの内容は、以下のとおりです。
source 'https://github.com/CocoaPods/Specs.git' platform :ios, '10.0' use_frameworks! target 'CognitoApplication' do pod 'AWSCore', '~> 2.5.5' pod 'AWSCognitoIdentityProvider', '~> 2.5.5' end
CocoaPodsがマシンにインストールされていると仮定すると、 pod install
を実行するだけで、必要な依存関係がインストールされます。
ユーザープールの構成
次のステップは、ユーザープールとクライアントアプリケーションの値を含めることです。 デモアプリケーションは、この情報を取得するファイルCognitoApplication/CognitoConfig.plist
を使用するように構成されています。 4つの値を定義する必要があります。
-
region
(文字列)
これは、ユーザープールを作成したリージョンです。 これは、us-east-1
やap-southeast-1
などの標準のリージョン識別子である必要があります。 -
poolId
(文字列)
これは、作成したユーザープールのIDです。 -
clientId
(文字列)
これは、ユーザープールに接続したアプリの一部として構成されたclientId
です。 -
clientSecret
(文字列)
これは、ユーザープールに接続したアプリの一部として構成されているclientSecret
です。
そのファイルと適切な値を設定すると、デモアプリケーションを起動できます。 起動中に例外が発生した場合は、以下に示す4つの値のそれぞれを含め、ファイルが正しいディレクトリに配置されていることを確認してください。
アプリデリゲートの統合
Amazon Cognitoとの統合の中核は、アプリケーションのAppDelegate
内で発生します。 最初のステップは、ロギングを設定し、ユーザープールに接続していることを確認することです。 そのプロセスの一環として、 AppDelegate
をユーザープールのデリゲートとして割り当てます。 この基本的な例では、このロジックをAppDelegate
内に保持できます。 大規模なプロジェクトの場合、これを他の場所で処理するのが理にかなっている場合があります。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // set up logging for AWS and Cognito AWSDDLog.sharedInstance.logLevel = .verbose AWSDDLog.add(AWSDDTTYLogger.sharedInstance) // set up Cognito config self.cognitoConfig = CognitoConfig() // set up Cognito setupCognitoUserPool() return true } func setupCognitoUserPool() { // we pull the needed values from the CognitoConfig object // this just pulls the values in from the plist let clientId:String = self.cognitoConfig!.getClientId() let poolId:String = self.cognitoConfig!.getPoolId() let clientSecret:String = self.cognitoConfig!.getClientSecret() let region:AWSRegionType = self.cognitoConfig!.getRegion() // we need to let Cognito know which region we plan to connect to let serviceConfiguration:AWSServiceConfiguration = AWSServiceConfiguration(region: region, credentialsProvider: nil) // we need to pass it the clientId and clientSecret from the app and the poolId for the user pool let cognitoConfiguration:AWSCognitoIdentityUserPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: clientId, clientSecret: clientSecret, poolId: poolId) AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: cognitoConfiguration, forKey: userPoolID) let pool:AWSCognitoIdentityUserPool = AppDelegate.defaultUserPool() // we need to set the AppDelegate as the user pool's delegate, which will get called when events occur pool.delegate = self }
この構成を行った後、ユーザープールのデリゲートメソッドを構成する必要があります。 実装しているプロトコルはAWSCognitoIdentityInteractiveAuthenticationDelegate
です。 このデリゲートは、ユーザーがログインしたり、パスワードをリセットしたり、多要素認証コードを提供したりする必要があるとき、またはデバイスを記憶するかどうかについてユーザーに問い合わせる必要があるときに呼び出されます。 この例では、 startNewPasswordRequired
startPasswordAuthentication
のみを実装する必要があります。
extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate { // This method is called when we need to log into the application. // It will grab the view controller from the storyboard and present it. func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication { if(self.navigationController == nil) { self.navigationController = self.window?.rootViewController as? UINavigationController } if(self.loginViewController == nil) { self.loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? LoginViewController } DispatchQueue.main.async { if(self.loginViewController!.isViewLoaded || self.loginViewController!.view.window == nil) { self.navigationController?.present(self.loginViewController!, animated: true, completion: nil) } } return self.loginViewController! } // This method is called when we need to reset a password. // It will grab the view controller from the storyboard and present it. func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired { if (self.resetPasswordViewController == nil) { self.resetPasswordViewController = self.storyboard?.instantiateViewController(withIdentifier: "ResetPasswordController") as? ResetPasswordViewController } DispatchQueue.main.async { if(self.resetPasswordViewController!.isViewLoaded || self.resetPasswordViewController!.view.window == nil) { self.navigationController?.present(self.resetPasswordViewController!, animated: true, completion: nil) } } return self.resetPasswordViewController! } }
注意すべき重要な点の1つは、これらのメソッドは両方とも、特定のプロトコルを実装するViewControllerを返すことです。 たとえば、 LoginViewController
はAWSCognitoIdentityPasswordAuthentication
を実装します。これには、ユーザーがログインプロセスを完了できるようにするために必要なパラメーターを使用して呼び出される単一のメソッドがあります。
認証フロー
これらすべての要素がデモアプリケーションに配置されているので、ログインプロセスが最初から最後まで機能することを確認できます。 アプリケーションのメインビューには、ユーザー名とユーザーの名前と名前が表示されます。 これを実現するには、次の手順を実行します。
-
AppViewController
では、viewDidLoad
メソッドでfetchUserAttributes
メソッドを呼び出します。 ユーザーがログインしていない場合、これによりログインプロセスがトリガーされます。 -
AppDelegate
のstartPasswordAuthentication
メソッドがトリガーされます。 このメソッドはLoginViewController
をロードし、それを提示します。 - LoginViewControllerの
getDetails
メソッドは、LoginViewController
によって呼び出されます。 これには、AWSTaskCompletionSource
のインスタンスであるオブジェクトが含まれます。これを使用して、ユーザーがログインを試行できるようにすることができます。 - ユーザーが「ログイン」ボタンを押すと、ログイン資格情報がそのオブジェクトに渡されます。 これにより、
didCompleteStepWithError
メソッドが呼び出され、それに応じて結果を処理できます。 エラーがない場合は、ViewControllerを閉じることができます。 - コンソールでユーザーを作成した場合は、ここで処理する別の手順があります。 ユーザーに一時的なパスワードを与えたため、より永続的なパスワードを設定する必要があります。 また、名前と家系の名前を必須パラメータとして設定しているため、ユーザーもそれらを入力できるようにする必要があります。 AWS SDKはこれを検出し、
AppDelegate
のstartNewPasswordRequired
メソッドを呼び出します。 これにより、ResetPasswordViewController
が表示され、AWSTaskCompletionSource
のインスタンスが設定されます。 -
ResetPasswordViewController
は、LoginViewController
とほぼ同じように機能します。 ユーザーに正しい値を尋ねてから、それらの値を送信するだけです。 このプロセスが正常に完了すると、ViewControllerを閉じます。 - ログインプロセス全体が完了すると、SDKはCognitoから返されたトークンを安全に保存します。 次に、最終的にユーザーの詳細を取得し、それらを使用して、
AppViewController
にユーザーのユーザー名、名前、および家族名を入力できます。
結論
ユーザープールのセットアッププロセスにはいくつかのステップがある場合がありますが、それらのステップは簡単にナビゲートできます。 さらに、可能な構成の量は、それがユースケースの大部分をサポートできるという自信を与えるはずです。 Universal Mindでの私の日々の仕事では、Cognitoがユーザー管理に提供する機能を活用するために、既存のアプリケーションを移行しているいくつかのクライアントと協力してきました。
ユーザー管理システムを定期的に実装する必要があるかどうかに関係なく、これはすべてのモバイルおよびWeb開発者がツールボックスに含める必要のあるツールです。 このシリーズの次の記事では、より多くの一般的なユーザー管理のユースケースを実装する、よりフル機能のデモアプリケーションを実装することにより、Cognitoの機能についてもう少し詳しく説明します。
少し練習すれば、これらすべてのユーザー管理のユースケースを1日以内に満たす新しいアプリケーションをセットアップすることで、すべての友達に感動を与えることができます。 それは一日の仕事にはかなり良いです。
リンクとリソース
- アマゾンコグニート
- 「開発者向けリソース」、Amazon Cognito
- AWSモバイルSDK
- 「SwiftのCocoaPodsチュートリアル:はじめに」、Joshua Greene、raywenderlich.com