Partilhar via


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

Aplica-se a: círculo verde com um símbolo de marca de seleção branco que indica que o conteúdo a seguir se aplica a locatários externos. Inquilinos externos (saiba mais)

Neste tutorial, você aprenderá a criar um aplicativo de página única do React que inscreve usuários usando autenticação nativa.

Neste tutorial, você:

  • Crie um projeto React.
  • Adicione componentes da interface do usuário do aplicativo.
  • Configure o projeto para inscrever o usuário usando nome de usuário (e-mail) e senha.

Pré-requisitos

Criar um projeto React e instalar dependências

Em um local de escolha em seu computador, execute os seguintes comandos para criar um novo projeto React com o nome reactspa, navegue até a pasta do projeto e instale pacotes:

npm config set legacy-peer-deps true
npx create-react-app reactspa --template typescript
cd reactspa
npm install ajv
npm install react-router-dom
npm install

Adicionar arquivo de configuração para seu aplicativo

Crie um arquivo chamado src/config.jse, em seguida, adicione o seguinte código:

// App Id obatained from the Microsoft Entra portal 
export const CLIENT_ID = "Enter_the_Application_Id_Here";

// URL of the CORS proxy server
const BASE_API_URL = `http://localhost:3001/api`;

// Endpoints URLs for Native Auth APIs
export const ENV = {
    urlSignupStart: `${BASE_API_URL}/signup/v1.0/start`,
    urlSignupChallenge: `${BASE_API_URL}/signup/v1.0/challenge`,
    urlSignupContinue: `${BASE_API_URL}/signup/v1.0/continue`,
}
  • Encontre o valor Enter_the_Application_Id_Here e substitua-o pelo Application ID (clientId) do aplicativo que você registrou no centro de administração do Microsoft Entra.

  • O BASE_API_URL aponta para um servidor proxy CORS (Cross-Origin Resource Sharing), que iremos configurar mais tarde nesta série de tutoriais. A API de autenticação nativa não suporta CORS, por isso configuramos um servidor proxy CORS entre o SPA React e a API de autenticação nativa para gerenciar os cabeçalhos CORS.

Configurar o aplicativo React para chamar a API de autenticação nativa e manipular a resposta

Para concluir um fluxo de autenticação, como um fluxo de inscrição, com as APIs de autenticação nativas, o aplicativo faz com que calla dn manipule a resposta. Por exemplo, o aplicativo inicia um fluxo de inscrição e aguarda uma resposta, em seguida, envia atributos de usuário e aguarda novamente até que o usuário seja inscrito com êxito.

Configurar a chamada do cliente para a API de autenticação nativa

Nesta seção, você define como fazer chamadas para a autenticação nativa e manipular respostas:

  1. Crie uma pasta chamada cliente no src.

  2. Crie um arquivo chamado scr/client/RequestClient.ts e adicione o seguinte trecho de código:

    import { ErrorResponseType } from "./ResponseTypes";
    
    export const postRequest = async (url: string, payloadExt: any) => {
    const body = new URLSearchParams(payloadExt as any);
    
    const response = await fetch(url, {
        method: "POST",
        headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        },
        body,
    });
    
    if (!response.ok) {
        try {
        const errorData: ErrorResponseType = await response.json();
        throw errorData;
        } catch (jsonError) {
        const errorData = {
            error: response.status,
            description: response.statusText,
            codes: [],
            timestamp: "",
            trace_id: "",
            correlation_id: "",
        };
        throw errorData;
        }
    }
    
    return await response.json();
    };
    

    Esse código define como o aplicativo faz chamadas para a API de autenticação nativa e como lidar com as respostas. Sempre que o aplicativo precisa iniciar um fluxo de autenticação, ele usa a função postRequest especificando a URL e os dados de carga.

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

Durante o fluxo de inscrição, o aplicativo faz várias chamadas para a API de autenticação nativa.

Para definir essas chamadas, crie um arquivo chamado scr/client/RequestTypes.ts e adicione o seguinte trecho de código:

    //SignUp 
    export interface SignUpStartRequest {
        client_id: string;
        username: string;
        challenge_type: string;
        password?: string;
        attributes?: Object;
    }
    
    export interface SignUpChallengeRequest {
        client_id: string;
        continuation_token: string;
        challenge_type?: string;
    }
    
    export interface SignUpFormPassword {
        name: string;
        surname: string;
        username: string;
        password: string;
    }
    
    //OTP
    export interface ChallengeForm {
        continuation_token: string;
        oob?: string;
        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 inscrição, crie um arquivo chamado src/client/ResponseTypes.ts e adicione o seguinte trecho de código:

    export interface SuccessResponseType {
    continuation_token?: string;
    challenge_type?: string;
    }
    
    export interface ErrorResponseType {
        error: string;
        error_description: string;
        error_codes: number[];
        timestamp: string;
        trace_id: string;
        correlation_id: string;
    }
        
    export interface ChallengeResponse {
        binding_method: string;
        challenge_channel: string;
        challenge_target_label: string;
        challenge_type: string;
        code_length: number;
        continuation_token: string;
        interval: number;
    }

Processar os pedidos de inscrição

Nesta seção, você adiciona código que processa solicitações de fluxo de inscrição. Exemplos dessas solicitações são iniciar um fluxo de inscrição, selecionar um método de autenticação e enviar uma senha única.

Para fazer isso, crie um arquivo chamado src/client/SignUpService.ts e adicione o seguinte trecho de código:

import { CLIENT_ID, ENV } from "../config";
import { postRequest } from "./RequestClient";
import { ChallengeForm, SignUpChallengeRequest, SignUpFormPassword, SignUpStartRequest } from "./RequestTypes";
import { ChallengeResponse } from "./ResponseTypes";

//handle start a sign-up flow
export const signupStart = async (payload: SignUpFormPassword) => {
const payloadExt: SignUpStartRequest = {
    attributes: JSON.stringify({
    given_name: payload.name,
    surname: payload.surname,
    }),
    username: payload.username,
    password: payload.password,
    client_id: CLIENT_ID,
    challenge_type: "password oob redirect",
};

return await postRequest(ENV.urlSignupStart, payloadExt);
};

//handle selecting an authentication method
export const signupChallenge = async (payload: ChallengeForm):Promise<ChallengeResponse> => {
    const payloadExt: SignUpChallengeRequest = {
        client_id: CLIENT_ID,
        challenge_type: "password oob redirect",
        continuation_token: payload.continuation_token,
    };

    return await postRequest(ENV.urlSignupChallenge, payloadExt);
};

//handle submit one-time passcode
export const signUpSubmitOTP = async (payload: ChallengeForm) => {
    const payloadExt = {
        client_id: CLIENT_ID,
        continuation_token: payload.continuation_token,
        oob: payload.oob,
        grant_type: "oob",
    };

    return await postRequest(ENV.urlSignupContinue, payloadExt);
};

A propriedade challenge_type mostra os métodos de autenticação suportados pelo aplicativo cliente. Este aplicativo autentica-se utilizando e-mail com senha, por isso o valor do tipo de desafio é redirecionamento de senha oob. Leia mais sobre os tipos de desafios .

Criar componentes da interface do usuário

Este aplicativo coleta detalhes do usuário, como nome próprio, nome de usuário (e-mail) e senha e uma senha única do usuário. Portanto, o aplicativo precisa ter uma inscrição e um formulário único de coleta de senha.

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

  2. Para criar, exibir e enviar o formulário de inscrição, crie um arquivo src/pages/SignUp/SignUp.tsx e adicione o seguinte código:

        import React, { useState } from 'react';
        import { signupChallenge, signupStart } from '../../client/SignUpService';
        import { useNavigate } from 'react-router-dom';
        import { ErrorResponseType } from "../../client/ResponseTypes";
    
        export const SignUp: React.FC = () => {
            const [name, setName] = useState<string>('');
            const [surname, setSurname] = useState<string>('');
            const [email, setEmail] = useState<string>('');
            const [error, setError] = useState<string>('');
            const [isLoading, setIsloading] = useState<boolean>(false);
            const navigate = useNavigate();
            const validateEmail = (email: string): boolean => {
              const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
              return re.test(String(email).toLowerCase());
            };
    
            const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
              e.preventDefault();
              if (!name || !surname || !email) {
                setError('All fields are required');
                return;
              }
              if (!validateEmail(email)) {
                setError('Invalid email format');
                return;
              }
              setError('');
              try {
                setIsloading(true);
                const res1 = await signupStart({ name, surname, username: email, password });
                const res2 = await signupChallenge({ continuation_token: res1.continuation_token });
                navigate('/signup/challenge', { state: { ...res2} });
              } catch (err) {
                setError("An error occurred during sign up " + (err as ErrorResponseType).error_description);
              } finally {
                setIsloading(false);
              }
            };
    
            return (
              <div className="sign-up-form">
                <form onSubmit={handleSubmit}>
                  <h2>Sign Up</h2>
                  <div className="form-group">
                    <label>Name:</label>
                    <input
                      type="text"
                      value={name}
                      onChange={(e) => setName(e.target.value)}
                      required
                    />
                  </div>
                  <div className="form-group">
                    <label>Last Name:</label>
                    <input
                      type="text"
                      value={surname}
                      onChange={(e) => setSurname(e.target.value)}
                      required
                    />
                  </div>
                  <div className="form-group">
                    <label>Email:</label>
                    <input
                      type="email"
                      value={email}
                      onChange={(e) => setEmail(e.target.value)}
                      required
                    />
                  </div>
                  {error && <div className="error">{error}</div>}
                  {isLoading && <div className="warning">Sending request...</div>}
                  <button type="submit">Sign Up</button>
                </form>
              </div>
            );
          };
    
  3. Para criar, exibir e enviar o formulário de senha única, crie um arquivo src/pages/signup/SignUpChallenge.tsx e adicione o seguinte código:

    import React, { useState } from "react";
    import { useNavigate, useLocation } from "react-router-dom";
    import { signUpSubmitOTP } from "../../client/SignUpService";
    import { ErrorResponseType } from "../../client/ResponseTypes";
    
    export const SignUpChallenge: React.FC = () => {
      const { state } = useLocation();
      const navigate = useNavigate();
      const { challenge_target_label, challenge_type, continuation_token, code_length } = state;
    
      const [code, setCode] = useState<string>("");
      const [error, setError] = useState<string>("");
      const [isLoading, setIsloading] = useState<boolean>(false);
    
      const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        if (!code) {
          setError("All fields are required");
          return;
        }
    
        setError("");
        try {
          setIsloading(true);
          const res = await signUpSubmitOTP({ continuation_token, oob: code });
          navigate("/signup/completed");
        } catch (err) {
          setError("An error occurred during sign up " + (err as ErrorResponseType).error_description);
        } finally {
          setIsloading(false);
        }
      };
    
      return (
        <div className="sign-up-form">
          <form onSubmit={handleSubmit}>
            <h2>Insert your one time code received at {challenge_target_label}</h2>
            <div className="form-group">
              <label>Code:</label>
              <input maxLength={code_length} type="text" value={code} onChange={(e) => setCode(e.target.value)} required />
            </div>
            {error && <div className="error">{error}</div>}
            {isLoading && <div className="warning">Sending request...</div>}
            <button type="submit">Sign Up</button>
          </form>
        </div>
      );
    };
    
  4. Crie um arquivo src/pages/signup/SignUpCompleted.tsx e adicione o seguinte código:

    import React from 'react';
    import { Link } from 'react-router-dom';
    
    export const SignUpCompleted: React.FC = () => {
      return (
        <div className="sign-up-completed">
          <h2>Sign Up Completed</h2>
          <p>Your sign-up process is complete. You can now log in.</p>
          <Link to="/signin" className="login-link">Go to Login</Link>
        </div>
      );
    };
    

    Esta página exibe uma mensagem de sucesso e um botão para levar o usuário à página de entrada depois que ele se inscrever com êxito.

  5. Abra o arquivo src/App.tsx e substitua seu conteúdo pelo seguinte código:

    import React from "react";
    import { BrowserRouter, Link } from "react-router-dom";
    import "./App.css";
    import { AppRoutes } from "./AppRoutes";
    
    function App() {
      return (
        <div className="App">
          <BrowserRouter>
            <header>
              <nav>
                <ul>
                  <li>
                    <Link to="/signup">Sign Up</Link>
                  </li>
                  <li>
                    <Link to="/signin">Sign In</Link>
                  </li>
                  <li>
                    <Link to="/reset">Reset Password</Link>
                  </li>
                </ul>
              </nav>
            </header>
            <AppRoutes />
          </BrowserRouter>
        </div>
      );
    }
    
    export default App;
    
  6. Para exibir o aplicativo React corretamente:

    1. Abra o arquivo src/App.css e adicione a seguinte propriedade na App-header classe:

      min-height: 100vh;
      
    2. Abra o ficheiro src/Index.css e, em seguida, substitua o seu conteúdo pelo código de src/index.css

Adicionar rotas de aplicativos

Crie um arquivo chamado src/AppRoutes.tsx e adicione o seguinte código:

import { Route, Routes } from "react-router-dom";
import { SignUp } from "./pages/SignUp/SignUp";
import { SignUpChallenge } from "./pages/SignUp/SignUpChallenge";
import { SignUpCompleted } from "./pages/SignUp/SignUpCompleted";

export const AppRoutes = () => {
  return (
    <Routes>
      <Route path="/" element={<SignUp />} />
      <Route path="/signup" element={<SignUp />} />
      <Route path="/signup/challenge" element={<SignUpChallenge />} />
      <Route path="/signup/completed" element={<SignUpCompleted />} />
   
    </Routes>
  );
};

Neste ponto, seu aplicativo React pode enviar solicitações de inscrição para a API de autenticação nativa, mas precisamos configurar o servidor proxy CORS para gerenciar os cabeçalhos CORS.

Próximo passo