Acquire an access token in your Node.js web app

Applies to: White circle with a gray X symbol. Workforce tenants Green circle with a white check mark symbol. External tenants (learn more)

In this article, you update your code, to enable your web app to acquire an access token. You use Microsoft Authentication Library (MSAL) for Node to simplify adding authentication and authorization to your node web application. This article is the third part of a four-part guide series.

Prerequisites

Update MSAL configuration object

In your code editor, open authConfig.js file, then update the code by adding the protectedResources object:

    //..   
    const toDoListReadScope = process.env.TODOLIST_READ || 'api://Enter_the_Web_Api_Application_Id_Here/ToDoList.Read';
    const toDoListReadWriteScope = process.env.TODOLIST_READWRITE || 'api://Enter_the_Web_Api_Application_Id_Here/ToDoList.ReadWrite';
    
    const protectedResources = {
        toDoListAPI: {
            endpoint: 'https://localhost:44351/api/todolist',
            scopes: {
                read: [toDoListReadScope],
                write: [toDoListReadWriteScope],
            },
        },
    };    
    module.exports = {
        //..
        protectedResources,
        //..
    };

In your authConfig.js file, replace Enter_the_Web_Api_Application_Id_Here with the Application (client) ID of the web API app that you registered in your customer's tenant.

The todolistReadScope and todolistReadWriteScope variables hold the web API full scope URLs that you set in your external tenant. Make sure you export the protectedResources object.

Acquire access token

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

    const axios = require('axios');
    class AuthProvider {
    //...
        getToken(scopes) {
            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: 'http://localhost:3000/todos',
                                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);
                }
            };
        }
    //...
    }
  • First, the function attempts to acquire an access token silently (without prompting the user for credentials):

    const silentRequest = {
        account: req.session.account,
        scopes: scopes,
    };
    
    const tokenResponse = await msalInstance.acquireTokenSilent(silentRequest);
    
  • If you successfully acquire a token silently, store it in a session. You retrieve the token from the session when you call an API.

    req.session.accessToken = tokenResponse.accessToken;
    
  • If you fail to acquire the token silently (such as with InteractionRequiredAuthError exception), request an access token afresh.

Note

Once your client application receives an access token, it should treat it as an opaque string. The access token is intended for the API, not for the client application. Therefore, the client application should not attempt to read or process the access token. Instead, it should include the access token as-is in the Authorization header of its requests to the API. The API is responsible for interpreting the access token and using it to authenticate and authorize the client application’s requests.

Next step