Partilhar via


Editar perfil em um aplicativo Web Node.js

Aplica-se a:Círculo branco com um símbolo X cinzento. Locatários da força deCírculo verde com um símbolo de marca de verificação branco. trabalho Locatários externos (saiba mais)

Este artigo é a parte 2 de uma série que demonstra como adicionar a lógica de edição de perfil em um aplicativo Web Node.js. Na parte 1 desta série, você configura seu aplicativo para edição de perfil.

Neste guia de instruções, você aprenderá a chamar a API do Microsoft Graph para edição de perfil.

Pré-requisitos

Conclua o aplicativo Web cliente

Nesta seção, você adiciona o código relacionado à identidade para o aplicativo Web cliente.

Atualizar arquivo authConfig.js

Atualize o arquivo authConfig.js para o aplicativo Web cliente:

  1. No editor de códigos, abra o arquivo App/authConfig.js e adicione três novas variáveis GRAPH_API_ENDPOINTGRAPH_ME_ENDPOINT e editProfileScope. Certifique-se de exportar as três variáveis:

    //...
    const GRAPH_API_ENDPOINT = process.env.GRAPH_API_ENDPOINT || "https://graph.microsoft.com/";
    // https://learn.microsoft.com/graph/api/user-update?tabs=http
    const GRAPH_ME_ENDPOINT = GRAPH_API_ENDPOINT + "v1.0/me";
    const editProfileScope = process.env.EDIT_PROFILE_FOR_CLIENT_WEB_APP || 'api://{clientId}/EditProfileService.ReadWrite';
    
    module.exports = {
        //...
        editProfileScope,
        GRAPH_API_ENDPOINT,
        GRAPH_ME_ENDPOINT,
        //...
    };
    
    • A editProfileScope variável representa o recurso protegido por MFA, que é o aplicativo de camada intermediária (aplicativo EditProfileService).

    • O GRAPH_ME_ENDPOINT é o ponto de extremidade da API do Microsoft Graph.

  2. Substitua o espaço reservado {clientId} pela ID do aplicativo (cliente) do aplicativo de camada intermediária (aplicativo EditProfileService) que você registrou anteriormente.

Adquirir token de acesso no aplicativo Web cliente

No editor de códigos, abra o arquivo App/auth/AuthProvider.js e atualize o getToken método na AuthProvider classe:

    class AuthProvider {
    //...
        getToken(scopes, redirectUri = "http://localhost:3000/") {
            return  async function (req, res, next) {
                const msalInstance = authProvider.getMsalInstance(authProvider.config.msalConfig);
                try {
                    msalInstance.getTokenCache().deserialize(req.session.tokenCache);
    
                    const silentRequest = {
                        account: req.session.account,
                        scopes: scopes,
                    };
                    const tokenResponse = await msalInstance.acquireTokenSilent(silentRequest);
    
                    req.session.tokenCache = msalInstance.getTokenCache().serialize();
                    req.session.accessToken = tokenResponse.accessToken;
                    next();
                } catch (error) {
                    if (error instanceof msal.InteractionRequiredAuthError) {
                        req.session.csrfToken = authProvider.cryptoProvider.createNewGuid();
    
                        const state = authProvider.cryptoProvider.base64Encode(
                            JSON.stringify({
                                redirectTo: redirectUri,
                                csrfToken: req.session.csrfToken,
                            })
                        );
                        
                        const authCodeUrlRequestParams = {
                            state: state,
                            scopes: scopes,
                        };
    
                        const authCodeRequestParams = {
                            state: state,
                            scopes: scopes,
                        };
    
                        authProvider.redirectToAuthCodeUrl(
                            req,
                            res,
                            next,
                            authCodeUrlRequestParams,
                            authCodeRequestParams,
                            msalInstance
                        );
                    }
    
                    next(error);
                }
            };
        }
    }
    //...

O getToken método usa o escopo especificado para adquirir um token de acesso. O redirectUri parâmetro é a URL de redirecionamento depois que o aplicativo adquire um token de acesso.

Atualizar o arquivo users.js

No editor de códigos, abra o arquivo App/routes/users.js e adicione as seguintes rotas:

    //...
    
    var { fetch } = require("../fetch");
    const { GRAPH_ME_ENDPOINT, editProfileScope } = require('../authConfig');
    //...
    
router.get(
  "/gatedUpdateProfile",
  isAuthenticated,
  authProvider.getToken(["User.Read"]), // check if user is authenticated
  async function (req, res, next) {
    const graphResponse = await fetch(
      GRAPH_ME_ENDPOINT,
      req.session.accessToken,
    );
    if (!graphResponse.id) {
      return res 
        .status(501) 
        .send("Failed to fetch profile data"); 
    }
    res.render("gatedUpdateProfile", {
      profile: graphResponse,
    });
  },
);

router.get(
  "/updateProfile",
  isAuthenticated, // check if user is authenticated
  authProvider.getToken(
    ["User.Read", editProfileScope],
    "http://localhost:3000/users/updateProfile",
  ),
  async function (req, res, next) {
    const graphResponse = await fetch(
      GRAPH_ME_ENDPOINT,
      req.session.accessToken,
    );
    if (!graphResponse.id) {
      return res 
        .status(501) 
        .send("Failed to fetch profile data"); 
    }
    res.render("updateProfile", {
      profile: graphResponse,
    });
  },
);

router.post(
  "/update",
  isAuthenticated,
  authProvider.getToken([editProfileScope]),
  async function (req, res, next) {
    try {
      if (!!req.body) {
        let body = req.body;
        fetch(
          "http://localhost:3001/updateUserInfo",
          req.session.accessToken,
          "POST",
          {
            displayName: body.displayName,
            givenName: body.givenName,
            surname: body.surname,
          },
        )
          .then((response) => {
            if (response.status === 204) {
              return res.redirect("/");
            } else {
              next("Not updated");
            }
          })
          .catch((error) => {
            console.log("error,", error);
          });
      } else {
        throw { error: "empty request" };
      }
    } catch (error) {
      next(error);
    }
  },
);
    //...
  • Você aciona a /gatedUpdateProfile rota quando o usuário cliente seleciona o link de edição de perfil. A aplicação:

    1. Adquire um token de acesso com a permissão User.Read .
    2. Faz uma chamada para a API do Microsoft Graph para ler o perfil do usuário conectado.
    3. Exibe os detalhes do usuário na interface do usuário gatedUpdateProfile.hbs .
  • Você aciona a /updateProfile rota quando o usuário deseja atualizar seu nome de exibição, ou seja, ele seleciona o botão Editar perfil . A aplicação:

    1. Faz uma chamada para o aplicativo de camada intermediária (aplicativo EditProfileService) usando o escopo editProfileScope . Ao fazer uma chamada para o aplicativo de camada intermediária (aplicativo EditProfileService), o usuário deve concluir um desafio de MFA se ainda não o tiver feito.
    2. Exibe os detalhes do usuário na interface do usuário updateProfile.hbs .
  • Você aciona a /update rota quando o usuário seleciona o botão Salvar em gatedUpdateProfile.hbs ou updateProfile.hbs. A aplicação:

    1. Recupera o token de acesso para a sessão do aplicativo. Você aprende como o aplicativo de camada intermediária (aplicativo EditProfileService) adquire o token de acesso na próxima seção.
    2. Recolhe todos os detalhes do utilizador.
    3. Faz uma chamada para a API do Microsoft Graph para atualizar o perfil do usuário.

Atualizar o arquivo fetch.js

O aplicativo usa o arquivo App/fetch.js para fazer as chamadas de API reais.

No editor de códigos, abra o arquivo App/fetch.js e adicione a opção de operação PATCH. Depois de atualizar o arquivo, o arquivo resultante deve ser semelhante ao seguinte código:

var axios = require('axios');
var authProvider = require("./auth/AuthProvider");

/**
 * Makes an Authorization "Bearer" request with the given accessToken to the given endpoint.
 * @param endpoint
 * @param accessToken
 * @param method
 */
const fetch = async (endpoint, accessToken, method = "GET", data = null) => {
    const options = {
        headers: {
            Authorization: `Bearer ${accessToken}`,
        },
    };
    console.log(`request made to ${endpoint} at: ` + new Date().toString());

    switch (method) {
        case 'GET':
            const response = await axios.get(endpoint, options);
            return await response.data;
        case 'POST':
            return await axios.post(endpoint, data, options);
        case 'DELETE':
            return await axios.delete(endpoint + `/${data}`, options);
        case 'PATCH': 
            return await axios.patch(endpoint, ReqBody = data, options);
        default:
            return null;
    }
};

module.exports = { fetch };

Conclua o aplicativo intermediário

Nesta seção, você adiciona o código relacionado à identidade para o aplicativo de camada intermediária (aplicativo EditProfileService).

  1. No editor de códigos, abra o arquivo Api/authConfig.js e adicione o seguinte código:

    require("dotenv").config({ path: ".env.dev" });
    
    const TENANT_SUBDOMAIN =
      process.env.TENANT_SUBDOMAIN || "Enter_the_Tenant_Subdomain_Here";
    const TENANT_ID = process.env.TENANT_ID || "Enter_the_Tenant_ID_Here";
    const REDIRECT_URI =
      process.env.REDIRECT_URI || "http://localhost:3000/auth/redirect";
    const POST_LOGOUT_REDIRECT_URI =
      process.env.POST_LOGOUT_REDIRECT_URI || "http://localhost:3000";
    
    /**
     * Configuration object to be passed to MSAL instance on creation.
     * For a full list of MSAL Node configuration parameters, visit:
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
     */
    const msalConfig = {
      auth: {
        clientId:
          process.env.CLIENT_ID ||
          "Enter_the_Edit_Profile_Service_Application_Id_Here", // 'Application (client) ID' of the Edit_Profile Service App registration in Microsoft Entra admin center - this value is a GUID
        authority:
          process.env.AUTHORITY || `https://${TENANT_SUBDOMAIN}.ciamlogin.com/`, // Replace the placeholder with your external tenant name
      },
      system: {
        loggerOptions: {
          loggerCallback(loglevel, message, containsPii) {
            console.log(message);
          },
          piiLoggingEnabled: false,
          logLevel: "Info",
        },
      },
    };
    
    const GRAPH_API_ENDPOINT = process.env.GRAPH_API_ENDPOINT || "graph_end_point";
    // Refers to the user that is single user singed in.
    // https://learn.microsoft.com/en-us/graph/api/user-update?tabs=http
    const GRAPH_ME_ENDPOINT = GRAPH_API_ENDPOINT + "v1.0/me";
    
    module.exports = {
      msalConfig,
      REDIRECT_URI,
      POST_LOGOUT_REDIRECT_URI,
      TENANT_SUBDOMAIN,
      GRAPH_API_ENDPOINT,
      GRAPH_ME_ENDPOINT,
      TENANT_ID,
    };
    

    Encontre o espaço reservado:

    • Enter_the_Tenant_Subdomain_Here e substitua-o pelo subdomínio Directory (locatário). Por exemplo, se o domínio principal do locatário for contoso.onmicrosoft.com, use contoso. Se não tiver o nome do inquilino, saiba como ler os detalhes do inquilino.
    • Enter_the_Tenant_ID_Here e substitua-o pelo ID do locatário. Se não tiver o seu ID de inquilino, saiba como ler os detalhes do inquilino.
    • Enter_the_Edit_Profile_Service_Application_Id_Here e substitua-o por é o valor de ID do aplicativo (cliente) do EditProfileService que você registrou anteriormente.
    • Enter_the_Client_Secret_Heree substitua-o pelo valor secreto do aplicativo EditProfileService copiado anteriormente.
    • graph_end_point e substitua-o pelo ponto de https://graph.microsoft.com/extremidade da API do Microsoft Graph, ou seja.
  2. No editor de códigos, abra o arquivo Api/fetch.js e cole o código do arquivo Api/fetch.js . A fetch função usa um token de acesso e o ponto de extremidade do recurso para fazer a chamada de API real.

  3. No editor de códigos, abra o arquivo Api/index.js e cole o código do arquivo Api/index.js .

Adquira um token de acesso usando acquireTokenOnBehalfOf

No arquivo Api/index.js, o aplicativo de camada intermediária (aplicativo EditProfileService) adquire um token de acesso usando a função acquireTokenOnBehalfOf, que ele usa para atualizar o perfil em nome desse usuário.

async function getAccessToken(tokenRequest) {
  try {
    const response = await cca.acquireTokenOnBehalfOf(tokenRequest);
    return response.accessToken;
  } catch (error) {
    console.error("Error acquiring token:", error);
    throw error;
  }
}

O tokenRequest parâmetro é definido como mostrado o seguinte código:

    const tokenRequest = {
      oboAssertion: req.headers.authorization.replace("Bearer ", ""),
      authority: `https://${TENANT_SUBDOMAIN}.ciamlogin.com/${TENANT_ID}`,
      scopes: ["User.ReadWrite"],
      correlationId: `${uuidv4()}`,
    };

No mesmo arquivo, API/index.js, o aplicativo de camada intermediária (aplicativo EditProfileService) faz uma chamada para a API do Microsoft Graph para atualizar o perfil dos usuários:

   let accessToken = await getAccessToken(tokenRequest);
    fetch(GRAPH_ME_ENDPOINT, accessToken, "PATCH", req.body)
      .then((response) => {
        if (response.status === 204) {
          res.status(response.status);
          res.json({ message: "Success" });
        } else {
          res.status(502);
          res.json({ message: "Failed, " + response.body });
        }
      })
      .catch((error) => {
        res.status(502);
        res.json({ message: "Failed, " + error });
      });

Testar a sua aplicação

Para testar seu aplicativo, use as seguintes etapas:

  1. Para executar o aplicativo cliente, forme a janela do terminal, navegue até o diretório App e execute o seguinte comando:

    npm start
    
  2. Para executar o aplicativo cliente, forme a janela do terminal, navegue até o diretório da API e execute o seguinte comando:

    npm start
    
  3. Abra o navegador e vá para http://localhost:3000. Se você tiver erros de certificado SSL, crie um .env arquivo e adicione a seguinte configuração:

    # Use this variable only in the development environment. 
    # Remove the variable when you move the app to the production environment.
    NODE_TLS_REJECT_UNAUTHORIZED='0'
    
  4. Selecione o botão Iniciar sessão e, em seguida, inicie sessão.

  5. Na página de início de sessão, escreva o seu endereço de e-mail, selecione Seguinte, escreva a sua Palavra-passe e, em seguida, selecione Iniciar sessão. Se você não tiver uma conta, selecione Sem conta? Crie um link, que inicia o fluxo de inscrição.

  6. Para atualizar o perfil, selecione o link Edição de perfil . Você verá uma página semelhante à seguinte captura de tela:

    Captura de ecrã do perfil de atualização do utilizador.

  7. Para editar o perfil, selecione o botão Editar perfil . Se você ainda não tiver feito isso, o aplicativo solicitará que você conclua um desafio de MFA.

  8. Faça alterações em qualquer um dos detalhes do perfil e selecione o botão Salvar .