Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
48
Help us understand the problem. What is going on with this article?
@horike37

Amazon Cognito User Poolsをもっと使いこなすためのTips

More than 3 years have passed since last update.

概要

こんにちは!この記事はServerless(2) Advent Calendar 2016の11日目の記事です。

ユーザの認証基盤を提供するサービスとして提供されているAmazon Cognito User Poolsは非常に柔軟にカスタマイズが出来る素晴らしいサーバレスなサービスなのですが、Tipsや細かいカスタマイズ方法をGoogleで検索してもあまり出てきません。

もう少し突っ込んだ使い方を書いた記事が世の中にあれば、
もっとこのサービスを皆が便利に使いこなせるようになるのでは、という思いでこの記事を書くことにしました。

それではよろしくお願いします!

カスタマイズの基礎

僕がNodejsで実装することが多いのでNodeのSDKをベースに説明します。

http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html
UserPoolsとのやり取りは上記のCognitoIdentityServiceProviderというSDKを使います。

大枠2種類のAPIが準備されていて、admin~から始まるメソッドとそうでないメソッドの2種類があります。admin~から始まるメソッドは要は管理者向けに用意されたメソッドです。usernameを指定することで様々なユーザの管理作業が可能になります。

そうでないメソッドはaccesstokenを元にしてユーザ自身が操作を行うためのメソッドです。
UserPoolsはユーザのログイン時にaccesstokenが発行されます。アプリケーション内ではそのtokenを引き回して、ログイン状態を管理したりプロフィールのアップデートなど各種操作を可能とします。

https://github.com/aws/amazon-cognito-identity-js
ブラウザアプリケーション用のSDKも用意されています。これはLambda+API Gatewayで作らないといけない処理をすべてラップしてくれているSDKっす。
もちろん、admin~の操作は許可されていません。ブラウザからユーザに管理作業が可能になってしまうので。

メールアドレスログインを実装する

User Poolsはデフォルトで一意なUserIDをベースにして認証を行う仕組みとなっています。
UserID以外で認証をさせたい場合はエイリアスの機能が提供されています。

スクリーンショット 2016-12-11 22.54.24.png

UserPoolを作る際に最初に必要な属性を設定する画面が有ります。
メールアドレスでログインさせたい場合はemailのAliasにチェックを入れてあげましょう。

こうすることでメールアドレスはそのUserPool内で一意なものとなります。そしてログインやパスワード再発行、アクティベーションなどのAPIはすべてAliasで設定したデータを元に行えるようになります。

これ、一度UserPoolを作ってしまうと後から設定変更は出来ませんので注意しましょう。後からメールアドレスログインを実装したいと思っても後の祭りです。。
最初にUserテーブルの設計は帰れない前提で決めて上げる必要があります。

アクティベーションメールのカスタマイズ

ユーザの登録時やパスワード再発行時にUserpoolから承認用のメールが送信されます。
マネジメントコンソールから文面を編集することは可能なのですが、例えばUserIDをそのメールに記載したりといったことはマネジメントコンソールからは出来ません。

これはCustom messageというTriggerを使うことで可能です。
以下がサンプルのLambdaファンクションです。アクティベーションメールにUserIDを記載する例です。

'use strict';
exports.handler = (event, context, cb) => {
    event.response.emailMessage = `<p>Hello, ${event.userName}<br />Thanks you for your registration</p>`;
    event.response.emailSubject = 'Activation mail';
    cb(null, event);
};

また、eventには以下の様なJSONが格納されています。

{ 
version: '1', 
region: 'us-east-1', 
userPoolId: '<ID>',
userName: '<userid>',
callerContext: { 
  awsSdkVersion: 'aws-sdk-js-2.4.12',
  clientId: '<ID>'
}, 
triggerSource: 'CustomMessage_SignUp',
request: { 
  userAttributes: { 
    email_verified: 'false', 
    'cognito:user_status': 'UNCONFIRMED', 
    email: '<email>' 
  },
  codeParameter: '{####}', 
  usernameParameter: null 
}, 
response: { 
  smsMessage: null, 
  emailMessage: null, 
  emailSubject: null 
}

triggerSourceの値にユーザ登録後のメールなのか、メールアドレス変更時のメールなのか、パスワード再発行時のメールなのかのトリガー元の情報も入っています。

これを元にメールの内容を変更したりと言ったことも可能です。

アクティベーションを行った日付を登録する

ユーザが登録後にアクティベーションを行った日を記載して、その日から~日までは無料でサービスを使えますよ、といった機能が実装できます。

これにはカスタム属性の機能を使います。Attributesの画面からactivationDateというカスタム属性を作ります。
スクリーンショット 2016-12-11 23.18.47.png

さらに作った属性には書き込みと読み込みの権限を設定して上げる必要があります。
要はユーザデータとしてユーザ自身に読み込みや書き込みを許可させるのかという設定です。

Appsの画面から以下のように設定可能です。
スクリーンショット 2016-12-11 23.21.39.png

カスタム属性を設定すれば、ユーザ登録後のアクティベーションが実施されるタイミングで通るトリガーであるPost confirmationでカスタム属性にデータ登録するLambdaを設定してあげます。

以下がそのコードです。

'use strict';
const moment = require('moment');
const aws = require('aws-sdk');
aws.config.update({region:'us-east-1'});

exports.handler = (event, context, cb) => {
    console.log({event:JSON.stringify(event)});

    const trigger = new userPoolsTriggerPostConfirmation();
    trigger.registerActivatedDate(event.userPoolId, event.userName).then(() => {
      cb(null, event);
    }).catch((error) => {
      console.log(error);
      cb('Internal Server error');
    });
};

class userPoolsTriggerPostConfirmation {
  constructor() {
    this.cognito = new aws.CognitoIdentityServiceProvider();
  }

  registerActivatedDate(userPoolId, userName) {
    const params = {
      UserAttributes: [
        {
          Name: 'custom:activatedDate',
          Value: moment().format('YYYY-MM-DDTHH:mm:ssZ')
        },
      ],
      UserPoolId: userPoolId,
      Username: userName
    };

    return this.cognito.adminUpdateUserAttributes(params).promise()
      .then(() => Promise.resolve({}))
      .catch((error) => Promise.reject(error))
  }
}

ユーザに権限を設定する

いわゆるそのサービス内で権限を実装する仕組みです。これも上記と同様にカスタム属性を使って実装しましょう。

以下の通りroleというカスタム属性を作ります。
スクリーンショット 2016-12-11 23.29.23.png

権限はユーザ自身に読み込みは出来ても書き込みが出来ては行けません。書き込みが出来るとユーザ自身で自由に権限が設定できちゃうので。

スクリーンショット 2016-12-11 23.30.31.png

https://github.com/aws/amazon-cognito-identity-js
設定した属性はブラウザアプリケーションでは上記のgetUserAttributesメソッドで

http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#getUser-property
Lambda側で実装する場合は、getUsersを使用しましょう。

roleの値に応じて各権限で可能な処理を分岐させることが出来ます。

まとめ

というわけで、自身の経験を元にAmazon Cognito User PoolsのカスタマイズTipsをお送りました。カスタマイズのポイントはカスタム属性を如何に使いこなすところかなと思います。

それでは、良いUserPoolsライフを!

48
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
horike37
CEO of Serverless Operations, Inc Serverless Framework Core Maintainer
serverless-operations
AWSクラウド技術の豊富な知見を活かし、サーバーレスによる開発や運用の支援、コンサルティングまで行う会社です

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
48
Help us understand the problem. What is going on with this article?