Dela via


Redigera profil i en Node.js webbapp

Gäller för: Grön cirkel med en vit bockmarkeringssymbol som anger att följande innehåll gäller för externa klienter. Externa klienter (läs mer)

Den här artikeln är del 2 i en serie som visar hur du lägger till profilredigeringslogik i en Node.js webbapp. I del 1 i den här serienkonfigurerar du appen för profilredigering.

I den här guiden får du lära dig hur du anropar Microsoft Graph API för profilredigering.

Förutsättningar

Slutför klientwebbappen

I det här avsnittet lägger du till identitetsrelaterad kod för klientwebbappen.

Uppdatera authConfig.js-filen

Uppdatera authConfig.js-filen för klientwebbappen:

  1. Öppna filen App/authConfig.js i kodredigeraren och lägg sedan till tre nya variabler, GRAPH_API_ENDPOINT, GRAPH_ME_ENDPOINT och editProfileScope. Se till att exportera de tre variablerna:

    //...
    const GRAPH_API_ENDPOINT = process.env.GRAPH_API_ENDPOINT || "https://graph.microsoft.com/";
    // https://free.blessedness.top/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,
        //...
    };
    
    • Variabeln editProfileScope representerar MFA-skyddad resurs, dvs. mellannivåappen (EditProfileService-appen).

    • GRAPH_ME_ENDPOINT är Microsoft Graph API-slutpunkten.

  2. Ersätt platshållaren {clientId} med program-ID:t (klient) för den mellannivåapp (EditProfileService-app) som du registrerade tidigare.

Hämta åtkomsttoken i klientwebbappen

Öppna filen App/auth/AuthProvider.js i kodredigeraren och uppdatera sedan metoden getToken i klassen AuthProvider:

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

Metoden getToken använder det angivna omfånget för att hämta en åtkomsttoken. Parametern redirectUri är omdirigerings-URL:en när appen har hämtat en åtkomsttoken.

Uppdatera filen users.js

Öppna filen App/routes/users.js i kodredigeraren och lägg sedan till följande vägar:

    //...
    
    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);
    }
  },
);
    //...
  • Du aktiverar /gatedUpdateProfile rutten när kunden väljer länken Profilredigering. Appen:

    1. Hämtar en åtkomsttoken med behörigheten User.Read.
    2. Anropar Microsoft Graph API för att läsa den inloggade användarens profil.
    3. Visar användarinformationen i gatedUpdateProfile.hbs användargränssnittet.
  • Du utlöser rutten /updateProfile när användaren vill uppdatera sitt visningsnamn, det vill säga när de väljer knappen Redigera profil. Appen:

    1. Gör ett anrop till mellannivåappen (EditProfileService-appen) med editProfileScope omfång. Genom att göra ett anrop till mellannivåappen (EditProfileService-appen) måste användaren slutföra en MFA-utmaning om de inte redan har gjort det.
    2. Visar användarinformationen i updateProfile.hbs användargränssnittet.
  • Du aktiverar /update-rutten när användaren väljer knappen Spara i antingen gatedUpdateProfile.hbs eller updateProfile.hbs. Appen:

    1. Hämtar åtkomsttoken för appsessionen. Du lär dig hur mellannivåappen (EditProfileService-appen) hämtar åtkomsttoken i nästa avsnitt.
    2. Samlar in all användarinformation.
    3. Anropar Microsoft Graph API för att uppdatera användarens profil.

Uppdatera filen fetch.js

Appen använder filen App/fetch.js för att göra de faktiska API-anropen.

Öppna App/fetch.js-filen i kodredigeraren och lägg sedan till alternativet PATCH-åtgärd. När du har uppdaterat filen bör den resulterande filen se ut ungefär som följande kod:

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

Slutför mellannivåappen

I det här avsnittet lägger du till den identitetsrelaterade koden för mellannivåappen (EditProfileService-appen).

  1. Öppna Api/authConfig.js-fil i kodredigeraren och lägg sedan till följande kod:

    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
        clientSecret: process.env.CLIENT_SECRET || "Enter_the_Client_Secret_Here ", // Client secret generated from the app registration in Microsoft Entra admin center
      },
      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://free.blessedness.top/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,
    };
    

    Hitta platshållaren:

    • Enter_the_Tenant_Subdomain_Here och ersätt det med Katalog (klientorganisation) underdomän. Om din primära klientdomän till exempel är contoso.onmicrosoft.comanvänder du contoso. Om du inte har ditt klientnamn, lär dig hur du kan läsa klientinformationen.
    • Enter_the_Tenant_ID_Here och ersätt det med tenant-ID. Om du inte har ditt klient-ID, lär dig hur du läser dina klientdetaljer.
    • Enter_the_Edit_Profile_Service_Application_Id_Here och ersätt det med applikationens (klientens) ID-värde för den EditProfileService som du registrerade tidigare.
    • Enter_the_Client_Secret_Here och ersätt den med EditProfileService-apphemligheten värde som du kopierade tidigare.
    • graph_end_point och ersätt den med Microsoft Graph API-slutpunkten, som är https://graph.microsoft.com/.
  2. Öppna Api/fetch.js-filen i kodredigeraren och klistra sedan in koden från Api/fetch.js-filen. Funktionen fetch använder en åtkomsttoken och resursslutpunkten för att göra det faktiska API-anropet.

  3. Öppna Api/index.js-filen i kodredigeraren och klistra sedan in koden från Api/index.js-filen.

Hämta en åtkomsttoken med hjälp av acquireTokenOnBehalfOf

I filen Api/index.js hämtar mellannivåappen (EditProfileService-appen) en åtkomsttoken med hjälp av funktionen acquireTokenOnBehalfOf, som används för att uppdatera profilen åt användaren.

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

Parametern tokenRequest definieras enligt följande kod:

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

I samma fil anropar API/index.js, appen på mellannivå (EditProfileService-appen) ett anrop till Microsoft Graph API för att uppdatera användarens profil:

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

Testa din app

Använd följande steg för att testa din app:

  1. Om du vill köra klientappen skapar du terminalfönstret, navigerar till katalogen App och kör sedan följande kommando:

    npm start
    
  2. Om du vill köra klientappen skapar du terminalfönstret, navigerar till katalogen API och kör sedan följande kommando:

    npm start
    
  3. Öppna webbläsaren och gå sedan till http://localhost:3000. Om du får SSL-certifikatfel skapar du en .env-fil och lägger sedan till följande konfiguration:

    # 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. Välj knappen Logga in, och därefter loggar du in.

  5. På inloggningssidan skriver du din e-postadress, väljer Nästa, skriver Lösenordoch väljer sedan Logga in. Om du inte har något konto väljer du Inget konto? Skapa en länk som startar registreringsflödet.

  6. Om du vill uppdatera profilen väljer du länken Profilredigering. Du ser en sida som liknar följande skärmbild:

    Skärmbild av användaruppdateringsprofilen.

  7. Om du vill redigera profilen väljer du knappen Redigera profil. Om du inte redan har gjort det uppmanar appen dig att slutföra en MFA-utmaning.

  8. Gör ändringar i någon av profilinformationen och välj sedan knappen Spara.