Freigeben über


Bearbeiten eines Profils in einer Node.js-Web-App

Gilt für: Weißer Kreis mit grauem X. Mitarbeitermandanten Grüner Kreis mit weißem Häkchen. Externe Mandanten (weitere Informationen)

Dieser Artikel ist Teil 2 einer Reihe, die veranschaulicht, wie die Profilbearbeitungslogik in einer Node.js Web-App hinzugefügt wird. In Teil 1 dieser Reihe richten Sie Ihre App für die Profilbearbeitung ein.

In diesem Leitfaden erfahren Sie, wie Sie die Microsoft Graph-API für die Profilbearbeitung aufrufen.

Voraussetzungen

Abschließen der Clientweb-App

In diesem Abschnitt fügen Sie den identitätsbezogenen Code für die Clientweb-App hinzu.

Aktualisieren der „authConfig.js“-Datei

Aktualisieren Sie die authConfig.js-Datei für die Clientweb-App:

  1. Öffnen Sie in Ihrem Code-Editor die Datei "App/authConfig.js", und fügen Sie dann drei neue Variablen hinzu, GRAPH_API_ENDPOINTGRAPH_ME_ENDPOINT und fügen Sie dann drei neue Variablen hinzueditProfileScope. Stellen Sie sicher, dass Sie die drei Variablen exportieren:

    //...
    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,
        //...
    };
    
    • Die editProfileScope Variable stellt eine MFA-geschützte Ressource dar, d. h. die Mid-Tier-App (EditProfileService-App).

    • GRAPH_ME_ENDPOINT ist der Microsoft Graph-API-Endpunkt.

  2. Ersetzen Sie den Platzhalter {clientId} durch die Anwendungs-ID (Client-ID) der Mid-Tier-App (EditProfileService-App), die Sie zuvor registriert haben.

Abrufen eines Zugriffstokens in der Clientweb-App

Öffnen Sie in Ihrem Code-Editor die Datei "App/auth/AuthProvider.js ", und aktualisieren Sie dann die getToken Methode in der AuthProvider Klasse:

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

Die getToken Methode verwendet den angegebenen Bereich, um ein Zugriffstoken abzurufen. Der redirectUri-Parameter ist die Umleitungs-URL, nachdem die App ein Zugriffstoken abgerufen hat.

Aktualisieren der „users.js“-Datei

Öffnen Sie in Ihrem Code-Editor die Datei "App/routes/users.js ", und fügen Sie dann die folgenden Routen hinzu:

    //...
    
    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);
    }
  },
);
    //...
  • Sie lösen die /gatedUpdateProfile Route aus, wenn der Benutzer des Kunden den Profilbearbeitungslink auswählt. Die App erfüllt diese Funktionen:

    1. Erwirbt ein Zugriffstoken mit der Berechtigung "User.Read ".
    2. Ruft die Microsoft Graph-API auf, um das Profil des angemeldeten Benutzers zu lesen.
    3. Zeigt die Benutzerdetails auf der gatedUpdateProfile.hbs-Benutzeroberfläche an.
  • Sie lösen die /updateProfile Route aus, wenn der Benutzer seinen Anzeigenamen aktualisieren möchte, d. h. er wählt die Schaltfläche "Profil bearbeiten" aus. Die App erfüllt diese Funktionen:

    1. Ruft die Mid-Tier-App (EditProfileService-App) mit editProfileScope-Bereich auf. Durch einen Aufruf an die Mid-Tier-App (EditProfileService-App) muss der Benutzer eine MFA-Abfrage ausführen, sofern dies noch nicht geschehen ist.
    2. Zeigt die Benutzerdetails auf der updateProfile.hbs-Benutzeroberfläche an.
  • Sie lösen die /update-Route aus, wenn der Benutzer die Schaltfläche Speichern entweder in gatedUpdateProfile.hbs oder updateProfile.hbs auswählt. Die App erfüllt diese Funktionen:

    1. Ruft das Zugriffstoken für die App-Sitzung ab. Sie erfahren, wie die Mid-Tier-App (EditProfileService-App) das Zugriffstoken im nächsten Abschnitt erwirbt.
    2. Sammelt alle Benutzerdetails.
    3. Ruft die Microsoft Graph-API auf, um das Profil des Benutzers zu aktualisieren.

Aktualisieren der „fetch.js“-Datei

Die App verwendet die Datei "App/fetch.js ", um die tatsächlichen API-Aufrufe vorzunehmen.

Öffnen Sie in Ihrem Code-Editor die Datei "App/fetch.js ", und fügen Sie dann die OPTION PATCH-Vorgang hinzu. Nachdem Sie die Datei aktualisiert haben, sollte die resultierende Datei dem folgenden Code ähneln:

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

Abschließen der Mid-Tier-App

In diesem Abschnitt fügen Sie den identitätsbezogenen Code für die Mid-Tier-App (EditProfileService-App) hinzu.

  1. Öffnen Sie in Ihrem Code-Editor die Datei "API/authConfig.js ", und fügen Sie dann den folgenden Code hinzu:

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

    Suchen Sie den folgenden Platzhalter:

    • Enter_the_Tenant_Subdomain_Here und ersetzen Sie sie durch die Unterdomäne "Verzeichnis(Mandant)". Wenn Ihre primäre Mandantendomäne beispielsweise contoso.onmicrosoft.com lautet, verwenden Sie contoso. Wenn Sie Ihren Mandantennamen nicht kennen, erfahren Sie hier, wie Sie Ihre Mandantendetails auslesen.
    • Enter_the_Tenant_ID_Here und ersetzen Sie sie durch Mandanten-ID. Wenn Sie nicht über Ihre Mandanten-ID verfügen, erfahren Sie, wie Sie Ihre Mandantendetails lesen.
    • Enter_the_Edit_Profile_Service_Application_Id_Here und ersetzen Sie ihn durch den Wert der Anwendungs-ID (Client) des zuvor registrierten EditProfileService.
    • Enter_the_Client_Secret_Hereund ersetzen Sie ihn durch den geheimen Wert der EditProfileService-App, den Sie zuvor kopiert haben.
    • graph_end_point, und ersetzen Sie sie durch den Microsoft Graph-API-Endpunkt, https://graph.microsoft.com/.
  2. Öffnen Sie in Ihrem Code-Editor die Datei "API/fetch.js ", und fügen Sie dann den Code aus der Api/fetch.js-Datei ein. Die fetch Funktion verwendet ein Zugriffstoken und den Ressourcenendpunkt, um den tatsächlichen API-Aufruf vorzunehmen.

  3. Öffnen Sie in Ihrem Code-Editor die Datei "API/index.js ", und fügen Sie dann den Code aus der Api/index.js-Datei ein.

Abrufen eines Zugriffstokens mithilfe von acquireTokenOnBehalfOf

In der Datei "API/index.js " erwirbt die Mid-Tier-App (EditProfileService-App) ein Zugriffstoken mithilfe der AcquireTokenOnBehalfOf-Funktion , die zum Aktualisieren des Profils im Namen dieses Benutzers verwendet wird.

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

Der tokenRequest Parameter wird wie im folgenden Code dargestellt definiert:

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

In derselben Datei führt api/index.js die Mid-Tier-App (EditProfileService-App) einen Aufruf der Microsoft Graph-API durch, um das Profil des Benutzers zu aktualisieren:

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

Testen Ihrer App

Führen Sie die folgenden Schritte aus, um Ihre App zu testen:

  1. Um die Client-App auszuführen, bilden Sie das Terminalfenster, navigieren Sie in das App-Verzeichnis , und führen Sie dann den folgenden Befehl aus:

    npm start
    
  2. Um die Client-App auszuführen, bilden Sie das Terminalfenster, navigieren Sie zum API-Verzeichnis , und führen Sie dann den folgenden Befehl aus:

    npm start
    
  3. Öffnen Sie Ihren Browser, und navigieren Sie zu http://localhost:3000.. Wenn SSL-Zertifikatfehler auftreten, erstellen Sie eine .env-Datei, und fügen Sie die folgende Konfiguration hinzu:

    # 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. Wählen Sie die Schaltfläche Anmelden aus, und melden Sie sich an.

  5. Geben Sie auf der Anmeldeseite Ihre E-Mail-Adresse ein, wählen Sie Weiter aus, geben Sie Ihr Kennwort ein und wählen Sie dann Anmelden aus. Wenn Sie kein Konto haben, wählen Sie den Link Kein Konto? Erstellen Sie eins aus, um den Registrierungsflow zu starten.

  6. Um das Profil zu aktualisieren, wählen Sie den Link "Profilbearbeitung " aus. Die daraufhin angezeigte Seite sieht in etwa wie im folgenden Screenshot aus:

    Screenshot des Updates für das Benutzerprofil.

  7. Um das Profil zu bearbeiten, wählen Sie die Schaltfläche "Profil bearbeiten" aus. Wenn Sie dies noch nicht getan haben, werden Sie von der App aufgefordert, eine MFA-Überprüfung durchzuführen.

  8. Nehmen Sie Änderungen an den Profildetails vor, und wählen Sie dann die Schaltfläche "Speichern " aus.