Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Se aplica a:
Inquilinos externos (más información)
En este tutorial, aprenderá a crear una aplicación de página única de React que registra a los usuarios mediante la autenticación nativa.
En este tutorial, harás lo siguiente:
- Cree un proyecto de React.
- Agregue componentes de interfaz de usuario de la aplicación.
- Configure el proyecto para registrarse con el nombre de usuario (correo electrónico) y la contraseña.
Prerrequisitos
- Complete los pasos descritos en Inicio rápido: Inicio de sesión de usuarios en una aplicación de página única de React de ejemplo mediante la API de autenticación nativa. En este inicio rápido se muestra cómo preparar el inquilino externo y ejecutar un ejemplo de código de React.
- Visual Studio Code u otro editor de código.
- Node.js.
Creación de un proyecto de React e instalación de dependencias
En una ubicación que prefiera en el equipo, ejecute los siguientes comandos para crear un nuevo proyecto de React con el nombre reactspa, vaya a la carpeta del proyecto y, a continuación, instale paquetes:
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
Adición de un archivo de configuración para la aplicación
Cree un archivo denominado src/config.jsy agregue el código siguiente:
// 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`,
}
Busque el
Enter_the_Application_Id_Herevalor y reemplácelo por el Id. de aplicación(clientId) de la aplicación que registró en el Centro de administración de Microsoft Entra.El
BASE_API_URLapunta a un servidor proxy de uso compartido de recursos entre orígenes (CORS), que configuraremos más adelante en esta serie. La API de autenticación nativa no admite CORS, por lo que configuramos un servidor proxy CORS entre react SPA y la API de autenticación nativa para administrar los encabezados de CORS.
Configuración de la aplicación React para llamar a la API de autenticación nativa y controlar la respuesta
Para completar un flujo de autenticación, como un flujo de registro, con las API de autenticación nativas, la aplicación realiza una llamada y gestiona la respuesta. Por ejemplo, la aplicación inicia un flujo de registro y espera una respuesta y envía atributos de usuario y espera de nuevo hasta que el usuario se registre correctamente.
Configuración de la llamada de cliente a la API de autenticación nativa
En esta sección, definirá cómo realizar llamadas a la autenticación nativa y controlar las respuestas:
Cree una carpeta denominada cliente en el src.
Cree un archivo denominado scr/client/RequestClient.tsy agregue el siguiente fragmento 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(); };Este código define cómo realiza la aplicación llamadas a la API de autenticación nativa y controla las respuestas. Siempre que la aplicación necesite iniciar un flujo de autenticación, usa la función
postRequestespecificando la dirección URL y los datos de carga.
Definición de tipos de llamadas que realiza la aplicación a la API de autenticación nativa
Durante el flujo de registro, la aplicación realiza varias llamadas a la API de autenticación nativa.
Para definir estas llamadas, cree un archivo denominado scr/client/RequestTypes.tsy agregue el siguiente fragmento 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;
}
Definición del tipo de respuestas que la aplicación recibe de la API de autenticación nativa
Para definir el tipo de respuestas que la aplicación puede recibir de la API de autenticación nativa para la operación de registro, cree un archivo denominado src/client/ResponseTypes.tsy agregue el siguiente fragmento 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;
}
Procesamiento de las solicitudes de registro
En esta sección, agregará código que procesa las solicitudes del flujo de registro. Algunos ejemplos de estas solicitudes son iniciar un flujo de registro, seleccionar un método de autenticación y enviar un código de acceso de un solo uso.
Para ello, cree un archivo denominado src/client/SignUpService.tsy agregue el siguiente fragmento 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);
};
La propiedad challenge_type muestra los métodos de autenticación que admite la aplicación cliente. Esta aplicación utiliza el correo electrónico con contraseña para iniciar sesión, por lo que el valor del tipo de desafío es redirección de contraseña fuera de banda. Lea más sobre los tipos de desafío .
Creación de componentes de interfaz de usuario
Esta aplicación recopila detalles del usuario, como el nombre, el nombre de usuario (correo electrónico) y la contraseña y un código de acceso de un solo uso del usuario. Así, la aplicación debe tener un formulario de registro y de recopilación de código de acceso de un solo uso.
Cree una carpeta denominada /pages/SignUp en la carpeta src.
Para crear, mostrar y enviar el formulario de registro, cree un archivo src/pages/SignUp/SignUp.tsxy agregue el código siguiente:
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 crear, mostrar y enviar el formulario de código de acceso de un solo uso, cree un archivo src/pages/signup/SignUpChallenge.tsxy agregue el código siguiente:
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> ); };Cree un archivo src/pages/signup/SignUpCompleted.tsxy agregue el código siguiente:
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> ); };En esta página se muestra un mensaje de éxito y un botón para llevar al usuario a la página de inicio de sesión después de registrarse con éxito.
Abra el archivo src/App.tsx y reemplace su contenido por el código siguiente:
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 mostrar la aplicación React correctamente:
Abra el archivo src/App.css y agregue la siguiente propiedad en la clase
App-header:min-height: 100vh;Abra el archivo src/Index.css de y reemplace su contenido por código de src/index.css
Adición de rutas de aplicación
Cree un archivo denominado src/AppRoutes.tsxy agregue el código siguiente:
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>
);
};
En este momento, la aplicación React puede enviar solicitudes de registro a la API de autenticación nativa, pero es necesario configurar el servidor proxy CORS para administrar los encabezados de CORS.