Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Aplica-se a: Locatários de força de trabalho
Locatários externos (saiba mais)
Neste tutorial, você aprenderá a criar um aplicativo de página única react que inscreve usuários usando a autenticação nativa.
Neste tutorial, você:
- Crie um projeto do React.
- Adicione componentes de interface do usuário do aplicativo.
- Configure o projeto para inscrever o usuário usando o nome de usuário (email) e a senha.
Pré-requisitos
- Conclua as etapas no Início Rápido: conectar usuários em um aplicativo de página única do React de exemplo usando a API de autenticação nativa. Esse início rápido mostra como preparar seu locatário externo e executar um exemplo de código React.
- Visual Studio Code ou qualquer outro editor de código.
- Node.js.
Criar um projeto do React e instalar dependências
Em um local de escolha no computador, execute os seguintes comandos para criar um novo projeto react com o nome reactspa, navegue até a pasta do projeto e instale os pacotes:
npm config set legacy-peer-deps true
npx create-react-app reactspa --template typescript
cd reactspa
npm install ajv
npm installreact-router-dom
npm install
Adicionar arquivo de configuração para seu aplicativo
Crie um arquivo chamado src/config.jse 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`,
}
Localize o valor
Enter_the_Application_Id_Here
e substitua-o pelo ID do Aplicativo (clientId) do aplicativo registrado no Centro de Administração do Microsoft Entra.O
BASE_API_URL
aponta para um servidor proxy , que é usado para compartilhamento de recursos entre origens (CORS), o qual configuramos mais adiante nesta série de tutoriais. A API de autenticação nativa não dá suporte ao CORS, portanto, configuramos um servidor proxy CORS entre o REACT SPA 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 nativa, o aplicativo fará chamadas e manipulará a resposta. Por exemplo, o aplicativo inicia um fluxo de inscrição e aguarda uma resposta e, em seguida, envia atributos de usuário e aguarda novamente até que o usuário seja inscrito com êxito.
Configurar a chamada de cliente para a API de autenticação nativa
Nesta seção, você define como fazer chamadas para a autenticação nativa e lidar com as respostas:
Crie uma pasta chamada client em src.
Crie um arquivo chamado scr/client/RequestClient.tse adicione o seguinte snippet 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 manipulando as respostas. Sempre que o aplicativo precisa iniciar um fluxo de autenticação, ele usa a função
postRequest
especificando os dados de URL e conteúdo.
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.tse adicione o seguinte snippet 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 resposta 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.tse adicione o seguinte snippet 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 solicitações de inscrição
Nesta seção, você adicionará um código que processa solicitações de fluxo de inscrição. Exemplos dessas solicitações estão iniciando um fluxo de inscrição, selecionando um método de autenticação e enviando uma senha única.
Para fazer isso, crie um arquivo chamado src/client/SignUpService.tse adicione o seguinte snippet 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 aos quais o aplicativo cliente dá suporte. Esse aplicativo entra usando email com senha, portanto, o valor do tipo de desafio é redirecionamento OOB de senha. Leia mais sobre tipos de desafios .
Criar componentes de interface do usuário
Esse aplicativo coleta detalhes do usuário, como nome, sobrenome (email) e senha e uma senha única do usuário. Portanto, o aplicativo precisa ter uma inscrição e um formulário de coleta de senha única.
Crie uma pasta chamada /pages/SignUp na pasta src.
Para criar, exibir e enviar o formulário de inscrição, crie um arquivo src/pages/SignUp/SignUp.tsxe 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> ); };
Para criar, exibir e enviar o formulário de senha única, crie um arquivo src/pages/signup/SignUpChallenge.tsxe 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> ); };
Crie um arquivo src/pages/signup/SignUpCompleted.tsxe 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 êxito e um botão para levar o usuário para a página de entrada depois que ele se inscrever com êxito.
Abra o arquivo src/App.tsx e substitua o 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;
Para exibir o aplicativo React corretamente:
Abra o arquivo src/App.css e adicione a seguinte propriedade na classe
App-header
:min-height: 100vh;
Abra o arquivo src/Index.css e substitua seu conteúdo pelo código de src/index.css
Adicionar rotas de aplicativo
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.