Wednesday, May 27, 2015

ADFS : OpenID Connect

This is for ADFS vNext or ADFS 4.0 running on Windows Server 2016 (Technical Preview at the moment). (aka Active Directory Federation Services or "AD FS").

One of the new features is that support for OpenID Connect has been enabled.

I tried a number of clients (including Postman) and couldn't get any of them to work so I had to write my own.

Except that Dominick has already done most of the work - refer Writing an OpenID Connect Web Client from Scratch.

Fantastic - except that Github link doesn't work.

The revised one in the comments does but it is to the whole sample and it's not obvious which one to use. In fact, it's this:

Clients/MvcFormPostClient

Download all the samples because that's what the VS .sln file references.

It compiled no problem with VS 2013 on my PC.

Now if you've used Dominick's samples before, you'll know that they reference Dominick's environment so you have to ferret out all the references and change them,

But before we do that we have to find out what they are.

I run up the Technical Preview 2 in an Azure VM using my MSDN subscription. I then made it a forest of one by promoting the server to a DC so now I have AD, checked I have a certificate, created a service account and installed ADFS as a service exactly like Server 2012 R2.

(Note - after creating the Azure VM, you have to add endpoints for ports 80 and 443!).

In ADFS, click the Client tab and then "Add new OAuth Client". You need to set the settings as below because that's what the sample uses. "Name" can be anything.


Also of interest is the "Scope Descriptions":


These are the only scopes that are recognised by default.

You'll see the sample uses scopes "openid" and "email".

Back to VS,

In the Constants,cs file (part of the larger project), you need to edit for your endpoints:

public static class Constants
    {
        //public const string BaseAddress = "https://localhost:44333/core";
        public const string BaseAddress = "https://ADFS.local/adfs";
        
        //public const string AuthorizeEndpoint = BaseAddress + "/connect/authorize";
        public const string AuthorizeEndpoint = BaseAddress + "/oauth2/authorize";
        public const string LogoutEndpoint = BaseAddress + "/connect/endsession";
        //public const string TokenEndpoint = BaseAddress + "/connect/token";
        public const string TokenEndpoint = BaseAddress + "/oauth2/token";
        public const string UserInfoEndpoint = BaseAddress + "/connect/userinfo";
        public const string IdentityTokenValidationEndpoint = BaseAddress + "/connect/identitytokenvalidation";
        public const string TokenRevocationEndpoint = BaseAddress + "/connect/revocation";

        public const string AspNetWebApiSampleApi = "http://localhost:2727/";
    }

Note that the ADFS OAuth2 endpoint is .../adfs/oauth2/authorize and similarly for token as above.

Houston - we have a problem with the ValidateIdentityTokenAsync method in the AccountController class.

The Github code does not match the code in the above link.

There was a later post - refer Using Discovery and Katana Middleware to write an OpenID Connect Web Client - and this matches the Github code.

In that code, you'll see:

var certString = "MIIC4jCCA..."

OK - see where does my certificate info. come from?

As per that post, it comes from:

https://ADFS.local/adfs/.well-known/openid-configuration

which returns JSON like:

{

    "issuer": "https://ADFS.local/adfs",
    "authorization_endpoint": "https://ADFS.local/adfs/oauth2/authorize/",
    "token_endpoint": "https://ADFS.local/adfs/oauth2/token/",
    "jwks_uri": "https://ADFS.local/adfs/discovery/keys",
    "token_endpoint_auth_methods_supported": 

[

    "client_secret_post",
    "client_secret_basic",
    "private_key_jwt",
    "windows_client_authentication"

],
"response_types_supported": 
[

    "code",
    "id_token",
    "code id_token",
    "token id_token"

],
"response_modes_supported": 
[

    "query",
    "fragment",
    "form_post"

],
"grant_types_supported": 
[

    "authorization_code",
    "refresh_token",
    "client_credentials",
    "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "implicit",
    "password"

],

and more - this is truncated. But nowhere in the JSON is there any certificate information.

The key is this line as above.

"jwks_uri": "https://ADFS.local/adfs/discovery/keys"

Browse to that URL and you'll see a "x5c" section:

x5c": [ 
"MIIC4j... 
]

That Base64 string is what you paste into your code.for "var certString =".

And then you should be good to go.

Run the application, it will take you to the normal ADFS logon screen where you authenticate and then you will see:


If you use some sort of trace like "SAML Tracer" or Fiddler, you'll see a line like:


Now if you copy and paste this into a JSON viewer like:

http://jwt.io/

you'll see the same thing.

Enjoy!

No comments: