1

For a test scenario I want to create an access token for an AAD Application with the APPLICATION_ID as aud claim.

I created a correct token using Azure CLI (after I authorized the Azure CLI Application using Expose an API view).

$ az login -u <USERNAME> --allow-no-subscription
$ az account get-access-token --tenant <TENANT_ID> --resource <APPLICATION_ID>

I tried something similar in Node.js using @azure/ms-rest-nodeauth.

async function getToken(): Promise<string> {
  const credentials = await msRestNodeAuth.loginWithUsernamePassword(
    USERNAME,
    PASSWORD, {
      domain: TENANT_ID,
      tokenAudience: APPLICATION_ID,
    },
  );
  return (await credentials.getToken()).accessToken;
}

The code returns a Bearer token, but I got 401 when using it. It turned out, that the aud claim was set in a different way than the Azure CLI does. The Node code prefixes the value with spn:, which is a documented behavior for the SAML protocol (see Audience section).

I tried also to use the ../oauth2/v2.0/token REST endpoint with grant_type=password, but wasn't able to get any token.

How can I create an user token with exactly APPLICATION_ID as aud claim?

5
  • Have you checked the Important in the document about ROPC flow? Commented Jan 28, 2021 at 6:33
  • @PamelaPeng Yes. It's used for grant_type=password approach. I got AADSTS50126: Error validating credentials due to invalid username or password. as error message. Commented Jan 28, 2021 at 7:19
  • 1
    Personal accounts that are invited to an Azure AD tenant can't use ROPC. Is your account an Azure AD Account that looks like "xxx@{tenant-name}.onmicrosoft.com"? Commented Jan 28, 2021 at 7:30
  • @PamelaPeng It's not a personal account. It's an Azure AD account but managed by another Azure AD instance (source: External Azure Active Directory, Creation Type: Invitation). Are there other authentication flows than ROPC, which can create user tokens programmatically? Commented Jan 28, 2021 at 7:57
  • You could use client credentials flow without user, Or use auth code flow with signed-in user. Code sample here. Commented Jan 28, 2021 at 8:30

2 Answers 2

1

Add my comment as an answer:

When using ROPC flow(means grant_type=password), we need the Azure AD Account of current tenant, but not the invited account. You could create a test AD user to obtain access token using this flow. For more details, see here.

Note: Microsoft recommends you do not use the ROPC flow.

If you would like to get the token without user login, client credentials flow will be better.

If you would like to get the token with a signed-in user, auth code flow could be used. However, the user needs to be logged in on the browser every time when requesting to get the token.

Code samples using ADAL: https://github.com/AzureAD/azure-activedirectory-library-for-nodejs/tree/master#windows-azure-active-directory-authentication-library-adal-for-nodejs


If you specify the resource as the {Guid}, the Audience value wil be prefixed with spn:. You could replace GUID with ID URI, looks like "api://xxx" or "https://graph.microsoft.com".

The simiar issue: AzureAD JWT Token Audience claim prefix makes JWT Token invalid

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. It's not obvious to me, how to programmatically get the access token using auth code flow. The adal-node documentation looks quite general. Could you give some additional hints, please?
A newer simple using MSAL.js 2.0 with PKCE: github.com/Azure-Samples/ms-identity-javascript-v2
0

It turned out, that Azure CLI does three steps to create the application specific access token.

  1. Authenticates the user credentials via SAML.
  2. Requests general user authorization using grant_type: 'urn:ietf:params:oauth:grant-type:saml1_1-bearer'
  3. Requests application specific authorization using grant_type: 'refresh_token'

I was able to create a valid token without spn: prefix using the REST API via axios.

async function getToken(): Promise<string> {
  const credentials = await msRestNodeAuth.loginWithUsernamePassword(
    USERNAME,
    PASSWORD, {
      domain: TENANT_ID,
    },
  );
  // Mimic Azure CLI`s 'az account get-access-token' to avoid spn: prefix
  const { refreshToken } = (await credentials.getToken());
  const resp = await axios.request<{ access_token: string }>({
    method: 'POST',
    url: `https://login.microsoftonline.com/${TENANT_ID}/oauth2/token`,
    data: qs.stringify({
      grant_type: 'refresh_token',
      client_id: credentials.clientId,
      resource: APPLICATION_ID,
      refresh_token: refreshToken,
    }),
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  });
  return resp.data.access_token;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.