LoginSignup
65
67

More than 3 years have passed since last update.

ReactとAmazon Cognitoでユーザーを管理する

Last updated at Posted at 2019-09-08

Amazon Cognitoを使ったのでその覚書ついでに:pencil2:
細かい説明とかはないです:relaxed:

ユーザー管理機能を設定する前に

Amazon Cognitoでユーザーを管理できるようにしますので、とりあえず、Reactのアプリを作成します。

$ npx create-react-app react-aws-cognito --typescript

アプリが作成されたら、srcディレクトリの中に、Cognitoを利用するためのコンフィグファイルを作成します。

awsConfiguration.ts
const awsConfiguration = {
  region: 'ap-northeast-1',
  IdentityPoolId: 'ap-northeast-1:**********',
  UserPoolId: 'ap-northeast-1_**********',
  ClientId: '**********',
}

export default awsConfiguration

**********となっている部分はこれから説明します。

以下もインストールしてください。

$ yarn add amazon-cognito-identity-js

Cognitoをjsで楽にいじられるモジュールです。
https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js

ユーザープールを作成する

Screenshot 0031-09-07 at 5.08.56 PM.png

Cognitoに入ると上のような画面になるので、まずは「ユーザープールの管理」に入ります。
「ユーザープールを作成する」ボタンがあると思いますので、そこから作成画面へ入っていきます。

Screenshot 0031-09-07 at 5.11.02 PM.png

こんな具合で設定画面が出てくるので、順に設定します。

ユーザープールはそのままの意味で、ユーザーのみずたまりです。
ユーザーの倉庫のようなものです。

今回はデフォルトで作ります。

Screenshot 0031-09-07 at 6.15.43 PM.png

デフォルトの場合こんな設定になります。
必須の属性はemailとなり、これはサインアップ時に必須の項目となります(細かく設定するとこの辺りも変えられます)。

パスワードの最小長や多要素認証、また、登録時に送信するEメールやSMSの内容を編集したりもできます。
lambdaと連携させることも可能です:gear:

次にIDプールを作ります。

IDプールを作成する

フェデレーテッドアイデンティティから「新しいIDプールの作成」をクリックすると、以下のような画面が表示されます。

Screenshot 0031-09-07 at 6.19.55 PM.png

IDプール名を入力し、認証プロバイダーのところでCognitoタブを選択して必要な情報を入れます。

Screenshot 0031-09-07 at 6.21.10 PM.png

ユーザープールID

 作成したユーザープールの「全般設定」(ユーザープールから作成したのを選択すると表示されます)にプールIDがあるのでこれを入力してください、

IDプール

 作成したユーザープールの「アプリクライアント」からアプリクライアントを作成すると表示される、アプリクライアントIDを設定してください。

Screenshot 0031-09-07 at 11.31.07 PM.png

今回はJavascriptSDKを利用するのですが、その場合は「クライアントシークレットを生成」のチェックを外してください。JSのSDKでの対応ができていないようですね。。。
https://qiita.com/noobar/items/6615501b035e47792227

入力したら、「プールの作成」を押し、IAMロールを作成します。
ここのロールは、Authenticated UserとUnauthenticated Userそれぞれのアプリケーションの利用可能範囲を定めたものを表します。

Authenticated Userには、アプリケーションに登録したユーザーとして実行可能な範囲のアクションと、そのアクションに伴うAWSリソースへのアクセスを認めるべきです。
反対に、Unauthenticated UserにはAuthenticated Userと比較してより厳しい範囲でのアプリケーションの利用を認めるようにした方が無難です。

Screenshot 0031-09-07 at 6.29.39 PM.png

IDプールに紐づくIAMロールを作成するので、「許可」を押して次へ行きましょう。
作成が成功したら、「Amazon Cognitoでの作業開始」が出てくるので、ドロップダウンからjavascriptを選びます。
すると、js用のコードが出てきて、その中に、IdentityPoolIdが表記されているので、それをコピーしておきます。

ここまでで今一度、
IdentityPoolId、UserPoolId、 ClientIdを整理しておきましょう(冒頭のコンフィグファイルの項目です)。

IdentityPoolId ・・・ IDプール作った時に出てきたやつ。
UserPoolId ・・・ ユーザープールの「全般設定」に表示されるプールID。
ClientId ・・・ ユーザープールの「アプリクライアント」に表示されるアプリクライアントID。

上記は全てコピーして、冒頭で書いたawsConfiguration.tsに書き込みます。
srcディレクトリ内はこんな感じになります。:point_down_tone2:

.
├── auth(SignUp.tsx, Verification.tsx, SignIn.tsx, SignOut.tsx)
├── App.css
├── App.test.tsx
├── App.tsx
├── awsConfiguration.ts
├── index.css
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
└── serviceWorker.ts

サインアップ

tsxで書いたサインアップのコンポーネントです。
肝はSignUp関数です。

必須の属性として設定されていたemailとパスワードを入力すると、入力したemailあてにメールが届きます(もちろん電話番号で設定するとSMSでも届けられるように設定できます)。

Screenshot 0031-09-08 at 11.53.07 AM.png

このメールの文言もAWSコンソールで設定できます。
コードの有効期限はデフォルトでは7日になっているようです。

ちなみに、パスワードのポリシーはデフォルトで以下のようになっているので、注意してください。

Screenshot 0031-09-08 at 12.48.03 PM.png

SignUp.tsx
import React from 'react'
import '../App.css'

import {
  CognitoUserPool,
  CognitoUserAttribute
} from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'

const userPool = new CognitoUserPool({
  UserPoolId: awsConfiguration.UserPoolId,
  ClientId: awsConfiguration.ClientId,
})

const SignUp: React.FC = () => {
  const [email, setEmail] = React.useState<string>('')
  const [password, setPassword] = React.useState<string>('')

  const changedEmailHandler = (event: any) => setEmail(event.target.value)
  const changedPasswordHandler = (event: any) => setPassword(event.target.value)
  const signUp = () => {
    const attributeList = [
      new CognitoUserAttribute({
        Name: 'email',
        Value: email
      })
    ]
    userPool.signUp(email, password, attributeList, [], (err, result) => {
      if (err) {
        console.error(err)
        return
      }
      setEmail('')
      setPassword('')
    })
  }

  return (
    <div className="SignUp">
      <h1 style={{ textAlign: 'left' }}>SignUp</h1>
      <input type="text" placeholder="email" onChange={changedEmailHandler} />
      <input type="text" placeholder="password" onChange={changedPasswordHandler} />
      <button onClick={signUp}>SignUp</button>
    </div>
  )
}

export default SignUp

検証

サインアップで受け取った検証コードを使ってサインアップを完了しましょう。

検証コードと必須の属性であるemailを入力する以下のコードを実行すると、アカウントのステータスがUNCONFIRMEDだったのが画像のようにCONFIRMEDに変わります(ユーザープール/全般設定/ユーザーとグループ)。

Screenshot 0031-09-08 at 12.19.17 PM.png

Verification.tsx
import React from 'react'
import '../App.css'

import {
  CognitoUserPool,
  CognitoUser
} from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'

const userPool = new CognitoUserPool({
  UserPoolId: awsConfiguration.UserPoolId,
  ClientId: awsConfiguration.ClientId,
})

const Verification: React.FC = () => {
  const [email, setEmail] = React.useState<string>('')
  const [verificationCode, setVerificationCode] = React.useState<string>('')
  const changedEmailHandler = (event: any) => setEmail(event.target.value)
  const changedVerificationCodeHandler = (event: any) => setVerificationCode(event.target.value)

  const verifyCode = () => {
    const cognitoUser = new CognitoUser({
      Username: email,
      Pool: userPool
    })
    cognitoUser.confirmRegistration(verificationCode, true, (err: any) => {
      if (err) {
        console.log(err)
        return
      }
      console.log('verification succeeded')
      setEmail('')
      setVerificationCode('')
    })
  }
  return (
    <div className="Verification">
      <h1>Authenticate</h1>
      <input type="text" placeholder="verification code" onChange={changedVerificationCodeHandler} />
      <input type="text" placeholder='email' onChange={changedEmailHandler} />
      <button onClick={verifyCode}>Authenticate</button>
    </div>
  )
}

export default Verification

サインイン

それでは検証成功したアカウントでサインインしてみます。
SignIn関数でサインインを行い、認証が成功すればonSuccessを通ってresultを返します。
ここではresultからaccessTokenを取り出してログに出しています。

SignIn.tsx
import React from 'react'
import '../App.css'

import {
  CognitoUserPool,
  CognitoUser,
  AuthenticationDetails
} from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'

const userPool = new CognitoUserPool({
  UserPoolId: awsConfiguration.UserPoolId,
  ClientId: awsConfiguration.ClientId,
})

const SignIn: React.FC = () => {
  const [email, setEmail] = React.useState<string>('')
  const [password, setPassword] = React.useState<string>('')
  const changedEmailHaldler = (e: any) => setEmail(e.target.value)
  const changedPasswordHandler = (e: any) => setPassword(e.target.value)

  const signIn = () => {
    const authenticationDetails = new AuthenticationDetails({
      Username : email,
      Password : password
    })
    const cognitoUser = new CognitoUser({
      Username: email,
      Pool: userPool
    })

    cognitoUser.authenticateUser(authenticationDetails, {
      onSuccess: (result) => {
        console.log('result: ' + result)
        const accessToken = result.getAccessToken().getJwtToken()
        console.log('AccessToken: ' + accessToken)
        setEmail('')
        setPassword('')
      },
      onFailure: (err) => {
        console.error(err)
      }
    })
  }

  return (
    <div className="SignIn">
      <h1>SingIn</h1>
      <input type="text" placeholder='email' onChange={changedEmailHaldler}/>
      <input type="text" placeholder='password' onChange={changedPasswordHandler}/>
      <button onClick={signIn}>Sign In</button>
    </div>
  )
}

export default SignIn

サインイン中のアカウントを検知する場合は、

const cognitoUser = userPool.getCurrentUser()
if (cognitoUser) {
  // sign inしている状態
  console.log('signing in')
} else {
  // sign inしていない状態
  console.log('no signing in')
}

という感じで行い、サインアウト時もこれがtrueの時に処理できるようにします。

サインアウト

サインアウトはこちら。
userPool.getCurrentUser()で現在サインイン中のアカウントを検知し、
サインインされていればcognitoUser.signOut()を、
そうでなければとりあえず念の為localStorageを綺麗にしています(サインインするとlocalStorageにもaccessToken等のデータが入るので)。

SignOut.tsx
import React from 'react'
import '../App.css'

import { CognitoUserPool } from "amazon-cognito-identity-js"
import awsConfiguration from '../awsConfiguration'

const userPool = new CognitoUserPool({
  UserPoolId: awsConfiguration.UserPoolId,
  ClientId: awsConfiguration.ClientId,
})

const SignOut: React.FC = () => {
  const signOut = () => {
    const cognitoUser = userPool.getCurrentUser()
    if (cognitoUser) {
      cognitoUser.signOut()
      localStorage.clear()
      console.log('signed out')
    } else {
      localStorage.clear()
      console.log('no user signing in')
    }
  }

  return (
    <div className="SignOut">
      <h1>SignOut</h1>
      <button onClick={signOut}>Sign Out</button>
    </div>
  )
}

export default SignOut

全部まとめて整理するとこんな感じ?:point_down_tone2:

App.tsx
import React from 'react'
import './App.css'

// components
import SignUp from './auth/SignUp'
import Verification from './auth/Verification'
import SignIn from './auth/SignIn'
import SignOut from './auth/SignOut'

import { CognitoUserPool } from "amazon-cognito-identity-js"
import awsConfiguration from './awsConfiguration'

const userPool = new CognitoUserPool({
  UserPoolId: awsConfiguration.UserPoolId,
  ClientId: awsConfiguration.ClientId,
})

const App: React.FC = () => {

  const authentication = () => {
    const cognitoUser = userPool.getCurrentUser()
    // サインインユーザーがいればアプリのメイン画面へ、
    // いなければサインアップ、検証、サインイン画面を表示する。
    if (cognitoUser) {
      return (
        <div className="authorizedMode">
          <SignOut />
        </div>
      )
    } else {
      return (
        <div className="unauthorizedMode">
          <SignUp />
          <Verification />
          <SignIn />
        </div>
      )
    }
  }

  return (
    <div className="App">
      <header />
      { authentication() }
    </div>
  )
}

export default App

グループ(IAMロール)の設定

Cognitoでは特定のIAMロールを持つグループを作成することができ、
そのグループに属するユーザーはそのIAMロールのポリシーに従ってAWSリソースへのアクセスが制限されます。

Screenshot 0031-09-08 at 1.30.14 PM.png

IAMロールから割り当てたいロールを選択してグループを作成します。
以降、AWSコンソールからグループにユーザーを割り当てることができるようになります。

ですが、これ、毎回手動でAWSコンソールをいじるのも面倒すぎるので、JavascripSDKを利用してやりたいです。
adminAddUserToGroupを利用して実現します。
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminAddUserToGroup-property

import AWS from 'aws-sdk'
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider()
const params = {
  GroupName: 'STRING_VALUE', /* required */
  UserPoolId: 'STRING_VALUE', /* required */
  Username: 'STRING_VALUE' /* required */
}
cognitoidentityserviceprovider.adminAddUserToGroup(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
})

上のように書くみたいです(試してませんが)。
検証が成功した後にこの処理を挟めば良いと思います。

Usernameはおそらくメールアドレスを入力すれば問題ないかと思います。
もしかしたら必須の属性設定によるかもしれませんが。。。

基本的には以上でCognitoを利用した認証機能を実装できるはずです:hugging:

デモ作ってみました。ここで説明したawsConfiguration.tsは入っていません。
https://github.com/yutaro1204/AmazonCognitoWithReact

65
67
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
65
67