Compartilhar via


Tutorial: Redefinir senha em um aplicativo de página única do React usando autenticação nativa (visualização)

Aplica-se a: Círculo branco com um símbolo X cinza. Locatários do Workforce Círculo verde com um símbolo de marca de seleção branco. Inquilinos externos (saber mais)

Neste tutorial, você aprenderá a redefinir a senha em um aplicativo de página única (SPA) React usando a autenticação nativa.

Neste tutorial, você:

  • Atualize o aplicativo React para redefinir a senha do usuário.
  • Testar o fluxo de redefinição de senha

Pré-requisitos

Definir tipos de chamadas que o aplicativo faz para a API de autenticação nativa

Durante o fluxo de redefinição de senha, o aplicativo faz várias chamadas para a API de autenticação nativa, como iniciar uma solicitação de redefinição de senha e enviar um formulário de redefinição de senha.

Para definir essas chamadas, abra o arquivo scr/client/RequestTypes.ts e acrescente o seguinte trecho de código:

    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;
    }

Definir o tipo de respostas que o aplicativo recebe da API de autenticação nativa

Para definir o tipo de respostas que o aplicativo pode receber da API de autenticação nativa para a operação de redefinição de senha, abra o arquivo src/client/ResponseTypes.ts e acrescente o seguinte trecho de código:

    export interface ChallengeResetResponse {
        continuation_token: string;
        expires_in: number;
    }

    export interface ResetPasswordSubmitResponse {
        continuation_token: string;
        poll_interval: number;
    }

Processar solicitações de redefinição de senha

Nesta seção, você adiciona código que processa solicitações de fluxo de redefinição de senha. Exemplos dessas solicitações são iniciar uma solicitação de redefinição de senha e enviar um formulário de redefinição de senha.

Para fazer isso, crie um arquivo chamado src/client/ResetPasswordService.tse, em seguida, adicione o seguinte trecho de código:

    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);
    };

A propriedade challenge_type mostra os métodos de autenticação suportados pelo aplicativo cliente. Leia mais sobre os tipos de desafios .

Criar componentes da interface do usuário

Durante o fluxo de redefinição de senha, este aplicativo, em telas diferentes, coleta o nome de usuário (e-mail), uma senha única e uma nova senha de usuário.

  1. Crie uma pasta chamada /pages/resetpassword na pasta src.

  2. Para criar, exibir e enviar os formulários de redefinição de senha, crie um arquivo src/pages/resetpassword/ResetPassword.tsxe, em seguida, adicione o seguinte código:

    // 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>
      );
    };
    

Adicionar rotas de aplicativos

Abra o arquivo src/AppRoutes.tsx e descomente as seguintes linhas de código:

    //uncomment
    import { ResetPassword } from "./pages/ResetAccount/ResetPassword";
    //...
    
    export const AppRoutes = () => {
      return (
        <Routes>
            //uncomment
            <Route path="/reset" element={<ResetPassword />} />
        </Routes>
      );
    };

Executar e testar seu aplicativo

Use as etapas em Executar e testar a sua aplicação para executar a sua aplicação. No entanto, teste o fluxo de redefinição de senha somente usando a conta de usuário que você se inscreveu anteriormente.