Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
S’applique à :
Locataires externes (en savoir plus)
Dans ce tutoriel, vous allez apprendre à créer une application à page unique React qui inscrit des utilisateurs à l’aide de l’authentification native.
Dans ce tutoriel, vous allez :
- Créez un projet React.
- Ajoutez des composants d’interface utilisateur de l’application.
- Configurez le projet pour inscrire l’utilisateur à l’aide du nom d’utilisateur (e-mail) et du mot de passe.
Conditions préalables
- Suivez les étapes de Quickstart : Connexion des utilisateurs dans un exemple d’application React à page unique à l’aide de l’API d’authentification native. Ce guide de démarrage rapide vous montre comment préparer votre locataire externe et exécuter un exemple de code React.
- Visual Studio Code ou un autre éditeur de code.
- Node.js.
Créer un projet React et installer des dépendances
Dans un emplacement de choix dans votre ordinateur, exécutez les commandes suivantes pour créer un projet React avec le nom reactspa, accédez au dossier du projet, puis installez les packages :
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
Ajouter un fichier de configuration pour votre application
Créez un fichier appelé src/config.js, puis ajoutez le code suivant :
// 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`,
}
Recherchez la valeur
Enter_the_Application_Id_Hereet remplacez-la par l’ID d’application (clientId) de l’application que vous avez inscrite dans le Centre d’administration Microsoft Entra.Le
BASE_API_URLpointe vers un serveur proxy de Partage de ressources entre origines (CORS), que nous mettrons en place plus tard dans cette série de tutoriels. L’API d’authentification native ne prend pas en charge CORS. Nous avons donc configuré un serveur proxy CORS entre la spa React et l’API d’authentification native pour gérer les en-têtes CORS.
Configurer l’application React pour appeler l’API d’authentification native et gérer la réponse
Pour compléter un flux d’authentification, tel qu’un flux d’inscription, avec les API d’authentification natives, l’application effectue des appels et gère les réponses. Par exemple, l’application lance un flux d’inscription et attend une réponse, puis envoie des attributs utilisateur et attend à nouveau que l’utilisateur soit correctement inscrit.
Configurer l’appel client vers l’API d’authentification native
Dans cette section, vous allez définir comment effectuer des appels à l’authentification native et gérer les réponses :
Créez un dossier appelé client dans le dossier src.
Créez un fichier appelé scr/client/RequestClient.ts, puis ajoutez l’extrait de code suivant :
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(); };Ce code définit la façon dont l’application effectue des appels à l’API d’authentification native et gère les réponses. Chaque fois que l’application doit lancer un flux d’authentification, elle utilise la fonction
postRequesten spécifiant les données d’URL et de charge utile.
Définir les types d’appels effectués par l’application à l’API d’authentification native
Pendant le flux d’inscription, l’application effectue plusieurs appels à l’API d’authentification native.
Pour définir ces appels, créez un fichier appelé scr/client/RequestTypes.ts, puis ajoutez l’extrait de code suivant :
//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;
}
Définir le type de réponse reçu par l’API d’authentification native
Pour définir le type de réponses que l’application peut recevoir à partir de l’API d’authentification native pour l’opération d’inscription, créez un fichier appelé src/client/ResponseTypes.ts, puis ajoutez l’extrait de code suivant :
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;
}
Traiter les demandes d’inscription
Dans cette section, vous ajoutez du code qui traite les demandes liées au processus d'inscription. Par exemple, ces demandes démarrent un flux d’inscription, sélectionnent une méthode d’authentification et envoient un code secret à usage unique.
Pour ce faire, créez un fichier appelé src/client/SignUpService.ts, puis ajoutez l’extrait de code suivant :
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 propriété challenge_type affiche les méthodes d’authentification que l’application cliente prend en charge. Cette application se connecte en utilisant un e-mail avec mot de passe. Par conséquent, la valeur du type de défi est redirection de mot de passe oob. En savoir plus sur les types de défis .
Créer des composants d’interface utilisateur
Cette application collecte les détails de l’utilisateur, tels que le nom donné, le nom d’utilisateur (e-mail) et le mot de passe et un code secret à usage unique de l’utilisateur. Par conséquent, l’application doit avoir un formulaire d’inscription et un formulaire de collecte de code secret à usage unique.
Créez un dossier appelé /pages/SignUp dans le dossier src.
Pour créer, afficher et envoyer le formulaire d’inscription, créez un fichier src/pages/SignUp/SignUp.tsx, puis ajoutez le code suivant :
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> ); };Pour créer, afficher et soumettre le formulaire de code secret à usage unique, créez un fichier src/pages/signup/SignUpChallenge.tsx, puis ajoutez le code suivant :
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> ); };Créez un fichier src/pages/signup/SignUpCompleted.tsx, puis ajoutez le code suivant :
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> ); };Cette page affiche un message de réussite et un bouton pour que l’utilisateur accède à la page de connexion une fois qu’il s’est inscrit correctement.
Ouvrez le fichier src/App.tsx, puis remplacez son contenu par le code suivant :
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;Pour afficher correctement l’application React :
Ouvrez le fichier src/App.css, puis ajoutez la propriété suivante dans la classe
App-header:min-height: 100vh;Ouvrez le fichier src/Index.css, puis remplacez son contenu par du code de src/index.css
Ajouter des itinéraires d’application
Créez un fichier appelé src/AppRoutes.tsx, puis ajoutez le code suivant :
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>
);
};
À ce stade, votre application React peut envoyer des demandes d’inscription à l’API d’authentification native, mais nous devons configurer le serveur proxy CORS pour gérer les en-têtes CORS.