Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Dotyczy:
Dzierżawy zewnętrzne (dowiedz się więcej)
Z tego samouczka dowiesz się, jak zresetować hasło w aplikacji jednostronicowej React (SPA) przy użyciu uwierzytelniania natywnego.
W tym samouczku:
- Zaktualizuj aplikację React, aby zresetować hasło użytkownika.
- Testowanie przepływu resetowania hasła
Warunki wstępne
- Wykonaj kroki opisane w Samouczek: skonfiguruj serwer proxy dla CORS, aby zarządzać nagłówkami CORS na potrzeby natywnego uwierzytelniania.
Definiowanie typów wywołań aplikacji do natywnego interfejsu API uwierzytelniania
Podczas przepływu resetowania hasła aplikacja wykonuje wiele wywołań interfejsu API uwierzytelniania natywnego, takich jak inicjowanie żądania resetowania hasła i przesyłanie formularza resetowania hasła.
Aby zdefiniować te wywołania, otwórz plik scr/client/RequestTypes.ts, a następnie dołącz następujący fragment kodu:
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;
}
Definiowanie typu odpowiedzi odbieranych przez aplikację z natywnego interfejsu API uwierzytelniania
Aby zdefiniować typ odpowiedzi, które aplikacja może odbierać z natywnego interfejsu API uwierzytelniania dla operacji resetowania hasła, otwórz plik src/client/ResponseTypes.ts, a następnie dołącz następujący fragment kodu:
export interface ChallengeResetResponse {
continuation_token: string;
expires_in: number;
}
export interface ResetPasswordSubmitResponse {
continuation_token: string;
poll_interval: number;
}
Przetwarzanie żądań resetowania hasła
W tej sekcji dodasz kod, który przetwarza żądania resetowania hasła. Przykładami takich żądań są rozpoczęcie resetowania hasła i przesłanie formularza resetowania hasła.
W tym celu utwórz plik o nazwie src/client/ResetPasswordService.ts, a następnie dodaj następujący fragment kodu:
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);
};
Właściwość challenge_type pokazuje metody uwierzytelniania obsługiwane przez aplikację kliencka. Przeczytaj więcej na temat typów wyzwań .
Tworzenie składników interfejsu użytkownika
Podczas przepływu resetowania hasła ta aplikacja na różnych ekranach zbiera nazwę użytkownika (adres e-mail), jednorazowy kod dostępu i nowe hasło użytkownika.
Utwórz folder o nazwie /pages/resetpassword w folderze src.
Aby utworzyć, wyświetlić i przesłać formularze resetowania hasła, utwórz plik src/pages/resetpassword/ResetPassword.tsx, a następnie dodaj następujący kod:
// 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> ); };
Dodawanie tras aplikacji
Otwórz plik src/AppRoutes.tsx, a następnie odkomentuj następujące wiersze kodu:
//uncomment
import { ResetPassword } from "./pages/ResetAccount/ResetPassword";
//...
export const AppRoutes = () => {
return (
<Routes>
//uncomment
<Route path="/reset" element={<ResetPassword />} />
</Routes>
);
};
Uruchamianie i testowanie aplikacji
Aby uruchomić aplikację, wykonaj kroki opisane w Uruchom i przetestuj aplikację. Jednak przetestuj przepływ resetowania hasła tylko przy użyciu utworzonego wcześniej konta użytkownika.
Powiązana zawartość
- Skonfiguruj zwrotny serwer proxy dla jednostronicowej aplikacji korzystającej z natywnego interfejsu API uwierzytelniania, używając aplikacji funkcji Azure.
- Użyj usługi Azure Front Door jako zwrotnego serwera proxy w środowisku produkcyjnym dla aplikacji jednostronicowej korzystającej z natywnego uwierzytelniania.
- Dokumentacja API uwierzytelniania natywnego.