The mystery of the missing ADFS OAuth JWT claims

Kind of sounds like a new mystery for the five Find-Outers, a series of books (e.g. “The Mystery of the Spiteful Letters”) by End Blyton!

But it’s actually about why there are so many questions on stackoverflow and on the forums concerning how to augment the claims in the JWT when you are configuring applications (i.e. OpenID Connect / OAuth) in ADFS on Windows Server 2016 (aka ADFS 4.0).

They are all along the lines of “How do I add extra claims”?

There’s a good overview here — “ADFS : ADFS 4.0 with SPA”.

That article links to a stackoverflow post.

The crux is that the adal.js library for some reason does a GET and not a POST. This means that the claims would be returned in the query string and the fear is that too many claims will result in the query string overflowing. Hence the decision to not return any extra claims!

The client I’m using is the SPA sample here.

In adal.js, make the following changes:

/**
* Initiates the login process by redirecting the user to Azure AD authorization endpoint.
*/
AuthenticationContext.prototype.login = function (loginStartPage) {
// Token is not present and user needs to login
...

// var urlNavigate = this._getNavigateUrl('id_token', null) + '&nonce=' + encodeURIComponent(this._idTokenNonce);
// Force the response as a POST
var urlNavigate = this._getNavigateUrl('id_token', null) + '&response_mode=form_post&nonce=' + encodeURIComponent(this._idTokenNonce);

Looking at this on the wire, we see that the GET is now a POST:

where:

Referer = https://my-adfs.southeastasia.cloudapp.azure.com/adfs/oauth2/authorize?response_type=id_token&client_id=https%3A%2F%2Flocalhost%3A44326%2F&redirect_uri=https%3A%2F%2Flocalhost%3A44326%2Fapi%2Ftodolist%2F&state=30bb3216-5b3d-4eae-97e6-401ecd750a37&client-request-id=eed007f8-5b52-4c3c-9e18-7a19919473f5&x-client-SKU=Js&x-client-Ver=1.0.15&response_mode=form_post&nonce=53fe559b-9cf7-4de9-96df-537ce0346f5f

These are the various ways you can configure an ADFS application.

So adding a native application:

A native application on its own has no way to add extra claims. There’s no config. to do so.

The default claim set returned is:

(This is via jwt.io).

Now let’s add a web API.

The key here is the the RP identifier must match the client id in the native application above.

This results in:

Now in the web API under “Client Permissions”, we see a way to possibly add an email claim.

If you wanted to play around with this, in “app.js”, add:

extraQueryParameter: 'scope=openid email',

I played around with this but it didn’t seem to work.

Now let’s add some claims to the web API.

This results in:

Success!

Now let’s try with a web application.

As you can see from the list at the top, there is no way to add a web application on its own.

You can only do this via “Web browser accessing a web application”.

This results in:

The configuration is pretty much the same as for the web API.

Again, make sure the RP identifier matches the client id.

Let’s add some claims to the web application.

The resulting JWT is:

Success!

I also came across this post.

This says that the “allatclaims” scope needs to be included but I got it working without doing that.

There is also this —” Customize claims to be emitted in id_token when using OpenID Connect or OAuth with AD FS 2016"

As per that article:

Aside: If the above doesn’t work for you, try identityserver’s oidc-client-js library. There are samples in the docs link.

All good!