Delen via


Zelfstudie: Gebruikers registreren bij een React-app met één pagina met behulp van systeemeigen verificatie JavaScript SDK (preview)

Van toepassing op: Groene cirkel met een wit vinkje dat aangeeft dat de volgende inhoud van toepassing is op externe tenants. Externe tenants (meer informatie)

In deze zelfstudie leert u hoe u een React-app met één pagina bouwt waarmee gebruikers worden geregistreerd met behulp van de JavaScript SDK van systeemeigen verificatie.

In deze handleiding leert u:

  • Een React Next.js-project maken.
  • Voeg er MSAL JS SDK aan toe.
  • Ui-onderdelen van de app toevoegen.
  • Stel het project in om gebruikers te registreren.

Vereiste voorwaarden

Een React-project maken en afhankelijkheden installeren

Voer op een locatie naar keuze op uw computer de volgende opdrachten uit om een nieuw React-project te maken met de naam reactspa, navigeer naar de projectmap en installeer vervolgens pakketten:

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

Nadat u de opdrachten hebt uitgevoerd, moet u een app met de volgende structuur hebben:

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

JavaScript SDK toevoegen aan uw project

Als u de JavaScript SDK voor systeemeigen verificatie in uw app wilt gebruiken, gebruikt u de terminal om deze te installeren met behulp van de volgende opdracht:

npm install @azure/msal-browser

De systeemeigen verificatiemogelijkheden maken deel uit van de azure-msal-browser bibliotheek. Als u systeemeigen verificatiefuncties wilt gebruiken, importeert u uit @azure/msal-browser/custom-auth. Voorbeeld:

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

Clientconfiguratie toevoegen

In deze sectie definieert u een configuratie voor een openbare clienttoepassing voor systeemeigen verificatie, zodat deze kan communiceren met de interface van de SDK. Maak hiervoor een bestand met de naam src/config/auth-config.ts en voeg vervolgens de volgende code toe:

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;
        }
      },
    },
  },
};

Zoek in de code de tijdelijke aanduiding:

  • Enter_the_Application_Id_Here vervang deze vervolgens door de toepassings-id (client) van de app die u eerder hebt geregistreerd.

  • Enter_the_Tenant_Subdomain_Here vervang dit vervolgens door het tenantsubdomein in uw Microsoft Entra-beheercentrum. Als uw primaire tenantdomein bijvoorbeeld is contoso.onmicrosoft.com, gebruikt u contoso. Indien u uw tenantnaam niet hebt, leer dan hoe u de details van uw tenant kunt lezen.

UI-onderdelen maken

Deze app verzamelt gebruikersgegevens, zoals de opgegeven naam, de gebruikersnaam (e-mail), het wachtwoord en een eenmalige wachtwoordcode van de gebruiker. De app moet dus een formulier hebben waarmee deze gegevens worden verzameld.

  1. Maak een map met de naam src/app/sign-up in de src-map .

  2. Maak het bestand sign-up/components/InitialForm.tsx en plak de code uit sign-up/components/InitialForm.tsx. In dit onderdeel wordt een formulier weergegeven waarin gebruikersaanmeldingskenmerken worden verzameld.

  3. Maak een registratie/components/CodeForm.tsx-bestand en plak de code uit registratie/onderdelen/CodeForm.tsx. In dit onderdeel wordt een formulier weergegeven waarin een eenmalige wachtwoordcode wordt verzameld die naar de gebruiker wordt verzonden. U hebt dit formulier nodig voor e-mail met een wachtwoord of e-mail met een eenmalige verificatiemethode voor wachtwoordcodes.

  4. Als uw keuze voor de verificatiemethode e-mail is met een wachtwoord, maakt u een registratie-/onderdelen-/PasswordForm.tsx-bestand en plakt u de code uit registratie/components/PasswordForm.tsx. In dit onderdeel wordt een wachtwoordinvoerformulier weergegeven.

Formulierinteractie verwerken

In deze sectie voegt u code toe die interactie tussen registratieformulieren afhandelt, zoals het verzenden van gebruikersgegevens of een eenmalige wachtwoordcode of een wachtwoord.

Maak registratie/page.tsx voor het afhandelen van logica voor een registratiestroom. In dit bestand:

  • Importeer de benodigde onderdelen en geef het juiste formulier weer op basis van de status. Bekijk een volledig voorbeeld in registratie/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>
        );
    }
    

    Met deze code wordt ook een exemplaar van de openbare client-app voor systeemeigen verificatie gemaakt met behulp van de clientconfiguratie:

    const appInstance = await CustomAuthPublicClientApplication.create(customAuthConfig);
    setAuthClient(appInstance);
    
  • Gebruik het volgende codefragment om de inzending van het formulier af te handelen. Zie een volledig voorbeeld op sign-up/page.tsx voor meer informatie over het plaatsen van het codefragment:

    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);
    };
    

    De instantiemethode signUp() van de SDK start de registratiestroom.

  • Gebruik het volgende codefragment om de eenmalige wachtwoordcode te verwerken. Zie een volledig voorbeeld op sign-up/page.tsx voor meer informatie over het plaatsen van het codefragment:

    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);
        }
    };
    
  • Gebruik het volgende codefragment om het verzenden van wachtwoorden af te handelen. U kunt het verzenden van wachtwoorden afhandelen als uw keuze voor verificatiemethode e-mail is met een wachtwoord. Zie een volledig voorbeeld op sign-up/page.tsx voor meer informatie over het plaatsen van het codefragment:

        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);
        };
    
  • Gebruik de signUpState instanceof SignUpCompletedState functie om aan te geven dat de gebruiker is geregistreerd en dat de stroom is voltooid. Bekijk een volledig voorbeeld op sign-up/page.tsx:

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

Registratiefouten afhandelen

Tijdens het registreren slagen niet alle acties. De gebruiker kan bijvoorbeeld proberen zich aan te melden met een al gebruikt e-mailadres of een ongeldige wachtwoordcode voor eenmalige e-mail verzenden. Zorg ervoor dat u fouten correct afhandelt wanneer u het volgende doet:

  • Start de registratiestroom in de signUp() methode.

  • Verzend de eenmalige wachtwoordcode in de submitCode() methode.

  • Dien een wachtwoord in de submitPassword() methode in. U kunt deze fout afhandelen als uw keuze voor registratiestroom per e-mail en wachtwoord is.

Een van de fouten die kunnen voortvloeien uit de signUp() methode is result.error?.isRedirectRequired(). Dit scenario treedt op wanneer systeemeigen verificatie niet voldoende is om de verificatiestroom te voltooien. Als de autorisatieserver bijvoorbeeld mogelijkheden vereist die de client niet kan bieden. Meer informatie over terugval van systeemeigen verificatieweb en het ondersteunen van webterugval in uw React-app.

Optioneel: Gebruikers automatisch aanmelden na aanmelding

Nadat een gebruiker zich heeft geregistreerd, kunt u deze rechtstreeks aanmelden bij de app zonder een nieuwe aanmeldingsstroom te starten. Gebruik hiervoor het volgende codefragment. Bekijk een volledig voorbeeld op 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);
    }
}

Uw app uitvoeren en testen

  1. Open een terminalvenster en navigeer naar de hoofdmap van uw app:

    cd reactspa
    
  2. Voer de volgende opdracht uit in de terminal om de CORS-proxyserver te starten:

    npm run cors
    
  3. Als u de React-app wilt starten, opent u een ander terminalvenster en voert u vervolgens de volgende opdracht uit:

    cd reactspa
    npm start
    
  4. Open een webbrowser en navigeer naar http://localhost:3000/sign-up. Er wordt een registratieformulier weergegeven.

  5. Als u zich wilt registreren voor een account, voert u uw gegevens in, selecteert u de knop Doorgaan en volgt u de aanwijzingen.

Vervolgens kunt u de React-app bijwerken om u aan te melden bij een gebruiker of het wachtwoord van de gebruiker opnieuw in te stellen.

PoweredByHeader instellen op false in next.config.js

De header is standaard x-powered-by opgenomen in de HTTP-antwoorden om aan te geven dat de toepassing wordt aangedreven door Next.js. Om veiligheids- of aanpassingsredenen wilt u deze header echter verwijderen of wijzigen:

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

Volgende stap