Partager via


Tutoriel : Inscrire des utilisateurs dans une application à page unique React à l’aide du Kit de développement logiciel (SDK) JavaScript d’authentification native (préversion)

S’applique à : cercle vert avec un symbole de coche blanc qui indique que le contenu suivant s’applique aux locataires externes. Locataires externes (en savoir plus)

Dans ce tutoriel, vous allez apprendre à créer une application à page unique React qui inscrit des utilisateurs à l’aide du Kit de développement logiciel (SDK) JavaScript de l’authentification native.

Dans ce tutoriel, vous allez :

  • Créez un projet React Next.js.
  • Ajoutez le Kit de développement logiciel (SDK) MSAL JS à celui-ci.
  • Ajoutez des composants d’interface utilisateur de l’application.
  • Configurez le projet pour inscrire des utilisateurs.

Conditions préalables

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 :

npx create-next-app@latest
cd reactspa
npm install

Une fois que vous avez correctement exécuté les commandes, vous devez disposer d’une application avec la structure suivante :

spasample/
└──node_modules/
   └──...
└──public/
   └──...
└──src/
   └──app/
      └──favicon.ico
      └──globals.css
      └──page.tsx
      └──layout.tsx
└──postcss.config.mjs
└──package-lock.json
└──package.json
└──tsconfig.json
└──README.md
└──next-env.d.ts
└──next.config.ts

Ajouter le Kit de développement logiciel (SDK) JavaScript à votre projet

Pour utiliser le Kit de développement logiciel (SDK) JavaScript d’authentification native dans votre application, utilisez votre terminal pour l’installer à l’aide de la commande suivante :

npm install @azure/msal-browser

Les fonctionnalités d’authentification native font partie de la azure-msal-browser bibliothèque. Pour utiliser les fonctionnalités d’authentification native, vous importez à partir de @azure/msal-browser/custom-auth. Par exemple:

  import CustomAuthPublicClientApplication from "@azure/msal-browser/custom-auth";

Ajouter une configuration du client

Dans cette section, vous définissez une configuration pour l’application cliente publique d’authentification native pour lui permettre d’interagir avec l’interface du Kit de développement logiciel (SDK). Pour ce faire, créez un fichier appelé src/config/auth-config.ts, puis ajoutez le code suivant :

export const customAuthConfig: CustomAuthConfiguration = {
  customAuth: {
    challengeTypes: ["password", "oob", "redirect"],
    authApiProxyUrl: "http://localhost:3001/api",
  },
  auth: {
    clientId: "Enter_the_Application_Id_Here",
    authority: "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com",
    redirectUri: "/",
    postLogoutRedirectUri: "/",
    navigateToLoginRequestUrl: false,
  },
  cache: {
    cacheLocation: "sessionStorage",
  },
  system: {
    loggerOptions: {
      loggerCallback: (
        level: LogLevel,
        message: string,
        containsPii: boolean
      ) => {
        if (containsPii) {
          return;
        }
        switch (level) {
          case LogLevel.Error:
            console.error(message);
            return;
          case LogLevel.Info:
            console.info(message);
            return;
          case LogLevel.Verbose:
            console.debug(message);
            return;
          case LogLevel.Warning:
            console.warn(message);
            return;
        }
      },
    },
  },
};

Dans le code, recherchez l’espace réservé :

  • Enter_the_Application_Id_Here remplacez-la par l’ID d’application (client) de l’application que vous avez inscrite précédemment.

  • Enter_the_Tenant_Subdomain_Here remplacez-le par le sous-domaine du locataire dans votre centre d’administration Microsoft Entra. Par exemple, si le domaine principal de votre locataire est contoso.onmicrosoft.com, utilisez contoso. Si vous ne disposez pas du nom de votre locataire, découvrez de quelle manière consulter les détails de votre locataire.

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), le mot de passe et un code secret à usage unique de l’utilisateur. Par conséquent, l’application doit avoir un formulaire qui collecte ces informations.

  1. Créez un dossier appelé src/app/sign-up dans le dossier src .

  2. Créez le fichier sign-up/components/InitialForm.tsx, puis collez le code de sign-up/components/InitialForm.tsx. Ce composant affiche un formulaire qui collecte les attributs d’inscription de l’utilisateur.

  3. Créez un fichier d’inscription/components/CodeForm.tsx , puis collez le code de l’inscription/components/CodeForm.tsx. Ce composant affiche un formulaire qui collecte un code secret unique envoyé à l’utilisateur. Vous avez besoin de ce formulaire pour l’e-mail avec mot de passe ou e-mail avec une méthode d’authentification par code secret à usage unique.

  4. Si votre choix de méthode d’authentification est e-mail avec mot de passe, créez un fichier d’inscription/components/PasswordForm.tsx , puis collez le code de l’inscription/components/PasswordForm.tsx. Ce composant affiche un formulaire d’entrée de mot de passe.

Gérer les interactions avec le formulaire

Dans cette section, vous ajoutez du code qui gère les interactions de formulaire d’inscription, telles que l’envoi de détails d’inscription de l’utilisateur ou un code secret à usage unique ou un mot de passe.

Créez sign-up/page.tsx pour gérer la logique du flux d'inscription. Dans ce fichier :

  • Importez les composants nécessaires et affichez le formulaire approprié en fonction de l’état. Consultez un exemple complet dans sign-up/page.tsx :

        import { useEffect, useState } from "react";
        import { customAuthConfig } from "../../config/auth-config";
        import { styles } from "./styles/styles";
        import { InitialFormWithPassword } from "./components/InitialFormWithPassword";
    
        import {
        CustomAuthPublicClientApplication,
        ICustomAuthPublicClientApplication,
        SignUpCodeRequiredState,
        // Uncomment if your choice of authentication method is email with password
        // SignUpPasswordRequiredState,
        SignUpCompletedState,
        AuthFlowStateBase,
      } from "@azure/msal-browser/custom-auth";
    
        import { SignUpResultPage } from "./components/SignUpResult";
        import { CodeForm } from "./components/CodeForm";
        import { PasswordForm } from "./components/PasswordForm";    
    export default function SignUpPassword() {
        const [authClient, setAuthClient] = useState<ICustomAuthPublicClientApplication | null>(null);
        const [firstName, setFirstName] = useState("");
        const [lastName, setLastName] = useState("");
        const [jobTitle, setJobTitle] = useState("");
        const [city, setCity] = useState("");
        const [country, setCountry] = useState("");
        const [email, setEmail] = useState("");
        //Uncomment if your choice of authentication method is email with password
        //const [password, setPassword] = useState("");
        const [code, setCode] = useState("");
        const [error, setError] = useState("");
        const [loading, setLoading] = useState(false);
        const [signUpState, setSignUpState] = useState<AuthFlowStateBase | null>(null);
        const [loadingAccountStatus, setLoadingAccountStatus] = useState(true);
        const [isSignedIn, setSignInState] = useState(false);
    
        useEffect(() => {
            const initializeApp = async () => {
                const appInstance = await CustomAuthPublicClientApplication.create(customAuthConfig);
                setAuthClient(appInstance);
            };
            initializeApp();
        }, []);
    
        useEffect(() => {
            const checkAccount = async () => {
                if (!authClient) return;
                const accountResult = authClient.getCurrentAccount();
                if (accountResult.isCompleted()) {
                    setSignInState(true);
                }
                setLoadingAccountStatus(false);
            };
            checkAccount();
        }, [authClient]);
    
        const renderForm = () => {
            if (loadingAccountStatus) {
                return;
            }
            if (isSignedIn) {
                return (
                    <div style={styles.signed_in_msg}>Please sign out before processing the sign up.</div>
                );
            }
            if (signUpState instanceof SignUpCodeRequiredState) {
                return (
                    <CodeForm
                        onSubmit={handleCodeSubmit}
                        code={code}
                        setCode={setCode}
                        loading={loading}
                    />
                );
            } 
            //Uncomment the following block of code if your choice of authentication method is email with password 
            /*
            else if(signUpState instanceof SignUpPasswordRequiredState) {
                return <PasswordForm
                    onSubmit={handlePasswordSubmit}
                    password={password}
                    setPassword={setPassword}
                    loading={loading}
                />;
            }
            */
            else if (signUpState instanceof SignUpCompletedState) {
                return <SignUpResultPage />;
            } else {
                return (
                    <InitialForm
                        onSubmit={handleInitialSubmit}
                        firstName={firstName}
                        setFirstName={setFirstName}
                        lastName={lastName}
                        setLastName={setLastName}
                        jobTitle={jobTitle}
                        setJobTitle={setJobTitle}
                        city={city}
                        setCity={setCity}
                        country={country}
                        setCountry={setCountry}
                        email={email}
                        setEmail={setEmail}
                        loading={loading}
                    />
                );
            }
        }
        return (
            <div style={styles.container}>
                <h2 style={styles.h2}>Sign Up</h2>
                {renderForm()}
                {error && <div style={styles.error}>{error}</div>}
            </div>
        );
    }
    

    Ce code crée également une instance de l’application cliente publique d’authentification native à l’aide de la configuration du client :

    const appInstance = await CustomAuthPublicClientApplication.create(customAuthConfig);
    setAuthClient(appInstance);
    
  • Pour gérer la soumission initiale du formulaire, utilisez l’extrait de code suivant. Consultez un exemple complet à inscription/page.tsx pour savoir où placer l'extrait de code :

    const handleInitialSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        setError("");
        setLoading(true);
    
        if (!authClient) return;
    
        const attributes: UserAccountAttributes = {
            displayName: `${firstName} ${lastName}`,
            givenName: firstName,
            surname: lastName,
            jobTitle: jobTitle,
            city: city,
            country: country,
        };
    
        const result = await authClient.signUp({
            username: email,
            attributes
        });
        const state = result.state;
    
        if (result.isFailed()) {
            if (result.error?.isUserAlreadyExists()) {
                setError("An account with this email already exists");
            } else if (result.error?.isInvalidUsername()) {
                setError("Invalid uername");
            } else if (result.error?.isInvalidPassword()) {
                setError("Invalid password");
            } else if (result.error?.isAttributesValidationFailed()) {
                setError("Invalid attributes");
            } else if (result.error?.isMissingRequiredAttributes()) {
                setError("Missing required attributes");
            } else {
                setError(result.error?.errorData.errorDescription || "An error occurred while signing up");
            }
        } else {
            setSignUpState(state);
        }
        setLoading(false);
    };
    

    La méthode d’instance du Kit de développement logiciel (SDK) signUp() démarre le flux d’inscription.

  • Pour gérer la soumission de code secret unique, utilisez l’extrait de code suivant. Consultez un exemple complet à inscription/page.tsx pour savoir où placer l'extrait de code :

    const handleCodeSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        setError("");
        setLoading(true);
    
        try {
            if (signUpState instanceof SignUpCodeRequiredState) {
                const result = await signUpState.submitCode(code);
                if (result.error) {
                    if (result.error.isInvalidCode()) {
                        setError("Invalid verification code");
                    } else {
                        setError("An error occurred while verifying the code");
                    }
                    return;
                }
                if (result.state instanceof SignUpCompletedState) {
                    setSignUpState(result.state);
                }
            }
        } catch (err) {
            setError("An unexpected error occurred");
            console.error(err);
        } finally {
            setLoading(false);
        }
    };
    
  • Pour gérer la soumission de mot de passe, utilisez l’extrait de code suivant. Vous gérez la soumission de mot de passe si votre choix de méthode d’authentification est un e-mail avec mot de passe. Consultez un exemple complet à inscription/page.tsx pour savoir où placer l'extrait de code :

        const handlePasswordSubmit = async (e: React.FormEvent) => {
            e.preventDefault();
            setError("");
            setLoading(true);
    
            if (signUpState instanceof SignUpPasswordRequiredState) {
                const result = await signUpState.submitPassword(password);
                const state = result.state;
    
                if (result.isFailed()) {
                    if (result.error?.isInvalidPassword()) {
                        setError("Invalid password");
                    } else {
                        setError(result.error?.errorData.errorDescription || "An error occurred while submitting the password");
                    }
                } else {
                    setSignUpState(state);
                }
            }
    
            setLoading(false);
        };
    
  • Utilisez la signUpState instanceof SignUpCompletedState commande pour indiquer que l’utilisateur a été inscrit et que le flux est terminé. Consultez un exemple complet à sign-up/page.tsx :

    if (signUpState instanceof SignUpCompletedState) {
        return <SignUpResultPage/>;
    }
    

Gérer les erreurs d’inscription

Pendant l’inscription, toutes les actions n’aboutissent pas. Par exemple, l’utilisateur peut tenter de s’inscrire avec une adresse e-mail déjà utilisée, ou soumettre un code secret à usage unique envoyé par e-mail non valide. Vérifiez que vous gérez correctement les erreurs lorsque vous :

  • Lancez le flux d'inscription avec la méthode signUp().

  • Envoyez le code d'accès unique par la méthode submitCode().

  • Envoyez le mot de passe dans la submitPassword() méthode. Vous gérez cette erreur si votre choix de flux d’inscription est par e-mail et par mot de passe.

L’une des erreurs pouvant résulter de la signUp() méthode est result.error?.isRedirectRequired(). Ce scénario se produit quand l’authentification native n’est pas suffisante pour effectuer le flux d’authentification. Par exemple, si le serveur d’autorisation nécessite des fonctionnalités que le client ne peut pas fournir. En savoir plus sur le repli web pour l'authentification native et sur la prise en charge du repli web dans votre application React.

Facultatif : Connecter automatiquement les utilisateurs après l’inscription

Une fois qu’un utilisateur s’est correctement inscrit, vous pouvez directement les connecter à l’application sans lancer de nouveau flux de connexion. Pour ce faire, utilisez l’extrait de code suivant. Consultez un exemple complet à sign-up/page.tsx :

if (signUpState instanceof SignUpCompletedState) {
    const result = await signUpState.signIn();
    const state = result.state;
    if (result.isFailed()) {
        setError(result.error?.errorData?.errorDescription || "An error occurred during auto sign-in");
    }
    
    if (result.isCompleted()) {
        setData(result.data);
        setSignUpState(state);
    }
}

Exécuter et tester votre application

  1. Ouvrez une fenêtre de terminal et accédez au dossier racine de votre application :

    cd reactspa
    
  2. Pour démarrer le serveur proxy CORS, exécutez la commande suivante dans votre terminal :

    npm run cors
    
  3. Pour démarrer l’application React, ouvrez une autre fenêtre de terminal, puis exécutez la commande suivante :

    cd reactspa
    npm start
    
  4. Ouvrez un navigateur web et accédez à http://localhost:3000/sign-up. Un formulaire d’inscription s’affiche.

  5. Pour vous inscrire à un compte, entrez vos détails, sélectionnez le bouton Continuer , puis suivez les invites.

Ensuite, vous pouvez mettre à jour l’application React pour vous connecter à un utilisateur ou réinitialiser le mot de passe de l’utilisateur.

Configurer poweredByHeader sur false dans next.config.js

Par défaut, l’en-tête x-powered-by est inclus dans les réponses HTTP pour indiquer que l’application est alimentée par Next.js. Toutefois, pour des raisons de sécurité ou de personnalisation, vous pouvez supprimer ou modifier cet en-tête :

const nextConfig: NextConfig = {
  poweredByHeader: false,
  /* other config options here */
};

Étape suivante