Partilhar via


Tutorial: Inscrever usuários em um aplicativo de página única do React usando autenticação nativa JavaScript SDK (visualização)

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

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

Neste tutorial, você:

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

Pré-requisitos

Criar um projeto React e instalar dependências

Em um local de escolha em seu computador, execute os seguintes comandos para criar um novo projeto React com o nome reactspa, navegue até a pasta do projeto e instale pacotes:

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

Depois de executar os comandos com êxito, você deve 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 JavaScript SDK ao seu projeto

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

npm install @azure/msal-browser

Os recursos de autenticação nativos fazem parte da azure-msal-browser biblioteca. Para usar recursos de autenticação nativos, importe do @azure/msal-browser/custom-auth. Por exemplo:

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

Adicionar configuração do cliente

Nesta seção, você define 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 pelo ID do aplicativo (cliente) do aplicativo que você registrou anteriormente.

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

Criar componentes da interface do usuário

Este aplicativo coleta detalhes do usuário, como nome próprio, nome de usuário (e-mail), senha e uma senha única do usuário. Então, 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 o arquivo sign-up/components/InitialForm.tsx e cole o código de sign-up/components/InitialForm.tsx. Este componente exibe um formulário que coleta atributos de inscrição do usuário.

  3. Crie um arquivo sign-up/components/CodeForm.tsx e cole o código de sign-up/components/CodeForm.tsx. Este componente exibe um formulário que coleta uma senha única enviada ao usuário. Você precisa deste formulário para e-mail com senha ou e-mail com método de autenticação de senha única.

  4. Se a sua escolha de método de autenticação for e-mail com senha, crie um arquivo de inscrição/components/PasswordForm.tsx e, em seguida, cole o código de sign-up/components/PasswordForm.tsx. Este componente exibe um formulário de entrada de senha.

Manipular a interação do formulário

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

Crie sign-up/page.tsx para lidar com a lógica de um fluxo de inscrição. Neste ficheiro:

  • 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 manipular o envio inicial do formulário, use o trecho de código a seguir. Veja um exemplo completo em sign-up/page.tsx para saber onde colocar o trecho 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 trecho de código a seguir. Veja um exemplo completo em sign-up/page.tsx para saber onde colocar o trecho 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 seguinte trecho de código. Você lida com o envio de senha se sua escolha de método de autenticação for e-mail com senha. Veja um exemplo completo em sign-up/page.tsx para saber onde colocar o trecho 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 o fluxo está concluído. Veja um exemplo completo em sign-up/page.tsx:

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

Lidar com 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 e-mail já usado ou enviar uma senha única de e-mail inválida. Certifique-se de lidar com os erros corretamente quando:

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

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

  • Envie a senha no submitPassword() método. Você lida com esse erro se sua escolha de fluxo de inscrição for por e-mail e senha.

Um dos erros que podem resultar do signUp() método é result.error?.isRedirectRequired(). Esse cenário acontece 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 da Web de autenticação nativa e como dar suporte ao fallback da Web em seu aplicativo React.

Opcional: Iniciar sessão de utilizadores automaticamente após a inscrição

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

Executar e testar seu aplicativo

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

    cd reactspa
    
  2. Para iniciar o servidor proxy CORS, execute o seguinte comando no 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. É apresentado um formulário de inscrição.

  5. Para se inscrever numa conta, introduza os seus dados, selecione o botão Continuar e, em seguida, siga as instruções.

Em seguida, poderá atualizar a aplicação React para autenticar um utilizador ou redefinir a senha do utilizador.

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, convém remover ou modificar este cabeçalho:

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

Próximo passo