Movatterモバイル変換


[0]ホーム

URL:


キリウ君が読まないノート キリウ君が読まないノート

こうこく
作 ▸
改 ▸

AWS Cognito UserPoolをサーバーサイドで使うサンプル (Node.js)

Cognitoが全然分からなくて、クライアント側のJavaScriptで使う記事ばかり読んでしまっていた。

aws-amplify とかamazon-cognito-identity-js でめちゃくちゃ悩んだのに、サーバー側なら普通にaws-sdk を使えばよかったのだった。

Node.js v10.16.3aws-sdk 2.554.0
もくじ

前提: Cognitoユーザープールの設定

この記事では、以下の通りに作成したユーザープールとアプリクライアントを使います。

  • サインインはユーザー名で行う
  • 必須の標準属性は無し (メアドも無し)
  • ユーザーに自己サインアップを許可しない
  • 一時パスワードの有効期限は1日 (サーバー側で即座に更新するので何日でもいい)
  • 属性は検証しない (メアドも電話番号も無いので)
  • アプリクライアントの『クライアントシークレットを生成』ON(プール作成時しか設定できないので注意)
  • アプリクライアントの『サーバーベースの認証でサインインAPIを有効にする』ON

ユーザーに自己サインアップを許可せず、メアド等の検証も行わないので、ケースとしては完全に裏方としてCognitoを使う場合になると思います。ユーザー名とパスワードには、ユーザーが入力した値を使用します。

なお、メアド等の属性を検証するユーザープールだとステータス遷移が異なるっぽいので、この記事の方法でできるかは分かりません。adminConfirmSignUp() を使えば検証もサーバーサイドで済ませられるので、そこらへんを使うのかも。

必要なものインストール

サーバー側のアプリにaws-sdk をインストールします。

npm install --save aws-sdk

ユーザー登録 (サインアップ)

ユーザーに自己サインアップを許可しない場合、ユーザー登録にはadminCreateUser() を使います。

ユーザーを作成した後、即座にadminSetUserPassword() で一時パスワードを変更すれば、すぐにユーザーを使える状態になります。

const AWS = require('aws-sdk');AWS.config.update({region: 'ap-northeast-1',});    const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});(async() => {const userPoolId = 'ap-northeast-1_xxxxxxxxx';const username = 'kiriukun';const password = 'mypassword';try {// ユーザー登録// パスワード未指定の場合は自動でランダムな一時パスワードが設定されるconst user = await cognito.adminCreateUser({UserPoolId: userPoolId,Username: username,}).promise();console.log('登録完了', JSON.stringify(user, null, 4));// 作成したばかりのユーザーはステータス FORCE_CHANGE_PASSWORD なのでパスワード変更// べつに一時パスワードと同じパスワードでもエラーにはならないawait cognito.adminSetUserPassword({UserPoolId: userPoolId,Username: username,Password: password,Permanent: true}).promise();console.log('パスワード変更完了');}catch (err) {console.log(err);if (err.code == 'UsernameExistsException') {// ユーザーがすでに存在する場合} else if (err.code == 'InvalidPasswordException') {// パスワードがポリシーを満たしてない場合} else {// その他のエラー}}})();

adminCreateUser() のレスポンスは以下のような感じでした。

{    "User": {        "Username": "my_username",        "Attributes": [            {                "Name": "sub",                "Value": "aed7ad78-f440-47b3-ad9d-aaaaaaaaaaaa"            }        ],        "UserCreateDate": "2019-10-23T05:40:01.580Z",        "UserLastModifiedDate": "2019-10-23T05:40:01.580Z",        "Enabled": true,        "UserStatus": "FORCE_CHANGE_PASSWORD"    }}

サインイン

サインインにはadminInitiateAuth() を使います。

ここではアプリクライアントシークレットを生成してるので、このメソッドはシークレットハッシュをパラメータに指定して呼び出す必要があります。

const crypto = require('crypto');const AWS = require('aws-sdk');AWS.config.update({region: 'ap-northeast-1',});    const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});(async() => {const userPoolId = 'ap-northeast-1_xxxxxxxxx';const clientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';const clientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';const username = 'kiriukun';const password = 'mypassword';try {// シークレットハッシュ計算const secretHash = crypto.createHmac('sha256', clientSecret).update(username + clientId).digest('base64');// サインインconst user = await cognito.adminInitiateAuth({UserPoolId: userPoolId,ClientId: clientId,AuthFlow: 'ADMIN_NO_SRP_AUTH',AuthParameters: {USERNAME: username,PASSWORD: password,SECRET_HASH: secretHash}}).promise();console.log('サインイン完了', JSON.stringify(user, null, 4));}catch (err) {console.log(err);if (err.code == 'UserNotFoundException') {// ユーザーが存在しない場合} else if (err.code == 'NotAuthorizedException') {// パスワードが間違ってる場合} else {// その他のエラー}}})();

この時のadminInitiateAuth() のレスポンスは以下のような感じでした。

{    "ChallengeParameters": {},    "AuthenticationResult": {        "AccessToken": "eyJraWQiOiI3eWZRR1wvZnVTQ1F4UHNGK090R1RPUnQxWUY5Tk(省略)",        "ExpiresIn": 3600,        "TokenType": "Bearer",        "RefreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiU(省略)",        "IdToken": "eyJraWQiOiJ2bURHOWVpb1dHOHFZcGs1bEFFbTZmZjRNOGYya0R3Tk(省略)"    }}

トークンのリフレッシュ

IDトークン・アクセストークンのリフレッシュは、サインインと同様にadminInitiateAuth() を使います。

必要なのはリフレッシュトークンだけで、ユーザー名とパスワードは要りません。

const crypto = require('crypto');const AWS = require('aws-sdk');AWS.config.update({region: 'ap-northeast-1',});    const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});(async() => {const userPoolId = 'ap-northeast-1_xxxxxxxxx';const clientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';const clientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';const refreshToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(省略)';try {// トークンリフレッシュconst res = await cognito.adminInitiateAuth({UserPoolId: userPoolId,ClientId: clientId,AuthFlow: 'REFRESH_TOKEN_AUTH',AuthParameters: {REFRESH_TOKEN: refreshToken,SECRET_HASH: secretHash}}).promise();console.log('トークンリフレッシュ完了', JSON.stringify(res, null, 4));}catch (err) {console.log(err);if (err.code == 'UserNotFoundException') {// ユーザーが存在しない場合} else if (err.code == 'NotAuthorizedException') {// パスワードが間違ってる、またはリフレッシュトークンの期限が切れてる場合} else {// その他のエラー}}})();

この時のadminInitiateAuth() のレスポンスは以下のような感じでした。サインインの時と異なりRefreshToken が入ってないです。

{    "ChallengeParameters": {},    "AuthenticationResult": {        "AccessToken": "eyJraWQiOiI3eWZRR1wvZnVTQ1F4UHNGK090R1RPUnQxWUY5Tk(省略)",        "ExpiresIn": 3600,        "TokenType": "Bearer",        "IdToken": "eyJraWQiOiJ2bURHOWVpb1dHOHFZcGs1bEFFbTZmZjRNOGYya0R3Tk(省略)"    }}

サインアウト

サインアウトにはadminUserGlobalSignOut() を使います。

const AWS = require('aws-sdk');AWS.config.update({region: 'ap-northeast-1',});    const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});(async() => {const userPoolId = 'ap-northeast-1_xxxxxxxxx';const username = 'kiriukun';try {// サインアウトawait cognito.adminUserGlobalSignOut({UserPoolId: userPoolId,Username: username,}).promise();console.log('サインアウト完了');}catch (err) {console.log(err);}})();

注意点ですが、サインアウトで無効になるのはアクセストークンとリフレッシュトークンだけです。IDトークン (API Gatewayでオーソライザーに使えるやつ) は無効になりません。 試しにサインアウト後にIDトークンでAPI Gatewayを叩いてみれば分かります。

なんでだろと一瞬思いましたが、そもそもIDトークン自体が有効期限の情報を含んだJSON Web Tokenなので、考えてみればそういうものかもしれません。IDトークンの検証なら、API Gatewayのオーソライザーに限らずユーザーが手動でもできるわけですから。

なのでIDトークンを即座に無効にしたい場合、アプリ側でユーザーがログアウト済みかどうかを別途管理する必要があります。

ユーザー取得

ユーザーの取得にはadminGetUser() を使います。

const AWS = require('aws-sdk');AWS.config.update({region: 'ap-northeast-1',});    const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});(async() => {const userPoolId = 'ap-northeast-1_xxxxxxxxx';const username = 'kiriukun';try {const user = await cognito.adminGetUser({UserPoolId: userPoolId,Username: username,}).promise();console.log('取得完了', JSON.stringify(user, null, 4));}catch (err) {if (err.code == 'UserNotFoundException') {// ユーザーが存在しない場合} else {// その他のエラー}}})();

レスポンスの例は以下です。ユーザー登録時の戻りと微妙に違うので注意です (AttributesUserAttributes になってる)。

{    "Username": "my_username",    "UserAttributes": [        {            "Name": "sub",            "Value": "825d8d71-f582-4d9a-a35f-aaaaaaaaaaaa"        }    ],    "UserCreateDate": "2019-11-22T16:11:17.490Z",    "UserLastModifiedDate": "2019-11-22T16:11:17.705Z",    "Enabled": true,    "UserStatus": "CONFIRMED"}

ユーザー削除

ユーザーの削除にはadminDeleteUser() を使います。

const AWS = require('aws-sdk');AWS.config.update({region: 'ap-northeast-1',});    const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});(async() => {const userPoolId = 'ap-northeast-1_xxxxxxxxx';const username = 'kiriukun';try {// ユーザー削除await cognito.adminDeleteUser({UserPoolId: userPoolId,Username: username,}).promise();console.log('削除完了');}catch (err) {console.log(err);if (err.code == 'UserNotFoundException') {// ユーザーが存在しない場合} else {// その他のエラー}}})();

以上

他にも、ここに書いてないメソッド使ったら都度追記します。

この記事に何かあればこちらまで (非公開)

脳みそパープリン

--recent

--category

こうこく

--tag


[8]ページ先頭

©2009-2025 Movatter.jp