CognitoのWebサイトログインをES6で書く


こういう人向けの記事

  • Amazon Cognitoを使いたい
  • Webサイトにログイン機能を実装したい
  • ES2015(ES6)で書きたい
  • WebpackとかBabelとか使いたい
  • ReactとかAngularとか使いたい

Cognitoの素晴らしい点

  • 機能がめちゃくちゃ充実(ユーザー認証まわりの機能ほぼ揃ってる)
  • 開発コスト大幅削減(自分で作ろうとしたら数週間〜数ヶ月かかる)
  • 運用コストゼロ(自動スケール、障害対応不要)
  • 他社より安い(5万MAUまで無料、以降も比較的安い)
  • AWSコンソール上でユーザー情報一覧を閲覧できる
  • 何もしなくても日々進化してくれる

Cognitoの残念な点

  • CognitoのJavaScript対応はまだまだ発展途上
  • 不条理な仕様、ハマりどころが多々あることを覚悟する
  • ES6、Promise等々を使うなら公式READMEに書かれてるコードはそのまま使えないので注意

日々進化してるのでそのうち解消されるはず

事前準備

以下作業が必要。他のQiitaやブログで紹介されているので割愛。

  • AWS CLIをインストール
  • AWSコンソールでCognitoのアイデンティティプール(認証情報が保存されるDB的な)を作成
  • AWSコンソールでCognitoのユーザープール(ユーザー情報が保存されるDB的な)を作成
  • そのユーザープールにアプリクライアントを追加(※注:Webブラウザで使用する場合には クライアントシークレットを生成する のチェックを外す)

AWS CLIでユーザーを作る

Web画面上でユーザーを作ると別途JS側からパスワード再設定しなければならないので、CLIで作る。

aws cognito-idp sign-up --client-id <アプリクライアントID> --username <ユーザー名(メアド認証の場合はメアド)> --password <パスワード> --user-attributes Name=email,Value=<メアド>
aws cognito-idp admin-confirm-sign-up --user-pool-id <ユーザープールID> --username <ユーザー名(メアド認証の場合はメアド)>

必要なパッケージをインストール

AWS SDKとAWS Cognito SDKはともに amazon-cognito-identity-js をnpm installするだけで内包される。
amazon-cognito-identity-js はAWS公式JavaScriptライブラリ。
Promise.promisify() を使わない方は bluebird は入れなくてもOK。

npm i -S bluebird
npm i -S amazon-cognito-identity-js
npm i -D webpack json-loader

ログイン機能を実装

以下コードはサーバーサイドではなくフロントエンドです。
View(HTML/CSS)のコードは割愛しますが、ReactでもAngularでも何でもOK。

UserUtil.js
import Promise from 'bluebird'
import AWS from 'aws-sdk'
import {
  CognitoUserPool,
  CognitoUserAttribute,
  CognitoUser,
  AuthenticationDetails,
} from 'amazon-cognito-identity-js'

const REGION = '<リージョン>'
const IDENTITY_POOL_ID = '<アイデンティティプールID>'
const USER_POOL_ID = '<ユーザープールID>'
const APP_CLIENT_ID = '<アプリクライアントID>'
const userPool = new CognitoUserPool({
  UserPoolId: USER_POOL_ID,
  ClientId: APP_CLIENT_ID,
})
export function signIn(Username, Password) {
  const authenticationDetails = new AuthenticationDetails({Username, Password})
  const cognitoUser = new CognitoUser({
    Username,
    Pool: userPool
  })
  return new Promise((resolve, reject)=>{
    cognitoUser.authenticateUser(authenticationDetails, {
      onSuccess: (result)=>{
        resolve(getAccessToken(result))
      },
      onFailure: (err)=>{
        reject(err)
      },
      newPasswordRequired: ()=>{
        reject('Change your password')
      }
    })
  })
}

export function getSession() {
  const cognitoUser = userPool.getCurrentUser()
  if ( !cognitoUser ) return Promise.reject('Current user not found')
  return Promise.promisify(::cognitoUser.getSession)()
    .then((result)=>{
      if ( !result ) return Promise.reject('Session not found')
      return getAccessToken(result)
    })
}

export function getAccessToken(result) {
  return result.getAccessToken().getJwtToken()
}
index.js
import * as UserUtil from './UserUtil'

// ログイン
export function signIn() {
  const Username = '<ブラウザで入力されたユーザー名>'
  const Password = '<ブラウザで入力されたパスワード>'
  UserUtil
    .signIn(Username, Password)
    .then((accessToken)=>{
      // ログイン成功時の処理
    })
    .catch((err)=>{
      // ログイン失敗時の処理
    })
}

// ログイン後のセッション取得
export function getSession() {
  UserUtil
    .getSession()
    .then((accessToken)=>{
      // セッション取得成功時の処理
    })
    .catch((err)=>{
      // セッション取得失敗時の処理
    })
}

参考にさせていただいたページ

http://qiita.com/Yuki_BB3/items/ee8330830951acd907de
https://github.com/aws/amazon-cognito-identity-js