Search code examples
azurevue.jsauthenticationasp.net-web-apiazure-active-directory

JWT Token invalid msal


I am currently building a web app using Asp.net core web API as backend and Vue as frontend. THe authentication is realized through msal. I can successfully authenticate within the frontend but when I try to send an HTTP request against the asp.net web api to a protected endpoint, I get a 401 response even tho the JWT is contained in the request header. As I'm pretty new to the authentication world, I'm hoping for any help here.

appsettings.json in backend

  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<my-tenant-id>",
    "ClientId": "<my-client-id>",
    "Audience": "api://<app id>"
  }

authConfig.ts in frontend

import { LogLevel, PublicClientApplication } from '@azure/msal-browser';

export const msalConfig = {
  auth: {
    clientId: '<my-client-id>',
    authority: 'https://login.microsoftonline.com/<my-tenant-id>'
  },
  cache: {
    cacheLocation: 'localStorage'
  },
  system: {
    loggerOptions: {
      loggerCallback: (level: LogLevel, message: string, containsPii: boolean) => {
        if (containsPii) {
          return;
        }
        switch (level) {
          case LogLevel.Error:
            console.error(message);
            return;
          case LogLevel.Info:
            console.info(message);
            return;
          case LogLevel.Verbose:
            console.debug(message);
            return;
          case LogLevel.Warning:
            console.warn(message);
            return;
          default:
            return;
        }
      },
      logLevel: LogLevel.Verbose
    }
  }
};

export const msalInstance = new PublicClientApplication(msalConfig);

export const loginRequest = {
  scopes: ['api://<app id>/full']
};

export const graphConfig = {
  graphMeEndpoint: 'https://graph.microsoft.com/v1.0/me'
};

axios.ts in frontend

import axios from 'axios';

import { msalInstance, loginRequest, graphConfig } from './authConfig';

const apiClient = axios.create({
  baseURL: 'https://localhost:7158'
});

apiClient.interceptors.request.use(async (config) => {
  const activeAccount = msalInstance.getActiveAccount();
  const accounts = msalInstance.getAllAccounts();

  if (!activeAccount && accounts.length === 0) {
    return config;
  }

  try {
    const token = await msalInstance.acquireTokenSilent({
      ...loginRequest,
      account: activeAccount || accounts[0]
    });
    config.headers.Authorization = `Bearer ${token.idToken}`;
  } catch (error) {
    console.error();
  }

  return config;
});

export { apiClient };

The response I get has the following error: "Bearer error="invalid_token", error_description="The audience '' is invalid""

The error_description is only present when specifying the audience uri in the appsettings.json in backend. When I do not specify it, only the error but not the error_description is present.

I tried debugging and checked if the jwt was really valid using jwt.io until I came accross that I have to add the audience to the appsettings.json in the backend.


Solution

  • I generated access token using below parameters via Postman:

    https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
    
    client_id:ClientID
    grant_type:authorization_code
    scope:api://ClientID/test.read
    code:code
    redirect_uri:https://jwt.ms
    client_secret:ClientSecret
    

    enter image description here

    When I decoded the access token, the aud is ClientID:

    enter image description here

    Now, I tried to call the graphMeEndpoint using the above generated access token and got the similar error:

    GET https://graph.microsoft.com/v1.0/me
    

    enter image description here

    The error "The audience is invalid" usually occurs if you are access token audience is not matching the API you are calling.

    In your scenario, the error occurred as you are generating access token for web API and calling Microsoft Graph API.

    To resolve the error, Add Microsoft Graph API permissions in the Azure AD Application:

    enter image description here

    Generate the access token by passing scope as https://graph.microsoft.com/.default

    enter image description here

    When I decoded the access token, the aud is https://graph.microsoft.com:

    enter image description here

    I am able to call the graphMeEndpoint successfully:

    GET https://graph.microsoft.com/v1.0/me
    

    enter image description here

    In your case, to resolve the error modify the code like below:

    appsettings.json file

      "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "TenantId": "<my-tenant-id>",
        "ClientId": "<my-client-id>",
        "Audience": "https://graph.microsoft.com"
      }
    

    authConfig.ts file

    export const msalInstance = new PublicClientApplication(msalConfig);
    
    export const loginRequest = {
      scopes: ['user.read']
    };
    
    export const graphConfig = {
      graphMeEndpoint: 'https://graph.microsoft.com/v1.0/me'
    };
    

    If you want to call the Microsoft Graph API, then change the aud to Graph. If you want to call the Web API, then no need of changing the aud to Graph API.