От автора: как выполняется двухфакторная аутентификация (как SMS, так и TOTP) с использованием React, React Router и Amazon Cognito. В этом посте мы рассмотрим, как внедрить реальную регистрацию и авторизацию пользователя с двухфакторной аутентификацией параллельно с маршрутизацией и потоком аутентификации в веб-приложении React.
Когда пользователь выйдет из системы, мы будем защищать определенные роуты, перенаправлять их для входа в систему и разрешать доступ к этим защищенным роутам только при входе пользователя в систему. Также мы проверим, изменив роут, останется ли пользователь на странице, или будет перенаправлен на вход / регистрацию.
Инструменты, которые мы будем использовать, — это приложения Create React, Glamour для стилизации, React Router для маршрутизации, Amazon Cognito для аутентификации и AWS Amplify для взаимодействия с услугами AWS.
В части 1 будет показано, как настроить поставщика аутентификации и методы, которые будут взаимодействовать с провайдером и регистрировать пользователя.
Часть 2 посвящена тому, как добавить React Router и реализовать поток аутентификации, включая защищенные роуты, выключение, TOTP и т. д.
Создание проекта и установка зависимостей
Первое, что мы сделаем, это создадим новый проект, установим зависимости и настроим поставщика аутентификации.
Давайте создадим проект React с помощью Create React App и перейдем в каталог нового проекта:
1 2 |
create-react-app react-auth cd react-auth |
Затем установим React Router и Glamour с использованием npm или yarn:
1 |
yarn add react-router-dom glamor |
Или
1 |
npm i react-router-dom glamor --save |
Добавление поставщика идентификации
Вы можете использовать любой поставщик идентификации, который захотите, но мы будем использовать Amazon Cognito вместе с библиотекой AWS Amplify JavaScript и AWSMobile CLI, чтобы автоматически генерировать ресурсы из командной строки.
Если поставщик еще не установлен, установите AWMobile CLI:
1 |
npm i -g awsmobile-cli |
Затем сконфигурируйте CLI с учетными данными AWS IAM (нажмите здесь, чтобы просмотреть видеоролик о том, как создавать учетные данные и настраивать CLI):
1 |
awsmobile configure |
Теперь мы создадим новый проект (выберите значение по умолчанию для всех параметров и придумайте название для проекта, если хотите)
1 |
awsmobile init |
Это создаст новый проект Mobile Hub, а также файл aws-exports.js в каталоге src. Затем добавим user-signin (Amazon Cognito) и развернём новую конфигурацию:
1 2 |
awsmobile user-signin enable awsmobile push |
awsmobile user-signin запустит Amazon Cognito в проекте с настройками по умолчанию, включая двухфакторную аутентификацию с помощью SMS (TOTP мы добавим позже). Если вы хотите контролировать, как создается поставщик аутентификации, вы можете или передать флажок -p, чтобы указать более подробную конфигурацию ( awsmobile user-signin enable -p ), или перейти в Amazon Cognito, чтобы напрямую создавать и настраивать сервис.
Добавление и тестирование регистрации рядового пользователя
Давайте добавим базовую форму регистрации пользователя в приложение, чтобы узнать, можем ли мы зарегистрировать пользователя.
Мы будем работать с Auth- классом от AWS Amplify для взаимодействия с Amazon Cognito. У Auth есть несколько разных методов, позволяющих всем пользователям регистрироваться и входить в систему для изменения и получения паролей и всего прочего.
Основными методами, которые мы будем использовать, следующие:
signUp — регистрирует нового пользователя
1 |
signUp(username: string, password: string, attributes?: object) |
confirmSignUp — отправляет двухфакторную аутентификацию для нового пользователя
1 |
confirmSignUp(username: string, authenticationCode: string) |
signIn – авторизовывает существующего пользователя
1 |
signIn(username: string, password: string) |
confirmSignIn — отправляет двухфакторную аутентификацию для входа пользователя
1 |
confirmSignIn(user: object, authenticationCode: string) |
Теперь рассмотрим это! Первое, что нам нужно сделать, это настроить AWS Amplify в корне проекта, src/index.js:
1 2 3 4 5 6 7 |
// other imports not shown import config from './aws-exports' import Amplify from 'aws-amplify' Amplify.configure(config) ReactDOM.render(<App />, document.getElementById('root')) registerServiceWorker() |
Мы начнем внедрять авторизацию, разрешив пользователям зарегистрироваться.
Давайте создадим новый компонент SignUp.js и поместим его в каталог src. Сейчас мы создаём базовую форму, которая позволит пользователям зарегистрироваться:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import React from 'react' import { css } from 'glamor' class SignUp extends React.Component { render() { return ( <div {...css(styles.container)}> <h2>SignUp</h2> </div> ) } } const styles = { container: { display: 'flex', flexDirection: 'column', alignItems: 'center' } } |
Затем добавим состояние:
1 2 3 4 5 6 7 8 9 10 |
class SignUp extends React.Component { state = { username: '', password: '', email: '', phone_number: '', authCode: '' } // rest of the class } |
Мы добавили элементы, которые нужны, чтобы создать пользователя и разрешить двухфакторную аутентификацию. Нам также необходимо будет создать материалы, которые позволят получить информацию о пользователе, а также код авторизации. Когда пользователь регистрируется с использованием метода signUp , он получает код авторизации через SMS, и ему нужно будет ввести это значение в форму, тогда мы подтвердим, что этот код верен, вызвав confirmSignUp:
Давайте теперь создадим входы, кнопки и метод onChange который будет записывать вход в состояние:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import React from 'react' import { css } from 'glamor' class SignUp extends React.Component { state = { username: '', password: '', email: '', phone_number: '', authCode: '' } onChange = (key, value) => { this.setState({ [key]: value }) } render() { return ( <div {...css(styles.container)}> <h2>Sign Up</h2> <input {...css(styles.input)} placeholder='Username' onChange={evt => this.onChange('username', evt.target.value)} /> <input {...css(styles.input)} placeholder='Password' type='password' onChange={evt => this.onChange('password', evt.target.value)} /> <input {...css(styles.input)} placeholder='Email' onChange={evt => this.onChange('email', evt.target.value)} /> <input {...css(styles.input)} placeholder='Phone Number' onChange={evt => this.onChange('phone_number', evt.target.value)} /> <div {...css(styles.button)}> <span>Sign Up</span> </div> <input {...css(styles.input)} placeholder='Authentication Code' onChange={evt => this.onChange('authCode', evt.target.value)} /> <div {...css(styles.button)}> <span>Confirm Sign Up</span> </div> </div> ) } } let styles = { container: { display: 'flex', flexDirection: 'column', alignItems: 'center' }, button: { width: '170px', padding: '10px 0px', backgroundColor: '#ddd', cursor: 'pointer', borderRadius: '3px', ':hover': { backgroundColor: '#ededed' } }, input: { height: 40, marginBottom: '10px', border: 'none', outline: 'none', borderBottom: '2px solid #4CAF50', fontSize: '16px', '::placeholder': { color: 'rgba(0, 0, 0, .3)' } } } export default SignUp |
Теперь мы создали наш пользовательский интерфейс, и нам нужно сделать только одно: вызвать методы Auth для регистрации и подтверждения нашего пользователя, поэтому давайте добавим их в несколько методов класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// previous imports omitted import { Auth } from 'aws-amplify' // previously shown code omitted signUp = () => { const { username, password, email, phone_number } = this.state Auth.signUp({ username, password, attributes: { email, phone_number } }) .then(() => console.log('successful sign up!')) .catch(err => console.log('error signing up: ', err)) } confirmSignUp = () => { Auth.confirmSignUp(this.state.username, this.state.authCode) .then(console.log('successful confirm sign up!')) .catch(err => console.log('error confirming signing up: ', err)) } render() { // // here we need to update the buttons to attach class methods to onClick event <div {...css(styles.button)} onClick={this.signUp}> <span>Sign Up</span> </div> <input {...css(styles.input)} placeholder='Authentication Code' onChange={evt => this.onChange('authCode', evt.target.value)} /> <div {...css(styles.button)} onClick={this.confirmSignUp}> <span>Confirm Sign Up</span> </div> } |
Наконец, мы импортируем и используем этот компонент в App.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import SignUp from './SignUp' class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <SignUp /> </div> ); } } export default App; |
Теперь давайте всё это опробуем. Вы должны иметь возможность зарегистрироваться, получить SMS с кодом авторизации, затем ввести код и получить подтверждение на консоли.
Где сейчас находятся данные пользователя? Мы можем видеть их на консоли. Чтобы проверить это, перейдите на панель управления Amazon Cognito , нажмите «Manage your User Pools», а затем выберите имя своего приложения и нажмите «Users and Settings». Там вы можете просмотреть список пользователей, которые зарегистрировались в приложении.
Теперь можно авторизовать пользователя! Процесс авторизации очень похож на регистрацию. Мы объединяем юзернэйм и пароль пользователя и вызываем Auth.signIn (username, password), который в случае успеха возвращает объект пользователя и отправляет SMS-сообщение с кодом авторизации. Затем мы можем взять имя пользователя и номер подтверждения и отправить подтверждение:
1 2 3 4 5 6 7 8 9 10 11 12 |
signIn() { Auth.signIn(this.state.username, this.state.password) .then(user => this.setState({ user })) .catch(err => console.log('error signing in! :', err)) } confirmSignIn() { Auth.confirmSignIn(this.state.user, this.state.authCode) .then(userData => { console.log('userdata: ', userData) }) .catch(err => console.log('error confirming sign in!: ', err)) } |
Этот код работает, но ещё нужно выполнить правильный вход пользователя (во второй части), добавив маршруты в приложение. Не стесняйтесь испытывать его и смотреть, можете ли вы заставить код работать, прежде чем увидите мою реализацию в части 2.
confirmSignIn вернет объект пользователя со всеми данными, включая юзернэйм и уникальный идентификатор (sub, который останется уникальным, даже если пользователь удалит свою учетную запись, а новый пользователь зарегистрируется c тем же юзернэймом).
Если бы мы хотели получить доступ к данным пользователя в приведенном выше ответе, мы могли бы получить его из userData.signInUserSession.idToken.payload, и выглядеть это будет так:
Существует несколько способов получения этих данных в любой момент после входа пользователя в систему, включая Auth.currentAuthenticatedUser(). Для полного API Auth нажмите здесь.
После входа пользователя в систему сеанс сохраняется в localStorage помощью Amplify до тех пор, пока он не вызовет метод Auth.signOut . Таким образом, пользователь может покинуть страницу, вернуться и все равно войти в систему!
Во второй части мы продолжим добавление маршрутизации и стилей, а также TOTP, чтобы вы могли работать с Google Authenticator или вашим провайдером TOTP сравнивая с SMS!
Автор: Nader Dabit
Источник: //hackernoon.com/
Редакция: Команда webformyself.