Cognitoユーザー認証をAmplifyとReactを使って実装する。(その3)

Client側のAmplifyをInstallします。環境は前回「Reactを使ってStaticなWeb Hostingをする。」で使用した環境に追加します。
npm install --save aws-amplify aws-amplify-react

Client側のApplicationを作って行きます。ReactのWelcome画面にCognitoユーザー認証を追加します。

初めに読み込まれる、index.jsにAWS Amplifyの設定を入れて置く”services”というfileをimportする行を追加します。
import { configureAmplify } from './services'
そのあとで、Amlifyをconfigureするために、
configureAmplify();
を呼び出します。ReactDom.renderに渡すelementを<App />から<AppWithAuth />に変更し、ユーザー認証を追加した描画処理に変更します。
ReactDOM.render(<AppWithAuth />, document.getElementById('root'));

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import AppWithAuth from './AppWithAuth';
import * as serviceWorker from './serviceWorker';

//Import Amplify Configure function
import { configureAmplify } from './services'

configureAmplify();

ReactDOM.render(<AppWithAuth />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

index.jsからimportされる”services”を準備します。”services”では、Amplifyの設定をしますが、実際の情報は環境変数に設定しておき、起動時に”.env”から読み込むようにしておきます。このようにすることで、環境が変わっても再buildすることなく、環境を変更することができるようになります。

import Amplify from '@aws-amplify/core';

export function configureAmplify() {
  Amplify.configure(
  {
    Auth: {
      region: process.env.REACT_APP_region,
      userPoolId: process.env.REACT_APP_userPoolId,
      userPoolWebClientId: process.env.REACT_APP_userPoolWebClientId,
      identityPoolId: process.env.REACT_APP_identityPoolId,
   }
 } );
}

“.env”で4つの情報を設定し、projectのroot directoryに置きます。
REACT_APP_region=ap-northeast-1
REACT_APP_userPoolId=ap-northeast-1yourPoolID
REACT_APP_userPoolWebClientId=yourWebClientId
REACT_APP_identityPoolId=ap-northeast-1:yourIdentityPoolId
これらの情報は、Cognito User Poolを作成した時に生成されたUser ID、App Client IDと、Cognito Identity Poolを作成した時に生成されたIdentity Pool IDを設定します。これらは、Cognitoに接続するための情報ですが、URLのような物で隠す必要はありません。ボットによる攻撃を避けるためには、Cognito User Poolの作成の際に、Client Deviceの認証を追加するなど検討します。

次に、 ReactDom.renderから最初に描画されるelement、”AppWithAuth.js”を作成します。今回はEメールによる認証を行わないため、Login画面を簡素化しました。単純にusernameとpasswordだけを入力できるようにしています。

import React from 'react';
import { Authenticator, ConfirmSignIn, RequireNewPassword, ConfirmSignUp, VerifyContact, ForgotPassword, TOTPSetup } from 'aws-amplify-react';
import App from "./App";
import MyVerifyContact from "./MyVerifyContact"
import MyConfirmSignUp from "./MyConfirmSignUp"

const signUpConfig = {
  hideAllDefaults: true,
  signUpFields: [
    {
      label: 'Username',
      key: 'username',
      required: true,
      placeholder: 'Username',
      displayOrder: 1
    },
    {
      label: 'Password',
      key: 'password',
      required: true,
      placeholder: 'Password',
      type: 'password',
      displayOrder: 2
    }
  ]
}

function AppWithAuth() {

  function handleAuthStateChange(state, data) {
    if (state === 'confirmSignIn') {
        /* Do something when the user has signed-in */
    }
  }

  return (
    <Authenticator signUpConfig={signUpConfig} hide={[ConfirmSignIn, RequireNewPassword, VerifyContact, ConfirmSignUp, ForgotPassword, TOTPSetup]} onStateChange={handleAuthStateChange}>
      <MyVerifyContact override={VerifyContact} />
      <MyConfirmSignUp override={ConfirmSignUp} />
      <App />
    </Authenticator>
  )
}

export default AppWithAuth;

Login画面を簡素化するために、Amplifyが提供している”VerifyContact”、”ConfirmSignUp”をoverrideしてCustomizeしています。Amplifyは手っ取り早くApplicationを作成できるのですが、こういったCustomizeをするのが簡単ではありません。

import { AuthPiece } from 'aws-amplify-react';

class MyVerifyContact extends AuthPiece {
  constructor(props) {
    super(props);
    this._validAuthStates = ["verifyContact"];
    this.state = {}
  }

  static getDerivedStateFromProps(props, state) {
    if(props.authState === "verifyContact") {
      props.onStateChange('signedIn');
      return props;
    } else {
      return null;
    }
  }

  showComponent(theme) {
    return null;
  }

}

export default MyVerifyContact;
import React from 'react';

import { I18n } from 'aws-amplify';
import { AuthPiece } from 'aws-amplify-react';

import {
  FormSection,
  SectionHeader,
  SectionBody,
  SectionFooter,
  Button
} from 'aws-amplify-react';

class MyConfirmSignUp extends AuthPiece {
  constructor(props) {
    super(props);
    this._validAuthStates = ["confirmSignUp"];
    this.gotoSignIn = this.gotoSignIn.bind(this);
  }

  gotoSignIn() {
    super.changeState('signIn');
  }

  showComponent(theme) {
    return (
      <FormSection theme={theme}>
        <SectionHeader theme={theme}>{I18n.get('Please contact your administrator to grant your account.')}</SectionHeader>
        <SectionBody theme={theme}>
        </SectionBody>
        <SectionFooter theme={theme}>
          <Button onClick={this.gotoSignIn} theme={theme}>
            {I18n.get('Goto SignIn')}
          </Button>
        </SectionFooter>
      </FormSection>
    );

  }

}

export default MyConfirmSignUp;

最後に、”App.js”を編集して、Loginの状態を表示するようにします。

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App(props) {

  if(props.authState === 'signedIn') {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  } else if(props.authState === 'confirmSignUp') {
    return (
      <div className="Your account is not granted">
        <header className="App-header">
          Your account is not granted.
        </header>
      </div>
    );
  } else if(props.authState === 'signIn') {
    return (
      <div className="Welcome to the world">
        <header className="App-header">
          Welcome to the world.
        </header>
      </div>
    );
  } else {
    return (
      <div className="Login failed">
        <header className="App-header">
          Login failed.
        </header>
      </div>
    );
  }
}

export default App;

今回はLocal環境からnpm startしてみます。

AmplifyのLogin画面が表示されます。

次回は、ユーザー認証の方法について説明していきます。

コメントを残す