Help us understand the problem. What is going on with this article?

React.jsとAWS(cognito)の連携

何をする?

reactコンポーネントからAWSのcognitoにユーザ登録を行い、サインインを行います。

cognitoについて

アプリやwebサービスの認証基盤です。認証ロジックだけAWSに託します。
サービス提供者はログイン情報を保持することなく認証結果のみを受け取ります。
また、cognitoを介して様々なAWSサービスが使えるようになります。
詳細は公式ドキュメントを参照ください。
公式ドキュメント

バージョン

  • node.js 10.15.3
  • react.js ^16.12.0
  • material-ui ^0.20.2
  • amazon-cognito-identity-js ^3.0.15

準備

awsへの連携にはamazon-cognito-identity-jsを使います。

 npm install --save amazon-cognito-identity-js

詳細はこちらを参照ください。
github公式ページ

ユーザプールの作成

userpool.png

アプリクライアント.png

ユーザプール > ユーザとグループ
にアカウント情報が蓄積されます。
プール ARNとプール IDとアプリクライアントIDはプログラムで使用します。
また、今回はサインイン時の二段階認証にEメールを利用します。

IDプールの作成

IDpool.png

IDプールの編集.png

ユーザプールへの登録時にIDプールにも情報を登録します。
これにより、登録ユーザはIDプールを介して他のAWSサービスを利用できるようになります。
IDプールのIDはプログラムで使用します。

これでcognitoを使用する準備ができました。
次にアプリケーションとcognitoを連携します。

該当のプログラム

サインイン画面

まずはreact側でユーザIDとパスワードとEメールアドレスを入力する画面を作成。

signin.js
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles,createMuiTheme,MuiThemeProvider} from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import { connect } from "react-redux"
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import { compose } from 'redux'
import Grid from '@material-ui/core/Grid';
import CognitoAuth from 'aws'


const styles = theme => ({
    contents: {
        margin: '150px auto 0 ',
        maxWidth : '1260px',
    },
    formContents:{
        margin: '0 auto',
        maxWidth: '480px'
    },
    formControl:{
        display:'block',
        width:'100%'
    },
    btn:{
        marginTop:'20px',
    }

});
function typographyV1Theme(theme) {
    return createMuiTheme({
        ...theme,
        typography: {
            useNextVariants: false,
        },
    });
}

class SignIn extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            password: '',
            user_id: '',
            email:'',
            errors:[],
        };
    }
    goError = async (e) => {
        await this.setState(
            {errors: (e.message || JSON.stringify(e))}
        )
        await console.log("エラー:"+this.state.errors)
    };
    goNext = async () => {
        try{
            await this.props.history.push({
                pathname: '/activate',
                state:{
                    user_name: this.state.user_id,
                    password: this.state.password,
                    email: this.state.email,
                    user_id:this.state.user_id
                }
            })
        } catch (e){
            this.goError(e)
        }
    };
    signInCognito = async () => {
        await alert(this.state.user_id)
        await CognitoAuth.signIn(this.state.user_id,this.state.password,this.state.email)
            .then(this.goNext)
            .catch(this.goError)
    };

    render() {
        const { classes,dispatchAddValue } = this.props;
        return (
            <MuiThemeProvider theme={typographyV1Theme}>
                <div className={classes.contents}>
                    <p>お名前をメールアドレスを入力してください。</p>
                </div>
                <div className={classes.formContents}>
                    <Grid container spacing={16}>
                        <Grid item xs={12} sm={12}>
                            <FormControl className={classes.formControl}>
                                <InputLabel htmlFor="component-simple">ID</InputLabel>
                                <Input id="component-simple" name="user_id"  fullWidth defaultValue={this.state.user_id} />
                            </FormControl>
                        </Grid>
                    </Grid>
                    <Grid container spacing={16}>
                        <Grid item xs={12} sm={12}>
                            <FormControl className={classes.formControl}>
                                <InputLabel htmlFor="component-simple">パスワード</InputLabel>
                                <Input id="component-simple" name="password" fullWidth defaultValue={this.state.password} />
                            </FormControl>
                        </Grid>
                    </Grid>
                    <Grid container spacing={16}>
                        <Grid item xs={12} sm={12}>
                            <FormControl className={classes.formControl}>
                                <InputLabel htmlFor="component-simple">メールアドレス</InputLabel>
                                <Input id="component-simple" name="email"  fullWidth defaultValue={this.state.email} onChange={this.handleChange} />
                            </FormControl>
                        </Grid>
                    </Grid>
                    <Grid container spacing={16}>
                        <Grid item xs={12} sm={12}>
                            {this.state.errors}
                        </Grid>
                    </Grid>

                    <Grid className={classes.btn} container spacing={16}>
                        <Grid item xs={12} sm={12}>
                            <Button variant="contained" color="secondary" fullWidth className={classes.fcButton} onClick={this.signInCognito}>
                               次へ
                            </Button>
                        </Grid>
                    </Grid>
                </div>
            </MuiThemeProvider>
        );

    }
}

SignIn.propTypes = {
    classes: PropTypes.object.isRequired,
};
export default compose(
    withStyles(styles),
    connect(
        mapStateToProps,
        mapDispatchToProps
    )
)(SignIn)

画面はこんなイメージ
サインイン.png

認証

「次へ」ボタンを押すとCognitoAuth.signInが実行され、入力したアドレスに認証コードが届きます。

aws.js
import AWS from 'aws-sdk'
const AmazonCognitoIdentity = require('amazon-cognito-identity-js')

const CognitoAuth= {
    signIn: function (userName, password, email = null, phoneNumber = null) {
        return new Promise(function(resolve, reject){
            var userPoolData = {
                UserPoolId : [ユーザプールID],
                ClientId : [ユーザプールクライアントID]
            }
            var userPool = new AmazonCognitoIdentity.CognitoUserPool(userPoolData)

            var attributeList = [];

            if (email !== null){
                var attribute = {Name : 'email', Value : email};
                attributeList.push(new AmazonCognitoIdentity.CognitoUserAttribute(attribute));
            }
            if (phoneNumber !== null){
                var attribute = {Name : 'phone_number', Value : phoneNumber};
                attributeList.push(new AmazonCognitoIdentity.CognitoUserAttribute(attribute));
            }
            return userPool.signUp(userName, password, attributeList, null, function(err, result) {
                if (err) {
                    reject(err)
                }
                resolve(result.user)
            })
        })
    },

}
export default CognitoAuth;

認証コード入力

届いた認証コードと、ユーザネームと、パスワードを引数にconfirmRegistrationを実行するだけで認証が通ります。そのままログイン処理を実行することでサインイン後は自動的にホーム画面などに遷移させることができます。

aws.js
import AWS from 'aws-sdk'
const AmazonCognitoIdentity = require('amazon-cognito-identity-js')

const CognitoAuth= {
・・・
    confirmRegistration: function (userName, password, registrationCode) {
        return new Promise(function(resolve, reject){
            var authenticationData = {
                Username : userName,
                Password : password
            }
            var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData)

            var userPoolData = {
                UserPoolId : [ユーザプールID],
                ClientId : [ユーザプールクライアントID]
            }
            var userPool = new AmazonCognitoIdentity.CognitoUserPool(userPoolData)

            const userData = {
                Username : userName,
                Pool : userPool
            }
            var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
            cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH')

            cognitoUser.confirmRegistration(registrationCode, true, function(err, result) {
                if (err) {
                    reject(err)
                }
                resolve(result)
            })
        })
    },
}
export default CognitoAuth;

ログイン

aws.js
import AWS from 'aws-sdk'
const AmazonCognitoIdentity = require('amazon-cognito-identity-js')

const CognitoAuth= {
・・・
    doLogin: function (userName, password) {
        return new Promise(function(resolve, reject){
            let authenticationData = {
                Username : userName,
                Password : password
            };
            let authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData)

            let userPoolData = {
                UserPoolId : [ユーザプールID],
                ClientId : [ユーザプールクライアントID]
            };
            let userPool = new AmazonCognitoIdentity.CognitoUserPool(userPoolData)
            const userData = {
                Username : userName,
                Pool : userPool
            };
            let cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)

            cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH')

            cognitoUser.authenticateUser(authenticationDetails,{
                onSuccess: function (result) {

                    AwsAuth.setJwtToken(result.getIdToken().getJwtToken())
                    AWS.config.region = [リージョン]
                    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
                        IdentityPoolId : [IDプールのID],
                        Logins : {
                            [[プール ARN]]: result.getIdToken().getJwtToken()
                        }
                    });
                    AWS.config.credentials.clearCachedId()
                    AWS.config.credentials.refresh((error) => {
                        if (error) {
                            reject(error)
                        } else {
                            resolve(result.getIdToken().getJwtToken())
                        }
                    });
                },
                onFailure: function (err) {
                    console.log(err)
                    reject(err)
                },
                mfaRequired: function(result) {
                    reject("mfaRequired")
                },
                newPasswordRequired(result) {
                    reject("newPasswordRequired")
                },
                customChallenge(result) {
                    reject("customChallenge")
                }
            })
        })
    }
}

まとめ

cognitoを使うことでユーザのログイン情報をDBなどに保持する必要がなくなるのでサービスを提供する側にとっては大きなリスク回避になりますね。また、facebookやtwitterアカウントなどのリソースを利用した認証基盤も簡単に導入できます。

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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした