Compartilhar via


Tutorial: Inscrever usuários em um aplicativo de página única do React usando o SDK javaScript de autenticação nativa (versão prévia)

Aplica-se a: Círculo verde com um símbolo de marca de seleção branca que indica que o conteúdo a seguir se aplica a locatários externos. Locatários externos (saiba mais)

Neste tutorial, você aprenderá a criar um aplicativo de página única React que registra usuários utilizando o SDK JavaScript de autenticação nativa.

Neste tutorial, você:

  • Crie um projeto do React Next.js.
  • Adicione o SDK do JS da MSAL a ele.
  • Adicione componentes de interface do usuário do aplicativo.
  • Configure o projeto para inscrever usuários.

Pré-requisitos

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:

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

Depois de executar os comandos com êxito, você deverá ter um aplicativo com a seguinte estrutura:

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

Adicionar o SDK do JavaScript ao seu projeto

Para usar o SDK javaScript de autenticação nativa em seu aplicativo, use o terminal para instalá-lo usando o seguinte comando:

npm install @azure/msal-browser

Os recursos de autenticação nativa fazem parte da azure-msal-browser biblioteca. Para usar recursos de autenticação nativa, você importa de @azure/msal-browser/custom-auth. Por exemplo:

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

Adicionar configuração do cliente

Nesta seção, você definirá uma configuração para o aplicativo cliente público de autenticação nativa para permitir que ele interaja com a interface do SDK. Para fazer isso, crie um arquivo chamado src/config/auth-config.ts e adicione o seguinte código:

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

No código, localize o espaço reservado:

  • Enter_the_Application_Id_Here em seguida, substitua-o pela ID do aplicativo (cliente) do aplicativo que você registrou anteriormente.

  • Enter_the_Tenant_Subdomain_Here em seguida, substitua-o pelo subdomínio do inquilino no centro de administração do Microsoft Entra. Por exemplo, se o domínio primário do locatário for contoso.onmicrosoft.com, use contoso. Se você não tiver o nome do locatário, saiba como ler os detalhes do locatário.

Criar componentes de interface do usuário

Esse aplicativo coleta detalhes do usuário, como nome de usuário, nome de usuário (email), senha e senha única do usuário. Portanto, o aplicativo precisa ter um formulário que colete essas informações.

  1. Crie uma pasta chamada src/app/sign-up na pasta src .

  2. Crie um arquivo de inscrição/componentes/InitialForm.tsx e cole o código de inscrição/componentes/InitialForm.tsx. Esse componente exibe um formulário que coleta atributos de inscrição do usuário.

  3. Crie um arquivo de inscrição/componentes/CodeForm.tsx e cole o código de inscrição/componentes/CodeForm.tsx. Esse componente exibe um formulário que coleta uma senha única enviada ao usuário. Você precisa desse formulário para email com senha ou email com método de autenticação de senha única.

  4. Se sua opção de método de autenticação for email com senha, crie um arquivo de inscrição/componentes/PasswordForm.tsx e cole o código de inscrição/componentes/PasswordForm.tsx. Esse componente exibe um formulário de entrada de senha.

Gerenciar interação de formulário

Nesta seção, você adicionará um código que lida com interações do formulário de inscrição, como enviar detalhes de cadastro do usuário, um código de uso único ou uma senha.

Crie sign-up/page.tsx para lidar com a lógica de um fluxo de cadastro. Neste arquivo:

  • Importe os componentes necessários e exiba o formulário adequado com base no estado. Veja um exemplo completo em 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>
        );
    }
    

    Esse código também cria uma instância do aplicativo cliente público de autenticação nativa usando a configuração do cliente:

    const appInstance = await CustomAuthPublicClientApplication.create(customAuthConfig);
    setAuthClient(appInstance);
    
  • Para lidar com o envio do formulário inicial, use o snippet de código a seguir. Veja um exemplo completo em inscreva-se/page.tsx para saber onde colocar o snippet de código:

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

    O método signUp() de instância do SDK inicia o fluxo de inscrição.

  • Para lidar com o envio de senha única, use o snippet de código a seguir. Veja um exemplo completo em inscreva-se/page.tsx para saber onde colocar o snippet de código:

    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);
        }
    };
    
  • Para lidar com o envio de senha, use o trecho de código a seguir. Você manipulará o envio de senha se sua opção de método de autenticação for email com senha. Veja um exemplo completo em inscreva-se/page.tsx para saber onde colocar o snippet de código:

        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);
        };
    
  • Use o signUpState instanceof SignUpCompletedState para indicar que o usuário foi inscrito e que o fluxo está concluído. Veja um exemplo completo em inscreva-se/page.tsx:

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

Tratar erros de inscrição

Durante a inscrição, nem todas as ações são bem-sucedidas. Por exemplo, o usuário pode tentar se inscrever com um endereço de email já usado ou enviar uma senha única de email inválida. Certifique-se de lidar com erros corretamente ao:

  • Inicie o processo de inscrição no método signUp().

  • Envie a senha única pelo método submitCode().

  • Enviar a senha com o método submitPassword(). Você lidará com esse erro se sua opção de fluxo de inscrição for por email e senha.

Um dos erros que podem resultar do signUp() método é result.error?.isRedirectRequired(). Esse cenário ocorre quando a autenticação nativa não é suficiente para concluir o fluxo de autenticação. Por exemplo, se o servidor de autorização exigir recursos que o cliente não pode fornecer. Saiba mais sobre o fallback de autenticação nativa para a web e como oferecer suporte ao fallback para a web em seu aplicativo React.

Opcional: conectar usuários automaticamente após a inscrição

Depois que um usuário se inscreve com êxito, você pode conectá-lo diretamente ao aplicativo sem iniciar um novo fluxo de entrada. Para fazer isso, use o trecho de código a seguir. Veja um exemplo completo em inscreva-se/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);
    }
}

Executar e testar seu aplicativo

  1. Abra uma janela do terminal e navegue até a pasta raiz do aplicativo:

    cd reactspa
    
  2. Para iniciar o servidor proxy CORS, execute o seguinte comando em seu terminal:

    npm run cors
    
  3. Para iniciar o aplicativo React, abra outra janela do terminal e execute o seguinte comando:

    cd reactspa
    npm start
    
  4. Abra um navegador da Web e navegue até http://localhost:3000/sign-up. Um formulário de inscrição é exibido.

  5. Para se inscrever em uma conta, insira seus detalhes, selecione o botão Continuar e siga os prompts.

Em seguida, você pode atualizar o aplicativo React para conectar um usuário ou redefinir a senha do usuário.

Configurar poweredByHeader como false no next.config.js

Por padrão, o x-powered-by cabeçalho é incluído nas respostas HTTP para indicar que o aplicativo é alimentado por Next.js. No entanto, por motivos de segurança ou personalização, talvez você queira remover ou modificar este cabeçalho:

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

Próxima etapa