0
votes

Okay, so I've spent the last two days with this error, and just found a solution. In my search, I didn't find an single answer solving the issue that I had (rather I found multiple that eventually pointed me to the solution). So here's my attempt at explaining you the solution to the "Access token validation failure. Invalid audience" error:

TLDR:

/**
 * Retrieve token for backend
 */
export const getToken = async (account): Promise<AuthenticationResult> => {
  return await msalInstance.acquireTokenSilent({
    scopes: [process.env.REACT_APP_API_SCOPE as string],
    redirectUri: current_url,
    account,
  });
};

/**
 * Retrieve token for Microsoft Graph API:
 */
export const getTokenForGraphApi = async (
  account
): Promise<AuthenticationResult> => {
  return await msalInstance.acquireTokenSilent({
    scopes: ["https://graph.microsoft.com/User.Read"],
    redirectUri: current_url,
    account,
  });
};

Here's the long story of how I found out:

I wanted to be able to query the Microsoft Graph API from a React application.

I've had the admin of my organization set up the Azure Portal, so that our App registration has API Permissions:

  • Backend API permission
  • Microsoft Graph
    • "User.Read"
    • "User.ReadBasic.All".

In React when I authenticate, I've used scopes:

scopes: [
    process.env.REACT_APP_API_SCOPE as string,
    "User.Read",
],

The authentication goes well, and I get an access token.

The access token works with our backend API, however when I try to use the access token with the Microsoft Graph API, I get the error:

"Access token validation failure. Invalid audience".

I read and searched forums, and I tried using jwt.ms.

Only our API is listed as "aud", and hence I suspect I need a token where both our API and "https://graph.microsoft.com" is placed.

I then tried preceding my User.Read scope with "https://graph.microsoft.com" so it would be:

scopes: [
    process.env.REACT_APP_API_SCOPE as string,
    "https://graph.microsoft.com/User.Read"
],

But it failed to authenticate with the error message:

"AADSTS28000: Provided value for the input parameter scope is not valid because it contains more than one resource. Scope api://{API-application-id}/a-scope https://graph.microsoft.com/User.Read openid profile is not valid."

Here, our backend is one resource, which has a-scope, and "https://graph.microsoft.com" is another resource with scope "User.Read".

The solution is hence to require two seperate access tokens: One with scope "https://graph.microsoft.com/User.Read", that you can use with the graph api, and another access token for your backend:

/**
 * Retrieve token for backend
 */
export const getToken = async (account): Promise<AuthenticationResult> => {
  return await msalInstance.acquireTokenSilent({
    scopes: [process.env.REACT_APP_API_SCOPE as string],
    redirectUri: current_url,
    account,
  });
};

/**
 * Retrieve token for Microsoft Graph API:
 */
export const getTokenForGraphApi = async (
  account
): Promise<AuthenticationResult> => {
  return await msalInstance.acquireTokenSilent({
    scopes: ["https://graph.microsoft.com/User.Read"],
    redirectUri: current_url,
    account,
  });
};

1

1 Answers

0
votes

The error message: "Access token validation failure. Invalid audience" tells you that the "AUD" (audience) is incorrectly set on the access token you are using with the Microsoft Graph API.

You can check your token with https://jwt.ms/ by pasting in the access token. (Microsoft is behind the site jwt.ms site: https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens)

The solution is hence to require two separate access tokens: One with scope "https://graph.microsoft.com/User.Read", that you can use with the Microsoft graph api, and another access token for your backend:

/**
 * Retrieve token for backend
 */
export const getToken = async (account): Promise<AuthenticationResult> => {
  return await msalInstance.acquireTokenSilent({
    scopes: [process.env.REACT_APP_API_SCOPE as string],
    redirectUri: current_url,
    account,
  });
};

/**
 * Retrieve token for Microsoft Graph API:
 */
export const getTokenForGraphApi = async (
  account
): Promise<AuthenticationResult> => {
  return await msalInstance.acquireTokenSilent({
    scopes: ["https://graph.microsoft.com/User.Read"],
    redirectUri: current_url,
    account,
  });
};

Then the AUD (audience) will be set correctly on both access tokens.