PHPで Amazon Cognito を使用した認証機能の導入 その2:一般的な認証

  • HOME
  • SPI ブログ
  • PHPで Amazon Cognito を使用した認証機能の導入 その2:一般的な認証

PHPで Amazon Cognito を使用した認証機能の導入 その2:一般的な認証

ユーザー認証システムとしてAmazon Cognitoを利用する場合に、
JavaScriptやPythonを使わずあえて『PHP』でやりたいという方のための覚書になります。

※本記事は2023年4月のAWSコンソールを元に書かれています。
 それ以降は画面が変更されている可能性があります。

使用ツールは「AWS SDK for PHP – Version 3」
Github

フロントはCakePHP4で作成していますがお好みで。

前回AWSコンソールでCognito側の設定をしたので、今回はPHPを用いた基本的な認証の作成。
前回:その1:AWSの設定

AWS SDK for PHP

PHP用SDKのセットアップ。
一応zipファイル等も提供されていますが、
Githubに書かれている通りcomposerコマンドで入れるのが無難。

ComposerのセットアップについてはGoogle検索先生にお任せします。

composer require aws/aws-sdk-php
又は
php composer.phar require aws/aws-sdk-php

cakephpの場合はcomposer.jsonのあるディレクトリで上記コマンド。

Cognitoを利用する場合の基本設定はGithubに倣うと以下のように

require 'vendor/autoload.php';
//上記は利用する環境によりけり

use Aws\CognitoIdentityProvider\CognitoIdentityProviderClient;
use Aws\Exception\AwsException;

$client = new CognitoIdentityProviderClient([
	'version' => 'latest',
	'region' => 'ap-northeast-1',
	'credentials' => [
		'key' => 'AWS-access-key',
		'secret' => 'AWS-secret-key',
	],
]);

アクセスキーとシークレットキーの作成は
AWSコンソール右上の「ユーザー名」→「セキュリティ認証情報」→「アクセスキーを作成」から。

ユーザーの作成から簡単な認証

ここからの内容は基本的にドキュメントに書かれている内容をそのまま実装すれば大丈夫です。
ただし日本語ドキュメントはないので英語を読むか翻訳しながらやりましょう。
CognitoIdentityProviderClientのドキュメント

例えばユーザーを作る場合は

try {
	$result = $client->AdminCreateUser([
		'DesiredDeliveryMediums' => ['EMAIL',],
		'UserPoolId' => $userPoolId,
		'Username' => 'メールアドレス',
	]);

} catch (AwsException $errs) {

} catch (\Throwable $exep) {

}

今後登場する「$userPoolId」と「$ClientId」は前回の記事でメモした
ユーザープールIDとクライアントIDを使用します。

AwsExceptionは認証に失敗した場合などをエラーとしてcatchしますが、
「認証に必要な情報が不足している場合」のエラーなどはcatchしません。

そういったエラーは「\Throwable」で全部拾えば(一先ずは)楽。

また2023年4月現在、コンソールでエラー文章を設定できないため
エラーをわかりやすくしたい場合は「AwsException」でcatchしたエラーコードを自力で振り分けるしかありません。

例えばエラーコード「InvalidPasswordException」であれば
ユーザープール作成時に設定したパスワードの強度を満たさなかった場合のエラー。

エラーコードは同じでも
エラーテキストは(英語で)「大文字小文字を使え」「字数が足りない」等が返ってきますが
それらを引っ括めた文章にしておけば問題ないかと思われます。

Adminありなしの違い

例えば「AdminUpdateUserAttributes」と「UpdateUserAttributes」があり、
Adminありはユーザー名、Adminなしは発行されたアクセストークンで利用できます。

Adminなしはログインしている自分自身が対象になるため、
他のユーザーに確実に影響を及ぼさないため安心、といった使い方になります。

ただしメールアドレスの有効化など
Adminつきでないと変更できない項目もあるためケースバイケース。

基本的な利用メソッド

ユーザー作成「AdminCreateUser」

$result = $client->AdminCreateUser([
	'DesiredDeliveryMediums' => ['EMAIL',],
	'UserPoolId' => $userPoolId,
	'Username' => 'メールアドレス',
]);

ユーザーの作成に成功するとメールアドレスに仮パスワードが送信されます。
返り値で使用するものはありません。

ログイン「AdminInitiateAuth」

$result = $client->AdminInitiateAuth([
	'UserPoolId' => $userPoolId,
	'AuthFlow' => 'ADMIN_USER_PASSWORD_AUTH',
	'ClientId' => $ClientId,
	'AuthParameters' => [
		'USERNAME' => 'メールアドレス',
		'PASSWORD' => 'パスワード',
	],
]);

本パスワードで認証に成功すると「アクセストークン」と「リフレッシュトークン」が返ります。
今回作成したプログラムではこのトークンをセッションに保持し、
セッションが有効な間はログインしている状態としています。

USERNAMEがメールアドレスなのは、ユーザープールでログインに使用する必須項目をEメールにしているためです。
ユーザー名であればそれ用の文字列、電話番号であれば電話番号となります。
本記事で以降に登場する「Username」はすべてメールアドレスのことです。

仮パスワードで認証に成功すると「$result[“ChallengeName”]」に「NEW_PASSWORD_REQUIRED」が返却されます。
本パスワードを登録する場合は次のメソッドを使用します。

本パスワード登録「adminSetUserPassword」

$result = $client->adminSetUserPassword([
	'Password' => '本パスワード',
	'Permanent' => true,
	'UserPoolId' => $userPoolId,
	'Username' => 'ユーザー名 ※',
]);

なお仮パスワードと本パスワードは同じものが設定できます。

メールアドレス有効化「email_verified」

$result = $client->adminUpdateUserAttributes([
	'UserAttributes' => [
		[
			'Name' => 'email_verified',
			'Value' => 'True',
		]
	],
	'UserPoolId' => $userPoolId,
	'Username' => 'ユーザー名 ※',
]);

一定期間有効化しないと使えなくなる可能性があるため早めに設定してください。
※Bool型のValue値は文字列のTrueで良かったりダメだったりboolのtrueで良かったりダメだったり

ログアウト的な処理「GlobalSignOut,revokeToken」

$client->GlobalSignOut([
	'AccessToken' => 'アクセストークン',
]);
$client->revokeToken([
	'ClientId' => $ClientId,
	'Token' => 'リフレッシュトークン',
]);

Cognitoではトークンを破棄しているだけなので、
フロントでログアウトのように振る舞うにはそれ用の処理が必要です。

GlobalSignOutはドキュメントを読むと
「Amazon Cognito がユーザーに発行したすべての ID、アクセス、および更新トークンを無効にします。」
となっているため、ドキュメント通りであればすべてのトークンは破棄されるはず。

ただリフレッシュトークンは利用できるため、念のため続けてrevokeTokenを使用しています。
こちらもドキュメントでは
「指定されたリフレッシュ トークンと同時に生成されたすべてのアクセス トークンを取り消します。」
となっているため、2度同じことをしている可能性があります。

revokeTokenは念のためです。
ただし順序を逆にするとGlobalSignOutはエラーになります。

ユーザー管理操作

パスワード変更「ChangePassword」

$Client->ChangePassword([
	'AccessToken' => 'アクセストークン',
	'PreviousPassword' => '現在のパスワード',
	'ProposedPassword' => '新しいパスワード',
]);

ログイン中にパスワードを変更したい場合に使用。
「AdminChangePassword」はないため管理者がパスワードを管理することはできません。

パスワード忘れ「ForgotPassword」

$Client->ForgotPassword([
	'ClientId' => $ClientId,
	'Username' => 'ユーザー名 ※',
]);

cognitoに登録されているメールアドレス宛に再設定用のコードを送信します。
※Usernameに電話番号を利用している場合はSMSメッセージが飛ぶかもしれません(未検証)

なお殆ど同じ機能として「AdminResetUserPassword」がありますが、
こちらは使用した次点で現在設定されている本パスワードが使用できなくなります。
※恐らく不正ログインの遮断用

ForgotPasswordは再設定用のコードが送信されても本パスワードでログインできます。

パスワード忘れ時の再設定「ConfirmForgotPassword」

$Client->ConfirmForgotPassword([
	'ClientId' => $ClientId,
	'Username' => 'ユーザー名 ※',
	'ConfirmationCode' => '再設定用コード',
	'Password' => '新しいパスワード',
]);

トークンの確認「GetUser」

$result = $client->GetUser([
	'AccessToken' => 'アクセストークン',
]);

※トークンが有効かの確認自体はGetUser以外でも可能です。

ユーザー削除「DeleteUser」

$result = $client->DeleteUser([
	'AccessToken' => 'アクセストークン',
]);

削除に成功した場合、発行されていたトークンも無効になります。
このため「GlobalSignOut」を続けて使うとエラーとなります。
リフレッシュトークンはやはり利用できます。

次回予告

次回は他要素認証の利用について

次回:その3:SMS認証

最新ブログ一覧