Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AzureAD JWT Token Audience claim prefix makes JWT Token invalid

I'm using the 'adal-node' npm package to authenticate with an AzureAD. This is all working fine and I get a token back.

However, the when examining the 'aud' claim in the JWT token I see the audience GUID is prefixed with 'spn:'. I think this is causing me problems when I try to use the JWT token on an already existing Web API. When I authenticate via a WebApp using the same AzureAD the 'aud' claim is NOT prefixed with 'spn:' and I am able to called endpoints on the WebAPI.

Can anyone shed any light on this? This is last hurdle to get over after a lot of head banging getting this working.

Update:

Using the npm package 'azure-ad-jwt' to validate the JWT token with AzureAD as soon as I receive it gives me the error which I suspected is the problem - 'JWT audience is invalid'. It is expecting the 'aud' claim not to have the 'spn:' prefix. Where is this spn prefix coming from?

Here's my app.js

var adal = require('adal-node');

var activeDirectoryEndpointUrl = 'https://login.microsoftonline.com/';

var options = {
    domain: '<AzureAD GUID>',
    activeDirectoryResourceId: '<AzureAD App Client ID 1>',
    clientId: '<AzureAD App Client ID 2>'
};

var tokenCache = new adal.MemoryCache();
var authorityUrl = activeDirectoryEndpointUrl + options.domain;
var context = new adal.AuthenticationContext(authorityUrl, true, tokenCache);

context.acquireUserCode(options.activeDirectoryResourceId, options.clientId, 'en-us', function (err, userCodeResponse) {
    if (err) {
        console.error(err);
        return;
    }

    console.log('Use a web browser to open the page ' + userCodeResponse.verificationUrl + ' and enter the code ' + userCodeResponse.userCode + ' to sign in.');

    context.acquireTokenWithDeviceCode(options.activeDirectoryResourceId, options.clientId, userCodeResponse, function (err, tokenResponse) {
        if (err) {
            console.error(err);
            return;
        }

        console.log(tokenResponse);
    });
});

Decoded JWT Token:

{
    "typ":"JWT",
    "alg":"RS256",
    "x5t":"XXXXXXX",
    "kid":"XXXXXXX"
}
{
    "aud":"spn:XXXXXXX",    // <<< Offending claim
    "iss":"https://sts.windows.net/XXXXXXX/",
    "iat":1471355868,
    "nbf":1471355868,
    "exp":1471359768,
    "acr":"1",
    "amr":["pwd"],
    "appid":"XXXXXXX",
    "appidacr":"0",
    "e_exp":7200,
    "family_name":"XX",
    "given_name":"XX",
    "ipaddr":"XX.XX.XX.XX",
    "name":"XX XX",
    "oid":"XXXXXXX",
    "scp":"user_impersonation",
    "sub":"XXXXXXX",
    "tid":"XXXXXXX",
    "unique_name":"[email protected]",
    "upn":"[email protected]",
    "ver":"1.0"
}
like image 421
Adam Young Avatar asked Aug 16 '16 15:08

Adam Young


1 Answers

Appears to be documented "by design"1 for Azure AD.

In Azure AD's SAML 2.0 Single Sign-On SAML protocol describing the response fields, they describe the audience response value. Note the bold text at the bottom:

Audience

This contains a URI that identifies an intended audience. Azure AD sets the value of this element to the value of Issuer element of the AuthnRequest that initiated the sign-on. To evaluate the Audience value, use the value of the App ID URI that was specified during application registration.

Like the Issuer value, the Audience value must exactly match one of the service principal names that represents the cloud service in Azure AD. However, if the value of the Issuer element is not a URI value, the Audience value in the response is the Issuer value prefixed with spn:.

So for better or worse, the answer for Azure AD SAML 2.0 seems to be either:

  1. Change your Issuer Entity ID to be a URI
  2. Change your implementation to strip the spn: from the start of the audience response value.

1 The SAML 2.0 Core spec specifies the <Issuer> element not as a URI, but as a complex type NameIDType, a string with no format requirements by default. So we can feel annoyed that Azure AD is not happy with non-URI string. Having said that, every example Issuer in the spec is a URI, so maybe our annoyed self-justification has its limits.

like image 135
Daryn Avatar answered Oct 01 '22 07:10

Daryn