Sdílet prostřednictvím


Úprava profilu ve webové aplikaci Node.js

Platí pro: Bílý kruh se šedým symbolem X. Tenanti pracovních sil – externí tenanti Zelený kruh s bílým symbolem zaškrtnutí (další informace)

Tento článek je 2. část série, která ukazuje, jak přidat logiku úprav profilu do webové aplikace Node.js. V 1. části této série nastavíte aplikaci pro úpravy profilu.

V tomto návodu se dozvíte, jak volat rozhraní Microsoft Graph API pro úpravy profilu.

Požadavky

Dokončení klientské webové aplikace

V této části přidáte kód související s identitou pro klientskou webovou aplikaci.

Aktualizace souboru authConfig.js

Aktualizujte soubor authConfig.js pro klientskou webovou aplikaci:

  1. V editoru kódu otevřete soubor App/authConfig.js a pak přidejte tři nové proměnné GRAPH_API_ENDPOINTGRAPH_ME_ENDPOINT a editProfileScope. Nezapomeňte exportovat tři proměnné:

    //...
    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,
        //...
    };
    
    • Proměnná editProfileScope představuje chráněný prostředek MFA, to je aplikace střední vrstvy (aplikace EditProfileService).

    • Jedná se GRAPH_ME_ENDPOINT o koncový bod rozhraní Microsoft Graph API.

  2. Zástupný symbol {clientId} nahraďte ID aplikace (klienta) střední vrstvy (aplikace EditProfileService), kterou jste zaregistrovali dříve.

Získání přístupového tokenu v klientské webové aplikaci

V editoru kódu otevřete soubor App/auth/AuthProvider.js a pak aktualizujte getToken metodu AuthProvider ve třídě:

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

Metoda getToken používá zadaný obor k získání přístupového tokenu. Parametr redirectUri je adresa URL přesměrování po získání přístupového tokenu aplikace.

Aktualizace souboru users.js

V editoru kódu otevřete soubor App/routes/users.js a přidejte následující trasy:

    //...
    
    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);
    }
  },
);
    //...
  • Trasu /gatedUpdateProfile aktivujete, když uživatel zákazníka vybere odkaz pro úpravy profilu. Aplikace:

    1. Získá přístupový token s oprávněním User.Read .
    2. Volá rozhraní Microsoft Graph API ke čtení profilu přihlášeného uživatele.
    3. Zobrazí podrobnosti o uživateli v uživatelském rozhraní gatedUpdateProfile.hbs .
  • Trasu /updateProfile aktivujete, když chce uživatel aktualizovat zobrazované jméno, to znamená, že vybere tlačítko Upravit profil . Aplikace:

    1. Zavolá aplikaci střední vrstvy (aplikaci EditProfileService) pomocí oboru editProfileScope . Voláním aplikace střední vrstvy (aplikace EditProfileService) musí uživatel dokončit výzvu vícefaktorového ověřování, pokud to ještě neudělal.
    2. Zobrazí podrobnosti o uživateli v uživatelském rozhraní updateProfile.hbs .
  • Trasu /update aktivujete, když uživatel vybere tlačítko Uložit v gatedUpdateProfile.hbs nebo updateProfile.hbs. Aplikace:

    1. Načte přístupový token pro relaci aplikace. V další části se dozvíte, jak aplikace střední vrstvy (aplikace EditProfileService) získá přístupový token.
    2. Shromažďuje všechny podrobnosti o uživateli.
    3. Volá rozhraní Microsoft Graph API k aktualizaci profilu uživatele.

Aktualizace souboru fetch.js

Aplikace používá k provádění skutečných volání rozhraní API soubor App/fetch.js .

V editoru kódu otevřete soubor App/fetch.js a pak přidejte možnost operace PATCH. Po aktualizaci souboru by výsledný soubor měl vypadat podobně jako následující kód:

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

Dokončení aplikace střední vrstvy

V této části přidáte kód související s identitou pro aplikaci střední vrstvy (aplikace EditProfileService).

  1. V editoru kódu otevřete soubor Api/authConfig.js a přidejte následující kód:

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

    Vyhledejte zástupný symbol:

    • Enter_the_Tenant_Subdomain_Here a nahraďte ji subdoménou adresáře (tenanta). Pokud je například primární doména vašeho tenanta contoso.onmicrosoft.com, použijte contoso. Pokud nemáte název tenanta, přečtěte si, jak si přečíst podrobnosti o tenantovi.
    • Enter_the_Tenant_ID_Here a nahraďte ho ID tenanta. Pokud nemáte ID tenanta, přečtěte si, jak si přečíst podrobnosti o tenantovi.
    • Enter_the_Edit_Profile_Service_Application_Id_Here a nahraďte ji hodnotou ID aplikace (klienta) služby EditProfileService, kterou jste zaregistrovali dříve.
    • Enter_the_Client_Secret_Here a nahraďte ji hodnotou tajného kódu aplikace EditProfileService, kterou jste zkopírovali dříve.
    • graph_end_point a nahraďte ho koncovým bodem rozhraní Microsoft Graph API, tj https://graph.microsoft.com/.
  2. V editoru kódu otevřete soubor Api/fetch.js a vložte kód ze souboru Api/fetch.js . Funkce fetch používá přístupový token a koncový bod prostředku k volání skutečného rozhraní API.

  3. V editoru kódu otevřete soubor Api/index.js a vložte kód ze souboru Api/index.js .

Získání přístupového tokenu pomocí acquireTokenOnBehalfOf

V souboru Api/index.js získá aplikace střední vrstvy (aplikace EditProfileService) přístupový token pomocí funkce acquireTokenOnBehalfOf, kterou používá k aktualizaci profilu jménem tohoto uživatele.

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

Parametr tokenRequest je definován, jak je znázorněno v následujícím kódu:

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

Ve stejném souboru rozhraní API/index.js aplikace střední vrstvy (aplikace EditProfileService) volá rozhraní Microsoft Graph API k aktualizaci profilu uživatele:

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

Testování aplikace

K otestování aplikace použijte následující postup:

  1. Pokud chcete spustit klientskou aplikaci, vytvořte okno terminálu, přejděte do adresáře aplikace a spusťte následující příkaz:

    npm start
    
  2. Pokud chcete spustit klientskou aplikaci, vytvořte okno terminálu, přejděte do adresáře rozhraní API a spusťte následující příkaz:

    npm start
    
  3. Otevřete prohlížeč a přejděte na http://localhost:3000. Pokud dojde k chybám .env certifikátu SSL, vytvořte soubor a přidejte následující konfiguraci:

    # 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. Vyberte tlačítko Přihlásit se a pak se přihlaste.

  5. Na přihlašovací stránce zadejte svoji e-mailovou adresu, vyberte Další, zadejte heslo a pak vyberte Přihlásit se. Pokud účet nemáte, vyberte Možnost Žádný účet? Vytvořte jeden odkaz, který spustí tok registrace.

  6. Pokud chcete aktualizovat profil, vyberte odkaz pro úpravy profilu. Zobrazí se stránka podobná následujícímu snímku obrazovky:

    Snímek obrazovky s profilem aktualizace uživatele

  7. Pokud chcete upravit profil, vyberte tlačítko Upravit profil . Pokud jste to ještě neudělali, aplikace vás vyzve k dokončení úkolu vícefaktorového ověřování.

  8. Proveďte změny v libovolném z podrobností profilu a pak vyberte tlačítko Uložit .