Как быстро настроить аутентификацию с помощью AWS Amplify

В этой статье мы создадим приложение для блога с использованием 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.

Оцените статью
devanswers.ru
Добавить комментарий