Search code examples
node.jsoauth-2.0azure-active-directoryazure-ad-msalmsal.js

Authentication using the OAuth 2.0 authorization code flow with the Microsoft Identity Platform (Azure AD) and @azure/msal-node in a Node.js backend


I want to implement authentication using the OAuth 2.0 authorization code flow with the Microsoft Identity Platform (Azure AD) and the @azure/msal-node library in a Node.js backend.

So I can call graph api on successful authentication.

I tried to adopt code from official example at https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-nodejs-webapp-msal 

Here is my code:

const express = require('express');
const { PublicClientApplication, LogLevel } = require('@azure/msal-node');

// Initialize the MSAL client with your authentication configuration
const msalConfig = {
  auth: {
    clientId: process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRET,
    authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`,
    redirectUri: 'http://localhost:3000/redirect'
  },
  system: {
    loggerOptions: {
      loggerCallback(loglevel, message, containsPii) {
        console.log(message);
      },
      piiLoggingEnabled: false,
      logLevel: LogLevel.Info
    }
  }
};

const msalClient = new PublicClientApplication(msalConfig);

// Create an Express app
const app = express();

// Define a route for initiating the login process
app.get('/login', async (req, res) => {
  const authCodeUrlParameters = {
    scopes: ['openid', 'profile', 'offline_access', 'Calendars.Read'],
    redirectUri: 'http://localhost:3000/redirect'
  };

  // Generate the authorization URL
  const authUrl = await msalClient.getAuthCodeUrl(authCodeUrlParameters);
  console.log('alok', authUrl)

  // Redirect the user to the authorization URL
  res.redirect(authUrl);
});

// Define a route for handling the redirect callback
app.get('/redirect', async (req, res) => {
  const tokenRequest = {
    code: req.query.code,
    scopes: ['openid', 'profile', 'offline_access', 'Calendars.Read'],
    redirectUri: 'http://localhost:3000/redirect'
  };

  try {
    // Acquire an access token using the authorization code
    const response = await msalClient.acquireTokenByCode(tokenRequest);

    // use token now and save it for call graph api
    res.send('Authentication successful!');
  } catch (error) {
    // Handle the token acquisition error
    console.log(error);
    res.send('Authentication failed.');
  }
});

// Start the server
app.listen(3000, () => {
  console.log('Server started on http://localhost:3000');
});

After successful login I am getting the code via http://localhost:3000/redirect?code=something
But I am not able to get token in the line await msalClient.acquireTokenByCode(tokenRequest) I am getting error

ServerError: invalid_client: 7000218 - [2023-05-19 12:26:56Z]: AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
Trace ID: 1852de3e-3310-4503-a9c4-985e35880f00
Correlation ID: 22c042a3-0777-4151-a364-edad3312ccee
Timestamp: 2023-05-19 12:26:56Z - Correlation ID: 22c042a3-0777-4151-a364-edad3312ccee - Trace ID: 1852de3e-3310-4503-a9c4-985e35880f00
    at ServerError.AuthError [as constructor] (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:498:24)
    at new ServerError (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:3383:28)
    at ResponseHandler.validateTokenResponse (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:5414:19)
    at AuthorizationCodeClient.<anonymous> (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:5722:41)
    at step (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:79:23)
    at Object.next (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:60:53)
    at fulfilled (/home/alok/tmp/test-node/node_modules/@azure/msal-common/dist/index.cjs.js:50:58)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  errorCode: 'invalid_client',
  errorMessage: "7000218 - [2023-05-19 12:26:56Z]: AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\n" +
    'Trace ID: 1852de3e-3310-4503-a9c4-985e35880f00\r\n' +
    'Correlation ID: 22c042a3-0777-4151-a364-edad3312ccee\r\n' +
    'Timestamp: 2023-05-19 12:26:56Z - Correlation ID: 22c042a3-0777-4151-a364-edad3312ccee - Trace ID: 1852de3e-3310-4503-a9c4-985e35880f00',
  subError: ''
}

I tried to get help from

  1. How do I resolve the error AADSTS7000218: The request body must contain the following parameter: 'client_secret' or 'client_assertion'
  2. AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret
  3. The request body must contain the following parameter: 'client_assertion' or 'client_secret
  4. ROPC flow: The request body must contain the following parameter: 'client_assertion' or 'client_secret'

Out of these none are helpful for me.

What I am missing exactly so I am getting this error?


Solution

  • Note that, you need to enable Allow public client flows to "Yes" and add redirect URI by selecting platform as Mobile and desktop applications while using PublicClientApplication.

    I cloned this Github sample and registered one Azure AD application by adding redirect URI in Web platform as below:

    enter image description here

    When I ran the sample and authenticated successfully, it asked for consent like below:

    enter image description here

    After accepting the above consent, I got code value in address bar and same error as you like this:

    enter image description here

    When I checked the code terminal, I got similar logs as you like below:

    enter image description here

    To resolve the error, I removed redirect URI from Web platform and added it in Mobile and desktop applications platform by enabling Allow public client flows to "Yes" like below:

    Redirect URI:

    enter image description here

    Enable public client flow:

    enter image description here

    Note that, there is no need to pass client secret in code if you make above changes while using PublicClientApplication.

    When I ran the sample again after making above changes, I got below response like this:

    enter image description here

    In Browser:

    enter image description here

    In your case, check if you added redirect URI as Web and change it to Mobile and desktop applications platform by enabling Allow public client flows to "Yes".