В последней статье мы сосредоточились на создании контекста аутентификации и реализации регистрации. Теперь давайте пойдем дальше и добавим вход пользователя в систему, выход из системы и взглянем на сеанс пользователя.

Авторизоваться

Теперь, когда у нас есть зарегистрированный пользователь, пришло время предоставить ему возможность войти в систему.

Начнем с создания компонента Login с базовой разметкой. Мы можем начать с дублирования компонента SignUp и внесения нескольких изменений:

const Login = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
  };

  return (
    <Flex
      minH={"100vh"}
      align={"center"}
      justify={"center"}
      bg={useColorModeValue("gray.50", "gray.800")}
    >
      <Stack spacing={8} mx={"auto"} maxW={"lg"} py={12} px={6}>
        <Stack align={"center"}>
          <Heading fontSize={"4xl"}>Login</Heading>
        </Stack>
        <Box
          rounded={"lg"}
          bg={useColorModeValue("white", "gray.700")}
          boxShadow={"lg"}
          p={8}
        >
          <form onSubmit={handleSubmit}>
            <Stack spacing={4}>
              <FormControl id="email">
                <FormLabel>Email address</FormLabel>
                <Input
                  type="email"
                  onChange={(event) => setEmail(event.target.value)}
                />
              </FormControl>
              <FormControl id="password">
                <FormLabel>Password</FormLabel>
                <Input
                  type="password"
                  onChange={(event) => setPassword(event.target.value)}
                />
              </FormControl>
              <Stack spacing={10}>
                <Button
                  bg={"blue.400"}
                  color={"white"}
                  _hover={{
                    bg: "blue.500",
                  }}
                  type="submit"
                >
                  Login
                </Button>
              </Stack>
            </Stack>
          </form>
        </Box>
      </Stack>
    </Flex>
  );
};

export default Login;

Теперь давайте создадим функцию для аутентификации пользователя. Мы можем добавить эту функцию в наш файл контекста:

const authenticate = useCallback(
    async (Username: string, Password: string) => {
      const user = new CognitoUser({
        Username,
        Pool: UserPool,
      });

      const authDetails = new AuthenticationDetails({
        Username,
        Password,
      });

      user.authenticateUser(authDetails, {
        onSuccess: (data) => {
          setSession(data);
        },
        onFailure: (error) => {
          if (error) {
            toast({
              title: error.message,
              status: "error",
              isClosable: true,
            });
          }
        },
      });
    },
    [toast]
  );

Далее давайте импортируем и вызываем эту функцию, когда пользователь отправляет форму входа:

// Login.tsx
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    authenticate(email, password);
  };

Я столкнулся с ошибкой:

Причина ошибки в том, что требуется опция MFA (многофакторная аутентификация). Чтобы решить эту проблему, выполните следующие действия:

  1. Перейдите к настройкам пула пользователей.
  2. Перейдите в раздел «Вход в систему».
  3. Нажмите на опцию «Многофакторная аутентификация» и выберите «Изменить».

Когда вы окажетесь в настройках «Многофакторная аутентификация», у вас будет возможность выбрать предпочтительный метод аутентификации. Поскольку мне не нужен MFA для тестового приложения, я просто отключил его. Таким образом, вы можете продолжить тестирование без необходимости дополнительных шагов аутентификации.

Теперь мы можем попробовать войти снова:

Большой! Мы получили всю необходимую информацию для управления нашим пользователем. В следующем разделе я объясню, как получить сеанс пользователя, и покажу, как выйти из системы.

Сеанс пользователя

После авторизации получаем три вещи:

  1. токен доступа
  2. идентификатор токена
  3. обновить токен

Все это мы можем использовать для получения пользовательской сессии, содержащей информацию о пользователе.

Сначала я создаю функцию getSession в контексте Auth:

// Auth.tsx
const getSession: State["getSession"] = useCallback(async () => {
    return new Promise((resolve, reject) => {
// 1. get current session after login
      const userSession = UserPool.getCurrentUser();

      if (userSession) {
        userSession.getSession(
          async (error: unknown, session: CognitoUserSession) => {
            if (error) {
              console.error(error);
            } else {
              const attributes: { [key: string]: string } = await new Promise(
                (resolve, reject) => {
// 2. if we have a session we can get user info
                  userSession.getUserAttributes((error, attributes) => {
                    if (error) {
                      reject(error);
                    } else {
                      const results: { [key: string]: string } = {};
// 3. Normalize user info
                      if (attributes)
                        for (const attribute of attributes) {
                          const { Name, Value } = attribute;
                          results[Name] = Value;
                        }

                      resolve(results);
                    }
                  });
                }
              );

              const value = {
                session,
                attributes: attributes,
              };
// 4. Return user session and user info
              resolve(value);
            }
          }
        );
      } else {
        reject();
      }
    });
  }, []);

Теперь, когда у нас есть функция getSession, мы можем использовать ее после входа пользователя в систему. Вызывая эту функцию, мы можем получить сеанс пользователя и отобразить все необходимые данные для пользователя на странице его учетной записи. Итак, давайте создадим страницу аккаунта.

Чтобы страница «Учетная запись» отображалась только тогда, когда доступна сессия пользователя, нам нужно добавить условие в наш файл App.tsx. Это гарантирует, что страница отображается только тогда, когда у нас есть действующий сеанс пользователя:

function App() {
  const { session } = useAuth();

  return <>{session ? <Account /> : <Login />}</>;
}

Далее приступим к созданию страницы Account:

const Account = () => {
  const { getSession } = useAuth();

  const [profile, setProfile] = useState<{
    username: string;
    email: string;
  } | null>({
    username: "",
    email: "",
  });

  useEffect(() => {
    getSession().then((data) => {
      setProfile({
        username: data.attributes.name,
        email: data.attributes.email,
      });
    });
  }, [getSession]);

  return (
    <Flex
      minH={"100vh"}
      align={"center"}
      justify={"center"}
      bg={useColorModeValue("gray.50", "gray.800")}
    >
      <Stack
        spacing={4}
        w={"full"}
        maxW={"md"}
        bg={useColorModeValue("white", "gray.700")}
        rounded={"xl"}
        boxShadow={"lg"}
        p={6}
        my={12}
      >
        <Heading lineHeight={1.1} fontSize={{ base: "2xl", sm: "3xl" }}>
          User Profile
        </Heading>
        <FormControl id="userName">
          <FormLabel>User name</FormLabel>
          <Input
            placeholder="UserName"
            _placeholder={{ color: "gray.500" }}
            type="text"
            disabled
            value={profile?.username}
          />
        </FormControl>
        <FormControl id="email">
          <FormLabel>Email address</FormLabel>
          <Input
            placeholder="[email protected]"
            _placeholder={{ color: "gray.500" }}
            type="email"
            disabled
            value={profile?.email}
          />
        </FormControl>
        <Stack spacing={6} direction={["column", "row"]}>
          <Button
            bg={"red.400"}
            color={"white"}
            w="full"
            _hover={{
              bg: "red.500",
            }}
          >
            Logout
          </Button>
        </Stack>
      </Stack>
    </Flex>
  );
};

Вот так выглядит страница Account:

Здесь у нас есть простая форма, которая включает поля для имени пользователя и электронной почты, а также кнопку выхода. Это охватывает основы управления сеансом пользователя. Если вы заинтересованы в более глубоком изучении пользовательских сессий и пользовательских атрибутов, я рекомендую прочитать официальную документацию для получения более подробной информации.

Выйти

Выйти очень просто. Я создаю функцию выхода из системы в Auth.tsx:

const logout = useCallback(() => {
    const user = UserPool.getCurrentUser();
    if (user) {
      user.signOut();
      setSession(null);
    }
  }, []);

и вызовите эту функцию, когда пользователь нажимает кнопку выхода из системы:

// Account.tsx

  <Button
            bg={"red.400"}
            color={"white"}
            w="full"
            _hover={{
              bg: "red.500",
            }}
            onClick={logout} // <-- logout function from Auth.tsx
          >
            Logout
      </Button>

Это все для выхода из системы.

Давайте посмотрим, что мы сделали в этом разделе:

  1. Мы разрешили пользователям входить в наше приложение.
  2. Мы реализовали функциональность для получения сеанса пользователя и создали страницу «Учетная запись» для отображения пользовательских данных.
  3. Мы предоставили пользователям возможность выхода из своих учетных записей.

Надеюсь, вы хорошо провели время, просматривая эту статью! В следующей части нашей серии я покажу вам, как менять пароли и обновлять адреса электронной почты. До встречи в следующей статье!