В этой статье мы создадим приложение для блога с использованием AWS Amplify, которое позволит пользователям войти в систему и начать вести блог сразу после завершения регистрации.
AWS Amplify позволяет front-end и мобильным разработчикам легко создавать и развертывать полнофункциональные веб- и мобильные приложения на базе AWS. Аутентификация — одна из самых важных функций в приложении, поскольку она определяет личность пользователя. Недавно команда Amplify добавила новую функцию в поток аутентификации Amplify. Эта функция позволяет пользователям автоматически входить в приложение сразу после регистрации.
Предварительные условия
Чтобы следовать этому примеру, вам необходимо иметь:
- учетная запись AWS
- Знание JavaScript и React
- редактор кода (предпочтительно VS Code)
- Установленный Nodejs >=v14
- Установленный AWS Amplify CLI. Выполните эту команду, если он у вас не установлен npm install -g @aws-amplify/cli
- Разрешенный AWS Amplify CLI amplify configure
Полный код для этого поста находится на этом репозитории Github.
Настройка проекта React
В этом посте мы создадим простое приложение React. Выполните эту команду, чтобы создать новый проект React:
npx create-react-app react-amplify-auth
После завершения установки смените каталог на проект react и откройте папку в редакторе кода.
cd react-amplify-auth
code .
Давайте перейдем к настройке amplify для проекта и созданию профиля пользователя.
Настройка Amplify
Мы настроили профиль пользователя для проекта amplify, теперь давайте инициализируем amplify в проекте. Выполните эту команду в терминале.
amplify init
Выберите эти параметры:
➜ react-amplify-auth git:(master) amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project reactamplifyauth
The following configuration will be applied:
Project information
| Name: reactamplifyauth
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: react
| Source Directory Path: src
| Distribution Directory Path: build
| Build Command: npm run-script build
| Start Command: npm run-script start
? Initialize the project with the above configuration? Yes
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile
For more information on AWS Profiles, see:
<https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html>
? Please choose the profile you want to use react-amplify-auth
Вы увидите несколько журналов установки, а когда она завершится, вы увидите сообщение, подобное этому:
CREATE_COMPLETE amplify-reactamplifyauth-dev-133741 AWS::CloudFormation::Stack Wed Aug 03 2022 13:38:19 GMT+0100 (West Africa Standard Time)
✔ Successfully created initial AWS cloud resources for deployments.
✔ Help improve Amplify CLI by sharing non sensitive configurations on failures (y/N) · yes
✔ Initialized provider successfully.
✅ Initialized your environment successfully.
Your project has been successfully initialized and connected to the cloud!
Если вы дошли до этого момента, то вы Awesome 🙌🏾 Была создана папка amplify, структура директории выглядит следующим образом:
.
├── #current-cloud-backend
│ ├── amplify-meta.json
│ ├── awscloudformation
│ │ └── build
│ │ └── root-cloudformation-stack.json
│ └── tags.json
├── README.md
├── backend
│ ├── amplify-meta.json
│ ├── awscloudformation
│ │ └── build
│ │ └── root-cloudformation-stack.json
│ ├── backend-config.json
│ ├── tags.json
│ └── types
│ └── amplify-dependent-resources-ref.d.ts
├── cli.json
├── hooks
│ ├── README.md
│ ├── post-push.sh.sample
│ └── pre-push.js.sample
└── team-provider-info.json
Фантастика! Теперь давайте добавим модуль аутентификации в наше React-приложение.
Настройка Amplify Auth
Мы успешно инициализировали amplify и подключили наш проект amplify к облаку. Теперь давайте создадим службу аутентификации. Выполните эту команду в терминале:
amplify add auth
Вам будут предложены следующие варианты:
You will be prompted with these options:
Using service: Cognito, provided by: awscloudformation
The current configured provider is Amazon Cognito.
Do you want to use the deUPDATE_COMPLETE amplify-reactamplifyauth-dev-133741 AWS::CloudFormation::Stack Wed Aug 03 2022 15:03:45 GMT+0100 (West Africa Standard Time)
✔ All resources are updated in the cloudfault authentication and security configuration? Default configuration
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.
✅ Successfully added auth resource reactamplifyauth963ad60d locally
Это добавляет auth/reactamplifyauth963ad60d
в каталог бэкенда amplify. Теперь давайте продвинем сервис auth в облако amplify. Выполните эту команду в терминале:
amplify push
Вы увидите журналы развертывания и следующее сообщение в конце
UPDATE_COMPLETE amplify-reactamplifyauth-dev-133741 AWS::CloudFormation::Stack Wed Aug 03 2022 15:03:45 GMT+0100 (West Africa Standard Time)
✔ All resources are updated in the cloud
Перейдите в вашу студию amplify с помощью amplify console
и вы увидите что-то вроде этого:
Вы можете видеть добавленные категории: Аутентификация. Замечательно. Давайте теперь интегрируем аутентификацию во фронтенд.
Настройка GraphQL API
Мы будем создавать простой API для записей блога. Выполните эту команду, чтобы создать amplify api
amplify add api
Выберите эти параметры:
➜ react-amplify-auth git:(master) ✗ amplify add api
? Select from one of the below mentioned services: GraphQL
? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)
Обновите schema.graphql
с помощью этих строк кода:
@auth(rules: [{allow: owner}])
type Posts @model {
id: ID!
title: String!
description: String
author: String
}
Далее, запустите
amplify push --y
В результате будет создан
- AppSync GraphQL API,
- папку
graphql
в папке приложенияsrc
, которая состоит из локальных операций GraphQL - таблица DynamoDB для Posts
На данном этапе мы успешно создали бэкенд для проекта. Давайте перейдем к созданию пользовательского интерфейса с помощью React.
Настройка Auth в React
Сначала удалите все файлы, кроме App.js
, index.js
и aws-exports.js
. Установите эти зависимости:
npm i aws-amplify @aws-amplify/ui-react tailwindcss postcss autoprefixer
Обновите свой index.js
, чтобы он выглядел следующим образом:
//src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import Amplify from 'aws-amplify';
import config from './aws-exports';
Amplify.configure(config);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Создайте index.css
и добавьте эти строки кода Добавить стили tailwind в проект.
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Наконец, обновите содержимое tailwind.config.js
до этого.
//tailwind.config.js
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Давайте продолжим и создадим поток аутентификации. Создайте Signup.js
и добавьте эти строки кода.
//src/Signup.js
import { Auth } from 'aws-amplify';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
const initialFormState = { username: "", password: "", email: "" }
const Signup = () => {
const [formData, setFormData] = useState(initialFormState);
const navigate = useNavigate();
async function signUp() {
try {
const { user } = await Auth.signUp({
username: formData.username,
password: formData.password,
attributes: {
email: formData.email,
},
autoSignIn: { // optional - enables auto sign in after user is confirmed
enabled: true,
}
});
console.log(user);
localStorage.setItem('username', user.username);
navigate('/confirm', { replace: true })
} catch (error) {
console.log('error signing up:', error);
}
}
return ()
Здесь мы импортируем Auth
из aws-amplify
. В нем есть все методы, которые нам понадобятся для реализации потока аутентификации. Такие методы, как signUp
, confirmSignUp
, signOut
и множество других. Мы создали объект initialFormState
с username
, password
, и email
в качестве ключей, все они имеют пустую строку в качестве начальных значений, которые будут представлять начальные значения состояния для данных формы, которые мы будем создавать дальше.
Далее, в функции signUp
, мы деструктурируем данные, полученные из метода Auth.signup()
в объект user
. Метод Auth.signUp()
принимает имя пользователя, пароль, а также атрибуты и любые другие необязательные параметры. Здесь мы включили autoSignIn
и активировали его. Метод autoSignIn
— это новая функция, которая позволяет пользователю автоматически войти в систему, не требуя Auth.signIn
для входа.
Наконец, мы сохраним имя пользователя
из объекта user
в localStorage. Добавьте следующие строки кода:
<div className="bg-white">
<div className="grid max-w-screen-xl h-screen text-black m-auto place-content-center">
<div className="w-[30rem] space-y-6">
<div className="flex flex-col">
<label> Username </label>
<input
onChange={e => setFormData({ ...formData, 'username': e.target.value })}
placeholder="username"
value={formData.username}
type="text"
className="border border-sky-500 p-2 rounded w-full"
/>
</div>
<div className="flex flex-col">
<label>Password </label>
<input
onChange={e => setFormData({ ...formData, 'password': e.target.value })}
placeholder="pasword"
value={formData.password}
type="password"
className="border p-2 rounded border-sky-500"
/>
</div>
<div className="flex flex-col">
<label>Email </label>
<input
onChange={e => setFormData({ ...formData, 'email': e.target.value })}
placeholder="email"
value={formData.email}
type="email"
className="border p-2 rounded border-sky-500"
/>
</div>
<div>
<button className="border-none bg-sky-700 text-white p-2 mt-4 rounded m-auto" onClick={signUp}> Sign up </button>
</div>
</div>
</div>
</div>
Здесь у нас есть inputs
. Для каждого поля мы используем событие onChange
и присваиваем значение поля объекту formData
.
Давайте продолжим и создадим экран подтверждения регистрации. Создайте файл confirmSignup.js
и добавьте эти строки кода:
//src/confirmSignup.js
import { useState } from 'react';
import { Auth } from 'aws-amplify';
import { useNavigate } from 'react-router-dom';
const initialFormState = { code: "" }
const ConfirmSignup = () => {
const [formData, setFormData] = useState(initialFormState);
const navigate = useNavigate();
const username = localStorage.getItem('username')
const code = formData.code
async function confirmSignUp() {
try {
await Auth.confirmSignUp(username, code);
localStorage.clear();
navigate('/posts', { replace: true })
} catch (error) {
console.log('error confirming sign up', error);
}
}
return (
<div className="grid max-w-screen-xl h-screen text-black m-auto place-content-center">
<div className="w-[30rem] space-y-6">
<label htmlFor='Confirmation Code'> Enter the confirmation code sent to your email </label>
<input
onChange={e => setFormData({ ...formData, 'code': e.target.value })}
placeholder="code"
value={formData.code}
type="text"
className="border border-sky-500 p-2 rounded w-full shadow"
/>
</div>
<button className="border-2 bg-sky-700 border-none text-white p-2 mt-4 rounded m-auto" onClick={confirmSignUp}> Confirm</button>
</div>
)
}
export default ConfirmSignup
Здесь Auth.confirmSigUp()
принимает имя пользователя
и код
. Мы получаем имя пользователя
из localStorage
, а код — из значения ввода.
Создайте файл Home.js
и добавьте эти строки кода:
//src/Home.js
import { useNavigate } from "react-router-dom"
const Home = () => {
const navigate = useNavigate()
const redirectToSignup = () => {
navigate('/signup')
}
return (
<div className="bg-black">
<div className="grid max-w-screen-xl h-screen text-white m-auto place-content-center">
<h2 className="mb-8 text-4xl"> Amplify Posts app</h2>
<button className="border-2 p-2" onClick={redirectToSignup}> Create Account</button>
</div>
</div>
)
}
export default Home
На этой странице у нас будет только кнопка, которая перенаправит нас на экран регистрации.
Обновите App.js
этими строками кода:
//src/App.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
import Signup from './Signup'
import Posts from './Post'
import Confirm from './confirmSignup'
import Home from "./Home"
const App = () => {
return (
<div>
<Router>
<Routes>
<Route path="/" element={<Home />}> Home</Route>
<Route path="/signup" element={<Signup />}> Signup </Route>
<Route path="/confirm" element={<Confirm />}></Route>
<Route path="/posts" element={<Posts />}> </Route>
</Routes>
</Router>
</div>
)
}
export default App
Здесь мы определяем маршруты для нашего приложения. Пока мы создали маршруты Home
, Signup
и Confirm
, мы должны перейти к созданию экрана Posts.
Создайте src/Posts.js
и добавьте эти строки кода:
//src/Posts.js
import "@aws-amplify/ui-react/styles.css";
import { Auth, API } from "aws-amplify";
import { useState, useEffect } from 'react'
import { listPosts } from "./graphql/queries";
import { createPosts as createPostsMutation } from "./graphql/mutations";
import { useNavigate } from "react-router-dom";
const initialFormState = { title: "", author: "", description: "" }
function Posts() {
const [user, setUser] = useState()
const [posts, setPosts] = useState([]);
const [formData, setFormData] = useState(initialFormState);
const navigate = useNavigate()
async function fetchUser() {
Auth.currentAuthenticatedUser({
bypassCache: true // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
})
.then(user => {
console.log(user)
setUser(user)
})
.catch(err => console.log(err));
}
async function fetchPosts() {
const apiData = await API.graphql({ query: listPosts });
setPosts(apiData.data.listPosts.items);
}
async function createPost() {
if (!formData.title || !formData.description || !formData.author) return;
await API.graphql({ query: createPostsMutation, variables: { input: formData } });
setPosts([...posts, formData]);
setFormData(initialFormState);
}
async function signOut() {
try {
await Auth.signOut({ global: true });
navigate('/signup', { replace: true })
} catch (error) {
console.log('error signing out: ', error);
}
}
useEffect(() => {
fetchUser()
fetchPosts()
}, [])
return ()
Давайте пройдемся по этому фрагменту кода. Массив posts
будет содержать посты, которые мы получаем из API graphql. В массиве formData
будут храниться значения вводимых данных. У нас также есть эти функции:
Давайте перейдем к реализации ui. Добавьте эти строки jsx
<div className=" m-auto p-12">
<div className=" space-y-6 w-[30rem] m-auto">
<div className="flex flex-col">
<input
onChange={e => setFormData({ ...formData, 'title': e.target.value })}
placeholder="Post title"
value={formData.title}
type="text"
className="border border-sky-500 p-2 rounded w-full"
/>
</div>
<div className="flex flex-col">
<input
onChange={e => setFormData({ ...formData, 'author': e.target.value })}
placeholder="Post author"
value={formData.author}
type="text"
className="border border-sky-500 p-2 rounded w-full"
/>
</div>
<div className="flex flex-col">
<textarea
onChange={e => setFormData({ ...formData, 'description': e.target.value })}
placeholder="Post description"
value={formData.description}
className="border border-sky-500 p-2 rounded w-full"
/>
</div>
<button className="border-none bg-sky-700 text-white p-2 mt-4 rounded m-auto" onClick={createPost}>Create Post</button>
</div>
</div>
Наконец, в этом фрагменте кода у нас есть кнопка, которая вызывает функцию createPost()
. Давайте продолжим и создадим представление для постов.
<div className="mt-20">
<h1 className="text-3xl font-semibold "> Welcome {user && (<span>{user.username}</span>)} </h1>
<h2>Here are your posts:</h2>
{posts === 0 ?
(
<div>
<h3> They are no posts</h3>
</div>
) :
posts?.map(post => (
<div key={post.id || post.title} className="my-10">
<h2 className="text-xl font-semibold">Title: {post.title}</h2>
<p className="font-bold text-lg"> Author: {post.author}</p>
<p>Message: {post.description}</p>
</div>
))
}
</div>
<button className="border-2 bg-red-500 border-none text-white p-2 mt-8 rounded m-auto" onClick={signOut}> Signout</button>
</div>
Ранее мы создали массив posts
, который содержит посты, полученные из GraphQL API. Здесь мы просматриваем массив posts
и выводим заголовок, описание и информацию об авторе. У нас также есть кнопка SignOut, здесь мы используем функцию signOut
из Authenticator.
Если вы заметили, когда вы создаете учетную запись и проходите процесс верификации, вы автоматически входите в приложение. Эта функция была добавлена недавно, и мы можем использовать ее уже сейчас! За кулисами, слушатель подтверждения регистрации инициирует автоматический вход, как только учетная запись подтверждена. Это происходит, если autoSignIn
имеет значение true в параметрах функции SignUp
. Существует три различных способа автоматического входа в систему: code
, link
или autoConfirm
. По умолчанию используется code
.
Реализация кода использует событие прослушивания хаба. Как только пользователь подтверждает учетную запись с помощью кода, он отправляет событие прослушивания. Функция SignUp
прослушивает это событие, чтобы инициировать процесс SignIn
.
Реализация Link использует опрос для регистрации пользователей, поскольку нет явного способа узнать, перешел ли пользователь по ссылке. Опрос выполняется в течение 3 минут (время проверки ссылки на действительность) каждые 5 секунд.
Вот и все, у вас должно получиться что-то вроде этого:
Заключение
Amplify упрощает процесс аутентификации при использовании Amazon Cognito в качестве провайдера аутентификации. В этом посте мы узнали, как настроить и реализовать аутентификацию с помощью фреймворка Amplify.