Это последняя статья из серии Аутентификация с помощью AWS Cognito, если вы не читали предыдущие статьи, вы можете найти их в моем профиле.
Итак, в прошлой статье мы рассмотрели пользовательские сеансы и функции выхода из системы. Теперь давайте сделаем еще один шаг и предоставим нашим пользователям возможность изменять свои пароли и обновлять свои атрибуты, такие как имена пользователей. Итак, приступим к делу и начнем!
Изменить пароль
Начнем с создания новой формы для смены пароля пользователя:
const ChangePassword = () => { const [password, setPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); const [showPassword, toggleShowPassword] = useToggle(false); const [showNewPassword, toggleShowNewPassword] = useToggle(false); const handleShowPassword = (type: "current" | "new") => { if (type === "current") { toggleShowPassword(!showPassword); } else { toggleShowNewPassword(!showNewPassword); } }; const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); }; return ( <form onSubmit={handleSubmit}> <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" }}> Change Password </Heading> <FormControl id="password"> <FormLabel>Current password</FormLabel> <InputGroup size="md"> <Input pr="4.5rem" type={showPassword ? "text" : "password"} placeholder="Enter current password" value={password} onChange={(event) => setPassword(event.target.value)} /> <InputRightElement width="4.5rem"> <Button h="1.75rem" size="sm" onClick={() => handleShowPassword("current")} > {showPassword ? "Hide" : "Show"} </Button> </InputRightElement> </InputGroup> </FormControl> <FormControl id="newPassword"> <FormLabel>New password</FormLabel> <InputGroup size="md"> <Input pr="4.5rem" type={showNewPassword ? "text" : "password"} placeholder="Enter new password" value={newPassword} onChange={(event) => setNewPassword(event.target.value)} /> <InputRightElement width="4.5rem"> <Button h="1.75rem" size="sm" onClick={() => handleShowPassword("new")} > {showNewPassword ? "Hide" : "Show"} </Button> </InputRightElement> </InputGroup> </FormControl> <Stack spacing={6} direction={["column", "row"]}> <Button bg={"blue.400"} color={"white"} w="full" _hover={{ bg: "blue.500", }} type="submit" > Submit </Button> </Stack> </Stack> </Flex> </form> ); }; export default ChangePassword;
Вот простая форма с двумя контролируемыми входами для смены паролей. Кроме того, мы добавили кнопку, позволяющую переключать видимость поля пароля. Ниже скриншот того, как это выглядит:
Теперь давайте внесем несколько дополнений в наш контекст Auth:
- Я добавил переменную состояния пользователя, чтобы гарантировать, что мы можем получить доступ к объекту
CognitoUser
во всем нашем приложении.
// Auth.tsx const [user, setUser] = useState<CognitoUser | null>(null); //... other code 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); setUser(user); // <-- Set user after succesfull authentication }, onFailure: (error) => { if (error) { toast({ title: error.message, status: "error", isClosable: true, }); } }, }); }, [toast] );
2. После входа в систему у нас теперь есть доступ к объекту CognitoUser
, который позволяет нам создать функцию для смены паролей:
// Auth.tsx const changeUserPassword = useCallback( ({ password, newPassword }: { password: string; newPassword: string }) => { if (user) { user.changePassword(password, newPassword, (error, result) => { if (error) { toast({ title: error.message, status: "error", isClosable: true, }); } else if (result) { toast({ title: result, status: "success", isClosable: true, }); } }); } }, [user, toast] );
Давайте вызовем эту функцию в обработчике отправки нашей формы ChangePassword
:
// ChangePassword.tsx const { changeUserPassword } = useAuth(); const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); changeUserPassword({ password, newPassword }); // <-- pass input state to the function };
Большой! Теперь пришло время попробовать. Просто введите свой текущий пароль и новый пароль, который вы хотите установить, и давайте посмотрим, что произойдет.
Если все настроено правильно, теперь вы сможете успешно изменить свой пароль. Также, если вы введете неправильный пароль, он покажет вам ошибку, не стесняйтесь попробовать это самостоятельно.
Обновить атрибуты пользователя
Для обновления атрибутов пользователя я создал отдельную форму, которая позволяет вам изменять конкретные данные пользователя. Чтобы изменить атрибут username
, давайте создадим простую форму с одним текстовым полем. Это позволит нам обновлять свойство username
на основе пользовательского ввода. Рассмотрим реализацию формы.
import { FormEvent, useState } from "react"; import { Button, Flex, FormControl, FormLabel, Heading, Input, Stack, useColorModeValue, } from "@chakra-ui/react"; import { useAuth } from "../../context/Auth"; const EditProfile = () => { const [username, setUsername] = useState(""); const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); }; return ( <form onSubmit={handleSubmit}> <Flex align={"center"} justify={"center"}> <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" }}> Edit profile </Heading> <FormControl id="password"> <FormLabel>New user name</FormLabel> <Input placeholder={profileInfo?.username} _placeholder={{ color: "gray.500" }} type="text" /> </FormControl> <Stack spacing={6} direction={["column", "row"]}> <Button bg={"blue.400"} color={"white"} w="full" _hover={{ bg: "blue.500", }} type="submit" > Submit </Button> </Stack> </Stack> </Flex> </form> ); }; export default EditProfile;
Следующее, что я собираюсь сделать, это переместить состояние profile
в наш контекст аутентификации, чтобы сделать его глобальным, и создать функцию updateAttribute
.
Затем я перемещу состояние profile
в наш контекст Auth
, чтобы сделать его глобально доступным для всего приложения. Дополнительно я создам функцию updateAttribute
, которая будет отвечать за обновление атрибута пользователя. Это позволит нам легко изменить свойство имени пользователя.
// Auth.tsx // 1. Add profile state const [profileInfo, setProfileInfo] = useState<State["profileInfo"]>({ username: "", email: "", }); // 2. Set profile state after successfull authentication const getSession: State["getSession"] = useCallback(async () => { // ...code above setProfileInfo({ username: attributes.name, email: attributes.email, }); // ...code below }); }, [user]); // 3. Update user attributes const updateUserAttribute = useCallback( ({ Name, Value }: { Name: string; Value: string }) => { const attributes = [ new CognitoUserAttribute({ Name, Value, }), ]; user?.updateAttributes(attributes, (error, result) => { if (error) { toast({ title: error.message, status: "error", isClosable: true, }); } else { // 4. Call get session for updating our profile state globally getSession(); toast({ title: result, status: "success", isClosable: true, }); } }); }, [getSession, user, toast] );
Теперь, когда у нас есть готовая функция updateUserAttribute
, давайте используем ее в нашем компоненте EditProfile
для обновления username
:
// EditProfile.tsx const EditProfile = () => { // 1. Get state and function from the context const { updateUserAttribute, profileInfo } = useAuth(); const [username, setUsername] = useState(""); const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); // 2. call function with new `username` updateUserAttribute({ Name: "name", Value: username }); }; return ( <form onSubmit={handleSubmit}> <Flex align={"center"} justify={"center"}> <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" }}> Edit profile </Heading> <FormControl id="password"> <FormLabel>New user name</FormLabel> <Input placeholder={profileInfo?.username} // make current username as a placeholder _placeholder={{ color: "gray.500" }} type="text" value={username} onChange={(event) => setUsername(event.target.value)} /> </FormControl> <Stack spacing={6} direction={["column", "row"]}> <Button bg={"blue.400"} color={"white"} w="full" _hover={{ bg: "blue.500", }} type="submit" > Submit </Button> </Stack> </Stack> </Flex> </form> ); }; export default EditProfile;
После отправки нового имени пользователя в форме EditProfile
мы должны увидеть обновленное имя пользователя, отраженное в нашей форме Profile
. Это позволит пользователям сразу увидеть внесенные ими изменения.
Заключение
Поздравляем! Вы многого добились в этой серии. Мы рассмотрели различные аспекты, связанные с аутентификацией, включая управление сеансом и манипулирование атрибутами пользователя. Надеюсь, вы нашли эти темы интересными и полезными при создании приложения для аутентификации. Если у вас есть какие-либо вопросы или вам нужна дополнительная помощь, не стесняйтесь обращаться к нам. Молодец, что завершил это путешествие!
Идеи для самостоятельной реализации
- Добавьте проверку на стороне клиента с помощью
react-hook-form
. - Добавьте
react-router-dom
и соедините все компоненты с помощьюrouter