Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Применяется:
Внешние клиенты (дополнительные сведения)
В этом руководстве объясняется, как сбросить пароль в одностраничном приложении React (SPA) с помощью встроенной аутентификации.
В этом руководстве вы:
- Обновите приложение React, чтобы сбросить пароль пользователя.
- Тестовый процесс сброса пароля
Необходимые условия
- Выполните действия, описанные в руководстве по . Настройка прокси-сервера CORS для управления заголовками CORS для собственной проверки подлинности.
Определение типов вызовов приложения в собственный API проверки подлинности
Во время потока сброса пароля приложение выполняет несколько вызовов к собственному API проверки подлинности, таким как инициирование запроса сброса пароля и отправка формы сброса пароля.
Чтобы определить эти вызовы, откройте файл scr/client/RequestTypes.ts, а затем добавьте следующий фрагмент кода:
export interface ResetPasswordStartRequest {
username: string;
challenge_type: string;
client_id: string;
}
export interface ResetPasswordSubmitRequest {
client_id: string;
continuation_token: string;
new_password: string;
}
export interface ResetPasswordSubmitForm {
continuation_token: string;
new_password: string;
}
Определите типы ответов, которые приложение получает от нативного API аутентификации.
Чтобы определить тип ответов, которые приложение может получить из собственного API проверки подлинности для операции сброса пароля, откройте файл src/client/ResponseTypes.ts, а затем добавьте следующий фрагмент кода:
export interface ChallengeResetResponse {
continuation_token: string;
expires_in: number;
}
export interface ResetPasswordSubmitResponse {
continuation_token: string;
poll_interval: number;
}
Обработка запросов на сброс пароля
В этом разделе вы добавите код, обрабатывающий запросы на процесс сброса пароля. Примерами этих запросов являются инициирование запроса сброса пароля и отправка формы сброса пароля.
Для этого создайте файл с именем src/client/ResetPasswordService.ts, а затем добавьте следующий фрагмент кода:
import { CLIENT_ID, ENV } from "../config";
import { postRequest } from "./RequestClient";
import { ChallengeForm, ChallengeRequest, ResetPasswordStartRequest, ResetPasswordSubmitForm, ResetPasswordSubmitRequest } from "./RequestTypes";
import { ChallengeResetResponse, ChallengeResponse, ResetPasswordSubmitResponse } from "./ResponseTypes";
export const resetStart = async ({ username }: { username: string }) => {
const payloadExt: ResetPasswordStartRequest = {
username,
client_id: CLIENT_ID,
challenge_type: "password oob redirect",
};
return await postRequest(ENV.urlResetPwdStart, payloadExt);
};
export const resetChallenge = async ({ continuation_token }: { continuation_token: string }): Promise<ChallengeResponse> => {
const payloadExt: ChallengeRequest = {
continuation_token,
client_id: CLIENT_ID,
challenge_type: "oob redirect",
};
return await postRequest(ENV.urlResetPwdChallenge, payloadExt);
};
export const resetSubmitOTP = async (payload: ChallengeForm): Promise<ChallengeResetResponse> => {
const payloadExt = {
client_id: CLIENT_ID,
continuation_token: payload.continuation_token,
oob: payload.oob,
grant_type: "oob",
};
return await postRequest(ENV.urlResetPwdContinue, payloadExt);
};
export const resetSubmitNewPassword = async (payload: ResetPasswordSubmitForm): Promise<ResetPasswordSubmitResponse> => {
const payloadExt: ResetPasswordSubmitRequest = {
client_id: CLIENT_ID,
continuation_token: payload.continuation_token,
new_password: payload.new_password,
};
return await postRequest(ENV.urlResetPwdSubmit, payloadExt);
};
export const resetPoll = async (continuation_token: string): Promise<ChallengeResetResponse> => {
const payloadExt = {
client_id: CLIENT_ID,
continuation_token,
};
return await postRequest(ENV.urlResetPwdPollComp, payloadExt);
};
Свойство challenge_type показывает методы проверки подлинности, поддерживаемые клиентским приложением. Узнайте больше о типах вызовов.
Создание компонентов пользовательского интерфейса
Во время потока сброса пароля это приложение на разных экранах собирает имя пользователя (электронная почта), одноразовый секретный код и новый пароль пользователя.
Создайте папку с именем /pages/resetpassword в папке src.
Чтобы создать, отобразить и отправить формы сброса пароля, создайте файл src/pages/resetpassword/ResetPassword.tsx, а затем добавьте следующий код:
// ResetPassword.tsx import React, { useState } from "react"; import { resetChallenge, resetStart, resetSubmitNewPassword, resetSubmitOTP } from "../../client/ResetPasswordService"; import { ChallengeResetResponse, ChallengeResponse, ErrorResponseType } from "../../client/ResponseTypes"; export const ResetPassword: React.FC = () => { const [username, setUsername] = useState<string>(""); const [otp, setOTP] = useState<string>(""); const [newPassword, setNewPassword] = useState<string>(""); const [error, setError] = useState<string>(""); const [step, setStep] = useState<number>(1); const [isLoading, setIsloading] = useState<boolean>(false); const [tokenRes, setTokenRes] = useState<ChallengeResponse>({ binding_method: "", challenge_channel: "", challenge_target_label: "", challenge_type: "", code_length: 0, continuation_token: "", interval: 0, }); const [otpRes, setOTPRes] = useState<ChallengeResetResponse>({ expires_in: 0, continuation_token: "", }); const handleResetPassword = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!username) { setError("Username is required"); return; } setError(""); try { setIsloading(true); const res1 = await resetStart({ username }); const tokenRes = await resetChallenge({ continuation_token: res1.continuation_token }); setTokenRes(tokenRes); setStep(2); } catch (err) { setError("An error occurred during password reset " + (err as ErrorResponseType).error_description); } finally { setIsloading(false); } }; const handleSubmitCode = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!otp) { setError("All fields are required"); return; } setError(""); try { setIsloading(true); const res = await resetSubmitOTP({ continuation_token: tokenRes.continuation_token, oob: otp, }); setOTPRes(res); setStep(3); } catch (err) { setError("An error occurred while submitting the otp code " + (err as ErrorResponseType).error_description); } finally { setIsloading(false); } }; const handleSubmitNewPassword = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!newPassword) { setError('All fields are required'); return; } setError(''); try { setIsloading(true); await resetSubmitNewPassword({ continuation_token: otpRes.continuation_token, new_password: newPassword, }); setStep(4); } catch (err) { setError("An error occurred while submitting the new password " + (err as ErrorResponseType).error_description); } finally { setIsloading(false); } }; return ( <div className="reset-password-form"> //collect username to initiate password reset flow {step === 1 && ( <form onSubmit={handleResetPassword}> <h2>Reset Password</h2> <div className="form-group"> <label>Username:</label> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} required /> </div> {error && <div className="error">{error}</div>} {isLoading && <div className="warning">Sending request...</div>} <button type="submit">Reset Password</button> </form> )} //collect OTP {step === 2 && ( <form onSubmit={handleSubmitCode}> <h2>Submit one time code received via email at {tokenRes.challenge_target_label}</h2> <div className="form-group"> <label>One time code:</label> <input type="text" maxLength={tokenRes.code_length} value={otp} onChange={(e) => setOTP(e.target.value)} required /> </div> {error && <div className="error">{error}</div>} {isLoading && <div className="warning">Sending request...</div>} <button type="submit">Submit code</button> </form> )} //Collect new password {step === 3 && ( <form onSubmit={handleSubmitNewPassword}> <h2>Submit New Password</h2> <div className="form-group"> <label>New Password:</label> <input type="password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} required /> </div> {error && <div className="error">{error}</div>} {isLoading && <div className="warning">Sending request...</div>} <button type="submit">Submit New Password</button> </form> )} //report success after password reset is successful {step === 4 && ( <div className="reset-password-success"> <h2>Password Reset Successful</h2> <p>Your password has been reset successfully. You can now log in with your new password.</p> </div> )} </div> ); };
Добавление маршрутов приложений
Откройте файл src/AppRoutes.tsx, а затем раскомментируйте следующие строки кода:
//uncomment
import { ResetPassword } from "./pages/ResetAccount/ResetPassword";
//...
export const AppRoutes = () => {
return (
<Routes>
//uncomment
<Route path="/reset" element={<ResetPassword />} />
</Routes>
);
};
Запуск и тестирование приложения
Используйте шаги из раздела Запуск и тестирование приложения, чтобы запустить ваше приложение. Однако проверьте поток сброса пароля только с помощью учетной записи пользователя, зарегистрированной ранее.
Связанное содержимое
- настройка обратного прокси-сервера для одностраничного приложения, использующего собственный API проверки подлинности с помощью приложения-функции Azure.
- использовать Azure Front Door в качестве обратного прокси-сервера в производственной среде для одностраничного приложения, использующего нативную аутентификацию.
- справочник по API нативной аутентификации.