Skip to content

Latest commit

 

History

History
425 lines (346 loc) · 16.5 KB

how-to-web-app-node-edit-profile-update-profile.md

File metadata and controls

425 lines (346 loc) · 16.5 KB
titledescriptionmanagerauthorms.authorms.servicems.topicms.datems.custom
Edit profile in a Node.js web app
Learn how to edit profile with multifactor authentication protection in your external-facing Node.js web app
mwongerapz
kengaderdus
kengaderdus
identity-platform
how-to
03/14/2025

Edit profile in a Node.js web app

[!INCLUDE applies-to-external-only]

This article is part 2 of a series that demonstrates how to add the profile editing logic in a Node.js web app. In part 1 of this series, you set up your app for profile editing.

In this how-to guide, you learn how to call Microsoft Graph API for profile editing.

Prerequisites

Complete the client web app

In this section, you add the identity related code for the client web app.

Update authConfig.js file

Update the authConfig.js file for the client web app:

  1. In your code editor, open App/authConfig.js file, then add three new variables, GRAPH_API_ENDPOINT, GRAPH_ME_ENDPOINT and editProfileScope. Make sure to export the three variables:

    //...constGRAPH_API_ENDPOINT=process.env.GRAPH_API_ENDPOINT||"https://graph.microsoft.com/";// https://learn.microsoft.com/graph/api/user-update?tabs=httpconstGRAPH_ME_ENDPOINT=GRAPH_API_ENDPOINT+"v1.0/me";consteditProfileScope=process.env.EDIT_PROFILE_FOR_CLIENT_WEB_APP||'api://{clientId}/EditProfileService.ReadWrite';module.exports={//... editProfileScope,GRAPH_API_ENDPOINT,GRAPH_ME_ENDPOINT,//...};
    • The editProfileScope variable represents MFA protected resource, that's the mid-tier app (EditProfileService app).

    • The GRAPH_ME_ENDPOINT is the Microsoft Graph API endpoint.

  2. Replace the placeholder {clientId} with the Application (client) ID of the mid-tier app (EditProfileService app) that you registered earlier.

Acquire access token in client web app

In your code editor, open App/auth/AuthProvider.js file, then update the getToken method in the AuthProvider class:

classAuthProvider{//...getToken(scopes,redirectUri="http://localhost:3000/"){returnasyncfunction(req,res,next){constmsalInstance=authProvider.getMsalInstance(authProvider.config.msalConfig);try{msalInstance.getTokenCache().deserialize(req.session.tokenCache);constsilentRequest={account: req.session.account,scopes: scopes,};consttokenResponse=awaitmsalInstance.acquireTokenSilent(silentRequest);req.session.tokenCache=msalInstance.getTokenCache().serialize();req.session.accessToken=tokenResponse.accessToken;next();}catch(error){if(errorinstanceofmsal.InteractionRequiredAuthError){req.session.csrfToken=authProvider.cryptoProvider.createNewGuid();conststate=authProvider.cryptoProvider.base64Encode(JSON.stringify({redirectTo: redirectUri,csrfToken: req.session.csrfToken,}));constauthCodeUrlRequestParams={state: state,scopes: scopes,};constauthCodeRequestParams={state: state,scopes: scopes,};authProvider.redirectToAuthCodeUrl(req,res,next,authCodeUrlRequestParams,authCodeRequestParams,msalInstance);}next(error);}};}}//...

The getToken method uses the specified scope to acquire an access token. The redirectUri parameter is the redirect URL after the app acquires an access token.

Update the users.js file

In your code editor, open the App/routes/users.js file, then add the following routes:

//...var{ fetch }=require("../fetch");const{GRAPH_ME_ENDPOINT, editProfileScope }=require('../authConfig');//...router.get("/gatedUpdateProfile",isAuthenticated,authProvider.getToken(["User.Read"]),// check if user is authenticatedasyncfunction(req,res,next){constgraphResponse=awaitfetch(GRAPH_ME_ENDPOINT,req.session.accessToken,);if(!graphResponse.id){returnres.status(501).send("Failed to fetch profile data");}res.render("gatedUpdateProfile",{profile: graphResponse,});},);router.get("/updateProfile",isAuthenticated,// check if user is authenticatedauthProvider.getToken(["User.Read",editProfileScope],"http://localhost:3000/users/updateProfile",),asyncfunction(req,res,next){constgraphResponse=awaitfetch(GRAPH_ME_ENDPOINT,req.session.accessToken,);if(!graphResponse.id){returnres.status(501).send("Failed to fetch profile data");}res.render("updateProfile",{profile: graphResponse,});},);router.post("/update",isAuthenticated,authProvider.getToken([editProfileScope]),asyncfunction(req,res,next){try{if(!!req.body){letbody=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){returnres.redirect("/");}else{next("Not updated");}}).catch((error)=>{console.log("error,",error);});}else{throw{error: "empty request"};}}catch(error){next(error);}},);//...
  • You trigger the /gatedUpdateProfile route when the customer user selects the Profile editing link. The app:

    1. Acquires an access token with the User.Read permission.
    2. Makes a call to Microsoft Graph API to read the signed-in user's profile.
    3. Displays the user details in the gatedUpdateProfile.hbs UI.
  • You trigger the /updateProfile route when the user wants to update their display name, that's, they select the Edit profile button. The app:

    1. Makes a call to the mid-tier app (EditProfileService app) using editProfileScope scope. By making a call to the mid-tier app (EditProfileService app), the user must complete an MFA challenge if they've not already done so.
    2. Displays the user details in the updateProfile.hbs UI.
  • You trigger the /update route when the user selects the Save button in either gatedUpdateProfile.hbs or updateProfile.hbs. The app:

    1. Retrieves the access token for app session. You learn how the mid-tier app (EditProfileService app) acquires the access token in the next section.
    2. Collects all user details.
    3. Makes a call to Microsoft Graph API to update the user's profile.

Update the fetch.js file

The app uses the App/fetch.js file to make the actual API calls.

In your code editor, open App/fetch.js file, then add the PATCH operation option. After you update the file, the resulting file should look similar to the following code:

varaxios=require('axios');varauthProvider=require("./auth/AuthProvider");/** * Makes an Authorization "Bearer" request with the given accessToken to the given endpoint. * @param endpoint * @param accessToken * @param method */constfetch=async(endpoint,accessToken,method="GET",data=null)=>{constoptions={headers: {Authorization: `Bearer ${accessToken}`,},};console.log(`request made to ${endpoint} at: `+newDate().toString());switch(method){case'GET': constresponse=awaitaxios.get(endpoint,options);returnawaitresponse.data;case'POST': returnawaitaxios.post(endpoint,data,options);case'DELETE': returnawaitaxios.delete(endpoint+`/${data}`,options);case'PATCH': returnawaitaxios.patch(endpoint,ReqBody=data,options);default: returnnull;}};module.exports={ fetch };

Complete the mid-tier app

In this section, you add the identity related code for the mid-tier app (EditProfileService app).

  1. In your code editor, open Api/authConfig.js file, then add the following code:

    require("dotenv").config({path: ".env.dev"});constTENANT_SUBDOMAIN=process.env.TENANT_SUBDOMAIN||"Enter_the_Tenant_Subdomain_Here";constTENANT_ID=process.env.TENANT_ID||"Enter_the_Tenant_ID_Here";constREDIRECT_URI=process.env.REDIRECT_URI||"http://localhost:3000/auth/redirect";constPOST_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 */constmsalConfig={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 GUIDauthority: process.env.AUTHORITY||`https://${TENANT_SUBDOMAIN}.ciamlogin.com/`,// Replace the placeholder with your external tenant nameclientSecret: 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",},},};constGRAPH_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=httpconstGRAPH_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,};

    Find the placeholder:

    • Enter_the_Tenant_Subdomain_Here and replace it with Directory (tenant) subdomain. For example, if your tenant primary domain is contoso.onmicrosoft.com, use contoso. If you don't have your tenant name, learn how to read your tenant details.
    • Enter_the_Tenant_ID_Here and replace it with Tenant ID. If you don't have your Tenant ID, learn how to read your tenant details.
    • Enter_the_Edit_Profile_Service_Application_Id_Here and replace it with is the Application (client) ID value of the EditProfileService you registered earlier.
    • Enter_the_Client_Secret_Here and replace it with the EditProfileService app secret value you copied earlier.
    • graph_end_point and replace it with the Microsoft Graph API endpoint, that's https://graph.microsoft.com/.
  2. In your code editor, open Api/fetch.js file, then paste the code from Api/fetch.js file. The fetch function uses an access token and the resource endpoint to make the actual API call.

  3. In your code editor, open Api/index.js file, then paste the code from Api/index.js file.

Acquire an access token by using acquireTokenOnBehalfOf

In the Api/index.js file, the mid-tier app (EditProfileService app) acquires an access token using the acquireTokenOnBehalfOf function, which it uses to update the profile on behalf of that user.

asyncfunctiongetAccessToken(tokenRequest){try{constresponse=awaitcca.acquireTokenOnBehalfOf(tokenRequest);returnresponse.accessToken;}catch(error){console.error("Error acquiring token:",error);throwerror;}}

The tokenRequest parameter is defined as shown the following code:

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

In the same file, API/index.js, the mid-tier app (EditProfileService app) makes a call to Microsoft Graph API to update the user's profile:

letaccessToken=awaitgetAccessToken(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});});

Test your app

To test your app, use the following steps:

  1. To run the client app, form the terminal window, navigate into the App directory, then run the following command:

    npm start
  2. To run the client app, form the terminal window, navigate into the API directory, then run the following command:

    npm start
  3. Open your browser, then go to http://localhost:3000. If you experience SSL certificate errors, create a .env file, then add the following configuration:

    # 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. Select the Sign In button, then you sign in.

  5. On the sign-in page, type your Email address, select Next, type your Password, then select Sign in. If you don't have an account, select No account? Create one link, which starts the sign-up flow.

  6. To update profile, select the Profile editing link. You see a page similar to the following screenshot:

    :::image type="content" source="media/how-to-web-app-node-edit-profile-update-profile/edit-user-profile.png" alt-text="Screenshot of user update profile.":::

  7. To edit profile, select the Edit Profile button. If you haven't already done so, the app prompts you to complete an MFA challenge.

  8. Make changes to any of the profile details, then select Save button.

Related content

close