Showing posts with label OAuth2. Show all posts
Showing posts with label OAuth2. Show all posts

Thursday, October 05, 2017

OAuth2 : Displaying JWT tokens

The standard way that I've been looking at and debugging with JWT tokens is via:

https://jwt.io

Now, Microsoft have come to the party with:

https://jwt.ms

It starts off with


Then you copy / paste the ID token and it will display the details.

You can see the standard attributes:


or the same thing as claims rules (if this is a more familiar format for you).



And it gives you a bit of extra context.

Enjoy!

Wednesday, December 21, 2016

Swagger : Using Swagger for Implicit Grant on Azure AD

Review my previous post - Swagger : Using Swagger for Implicit Grant on ADFS 4.0.

This builds on that.

Given that I couldn't get it working on ADFS, I thought I would try Azure AD (AAD).

One of the problems with AAD is that the web API calls are protected so if you want to use Swagger, authentication always fails.

I started with the OpenID Connect - web app. with web API sample. Follow the details to configure the web app. and the web API in AAD.

I'm running this on my PC.

This is a standard "adding items to a ToDo list" project.

Then I added swashbuckle etc. as per my post above.

As always, the gist is here.

Note that AAD needs the "resource" parameter, hence the extra line:

additionalQueryStringParams: new Dictionary() { { "resource", "https://azure-tenant.onmicrosoft.com/TodoListService" } }

Also, you do not need the clientSecret for implicit flow. This is because implicit flow is normally used for SPA and the secret key could be easily exposed in the JavaScript.

Looking at the ToDoListController:

if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value != "user_impersonation")
            {
                throw new HttpResponseException(new HttpResponseMessage { StatusCode = HttpStatusCode.Unauthorized, ReasonPhrase = "The Scope claim does not contain 'user_impersonation' or scope claim not found" });
            }

            if (null != todo && !string.IsNullOrWhiteSpace(todo.Title))
            {
                todoBag.Add(new TodoItem { Title = todo.Title, Owner = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value });
            }

So we need a scope of  "user_impersonation" and we need a NameID claim.

We get the OAuth endpoints from the "View Endpoints" button in AAD. (Using the classic portal).


We need to add Swagger as an Azure AD application - the normal web site flow.

Ensure there is  "Reply URL" for:

  https://my pc/TodoListService/swagger/ui/o2c-html

We have to give this application permission to access the ToDoListService:


So what we are going to do is to use the same web API back-end but use Swagger as the web app.

Since there is a common back end, the ToDo list should reflect across both.

From the project, we add item "1234".


Now we try with Swagger.


and we get the 401.

So we click the red exclamation mark and we see:


Click "Authorize" and then authenticate with AAD.

Problem: we need to explicitly enable implicit flow. You do this via the manifest and then update:

"oauth2AllowImplicitFlow": true,

Now if we break-point at the NameID code and then check which attribute this matches in the token, we''ll see it matches the "sub":

...
"scp": "user_impersonation",
"sub": "nYa...Zow",
...

The OWIN classes must do this mapping internally viz. "sub" --> "NameID".


Now that we have authenticated, let's try again.


and we see that we get back the "1234" that we created with the web app.

Now we add a new item using the POST.

The object requires a "Title" and an "Owner" so we need to build one that looks like:

{
    "Title": "5678",
    "Owner": "nYa...Zow"
}

That returns OK and when we do another GET, we see:

[
  {
    "Title": "5678",
    "Owner": "nYa...Zow"
  },
  {
    "Title": "1234",
    "Owner": "nYa...Zow"
  }
]
 

Now go back to our web app.

so it all lines up.


Enjoy!



Monday, December 12, 2016

Swagger : Using Swagger for Implicit Grant on ADFS 4.0

This is based on this post.

I like Swagger. It maps your REST API and documents them and then allows you to test them from the documentation. Very neat.

You can write the code first and then Swagger reflects the API - much like the default API page in an MVC project or you can use the Swagger UI link to write the contract first and then generate.

Very much along the lines of code first vs. contract first.

(Aside: Once upon a time, I used SOAP and wsdl's and the WSCF (Web Services Contract First) project from thinktecture that does much the same thing. That article is dated from 2006! The more things change ...)

ADFS 4.0 (Server 2016) opens up the OpenID Connect / OAuth stack with full support for all four grant types (refer previous blog entries for a number of examples of this).

So it was a no brainer to try and put the two together.

I used this as a start point and that post gives a good overview of Swagger and how to integrate into a .NET MVC project with NuGet Swashbuckle.

The problem of course is that you need to authenticate the web API and the only OAuth type support in Swagger as I write is the implicit flow.

So following the article, as usual the gist is here.

You will notice that there is a scope called "sampleui". It's simple to add this to the Scopes section in the ADFS wizard.

So we run up the project in VS 2015 and then navigate to:

https://localhost:44326/swagger

(or whatever port your web API project uses).


Notice the highlighted exclamation mark in red.

Click that.


Check the boxes, click "Authorize".

It should take you off to the ADFS login page where you authenticate.


 Notice the exclamation mark is now blue (and if you click it you can Logout).

Now we put a value e.g. 5 in the "id" box and click "Try it out".

And it returns:

{
  "Message": "Authorization has been denied for this request."
}

I've tried everything I can think of  but I can't get this to work.

The JWT returned is:

{
  "aud": "microsoft:identityserver:7b23c943-6782-4d5b-b56c-7ecd59da17f2",
  "iss": "http://my-adfs/adfs/services/trust",
  "iat": 1480889151,
  "exp": 1480892751,
  "apptype": "Public",
  "appid": "7b2...7f2",
  "authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
  "auth_time": "2016-12-04T22:05:50.803Z",
  "ver": "1.0",
  "scp": "sampleapi user_impersonation"
}

and when I access the API normally from a client in the VS project, I get the JWT:

{
  "aud": "https://localhost:44326/NativeTodoListService1",
  "iss": "http://my-adfs/adfs/services/trust",
  "iat": 1480889366,
  "exp": 1480892966,
  "apptype": "Confidential",
  "appid": "0cc...549",
  "authmethod": "http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password",
  "auth_time": "2016-12-04T22:09:26.486Z",
  "ver": "1.0"
}

I suspect the problem is that the "aud" is wrong. But I can't figure out how to get rid of the "microsoft:identityserver" one?

So close :-(

Any bright ideas?

Enjoy!

Monday, September 12, 2016

OAuth2 : Verifying the ADFS JWT signature

I wrote up this post recently: OAuth2 : Verifying the Azure AD JWT signature.

So how do you do this with ADFS? You need ADFS 4.0 - Server 2016.

This has the following ADFS OAuth information ( ~ metadata) endpoints:



If we go to the "keys" endpoint, we see:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "hHk-...A6k",
      "x5t": "hHk-...A6k",
      "n": "tGy...w9Q",
      "e": "AQAB",
      "x5c": [
        "MIIC7..Log=="
      ]
    }
  ]
}

The information under "x5c" is the certificate that you need.

As  per the linked post, you need to wrap this with "---BEGIN--- ---END---" and copy / paste and you'll see that the signing key is now verified.

Enjoy!



Thursday, August 18, 2016

OAuth2 - Testing OpenID Connect / OAuth 2.0

Utilities to help you develop using OpenID Connect and OAuth 2.0 are always useful so I've listed out the ones I know about.

Postman - Really useful to for API testing. Requires Chrome.

v2.0 Protocols - OAuth 2.0 Authorization Code Flow - Article that has links to Postman collection to try this out step by step. Azure AD centric.

Inspecting the JWT token - from Auth0.

Outlook OAuth Sandbox - for testing Outlook.Office.com API.

OpenID Connect Playground - from Auth0 - for developers to test and work with OpenID Connect calls.

Demo. instance of IdentityServer - has full stack OIDC / OAuth2 support.

Tying the two together - hooking the playground up with IdentityServer.

Enjoy!

Monday, August 15, 2016

OAuth2 : Verifying the Azure AD JWT signature

Was having a look at Azure AD and JWT tokens and was wondering how the signature was calculated?

I use this useful utility from Auth0 to decode the tokens.

So I paste either the access or identity token into the "Encoded" box and set the "Algorithm" drop down to "RS256" (as below in bold). Problem is the signature is invalid. So how do I verify it.?

Looking at a typical token header,

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "MnC_VZcATfM5pOYiJHMba9goEKY"
}


we see the algorithm and the reference to the public key.

In Azure AD, we can get the OAuth 2.0 details from the discovery endpoint viz.



and that gives us:

"authorization_endpoint": "https://login.microsoftonline.com/common/oauth2/
    authorize",
    "token_endpoint": "https://login.microsoftonline.com/common/oauth2/token",
    "token_endpoint_auth_methods_supported": [
        "client_secret_post",
        "private_key_jwt"
    ],
    "jwks_uri": "https://login.microsoftonline.com/common/discovery/keys",
    "response_modes_supported": [
        "query",
        "fragment",
        "form_post"
    ],
... 

Now if we go to the key endpoint (bold above), we see:

"keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "MnC_VZcATfM5pOYiJHMba9goEKY",
            "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY",
            "n": "vIqz-4-ER_vNWLON9yv8hIYV737...NOhfXgelixLUQ",
            "e": "AQAB",
            "x5c": [
                "MIIC4jCCAc...H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ=="
            ]
        },
... 

There is  more than one of these sets. You need the one whose "kid" matches the "kid" in the JWT header.

The "x5c" is the public key. Copy the entire string e.g. from "MIIC" to "JQ==" in the above to Notepad.

Now add:

"-----BEGIN CERTIFICATE-----"

at the beginning and add:

"-----END CERTIFICATE-----"

at the end.

You should have something like:

-----BEGIN CERTIFICATE-----
MIIC4jCCAc
...
H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ==
-----END CERTIFICATE-----

Now paste that into the "Verify Signature" box and you'll see the "Signature Verified" message at the bottom in blue.

Enjoy!

Monday, May 02, 2016

ADFS : Server 2016 OAuth JWT is missing scope and NameID

This is for Active Directory Federation Services on Server 2016 Technical Preview 4 / 5.

As per ADFS : Daemon and Web API on Server 2016 TP4 ADFS 4.0, I made the comment:

"The Azure AD sample relies on scope and NameID claims being returned in the JWT token. This is OK in Azure AD where the claims are static and Azure ID knows the ID of the application which is returned as a GUID in the NameID claim. ADFS AFAIK does not have claims rules for this so I hard coded the NameID. Hopefully, this will be resolved as more documentation is forthcoming."

Then I came across AD FS On-behalf-of Authentication in Windows Server 2016.

As per that article, you have to manually generate the scope claims!

They use the following claims rules:
@RuleName = "All claims"
c:[]
=> issue(claim = c);

@RuleName = "Issue open id scope"
=> issue(Type = "http://schemas.microsoft.com/identity/claims/scope", 
Value = "openid");

@RuleName = "Issue user_impersonation scope"
=> issue(Type = "http://schemas.microsoft.com/identity/claims/scope", 
Value = "user_impersonation");
These match the scope check boxes configured in ADFS in the application group.

This means that every time you alter the scopes you need to alter the claims rules.

Hopefully, this will be fixed before the official release.

The other issue I had was with NameID.

As per that article:

"From AD FS we are issuing the Nmae claim but we are not issuing NameIdentifier claim. The sample uses NameIdentifier to uniquely key in the ToDo items. For simplicity, you can safely remove the NameIdentifier with Name claim in the code. Find and replace all occurences of NameIdentifier wiht Name."

(Spelling as per original article.)

My solution was simply to generate a static NameID. The whole point is to have a unique key into the token store.

Enjoy!

ADFS : OpenID Connect and OAuth2 support on Server 2016 TP5

This is for Active Directory Federation Services on Server 2016 Technical Preview 5.

This is the last TP before RTM.

I've done a series of posts on this capability in TP4 and I tried a few of the use cases in TP5 and they worked no problem.

All you need to do is to change the metadata URL and the clientID and the secret key.

So much easier when you have a working sample as reference :-).

There is a similar series of Microsoft posts here. I believe that more content is going to be added prior to RTM.

The format of these posts is somewhat different to the way that I do them but I will leave you to decide which way you prefer!

Enjoy!

Thursday, April 28, 2016

ADFS : Daemon and Web API on Server 2016 TP4 ADFS 4.0

This follows on from my previous series of posts around taking the Azure AD OpenID Connect / OAuth2 samples and getting them to run on ADFS on TP4.

This uses the active-directory-dotnet-daemon sample that has a Windows console application calling a Web API using its application identity.

This is for Active Directory Federation Services on Server 2016 Technical Preview 4.

Just ignore all the Azure AD comments. There is no Azure in this solution.

In the solution, I've set the Web API to be at localhost:44326.

Just to re-iterate - the ADFS has to be Server 2016 - TP4 and above. This will not work on Server 2012 R2 - ADFS 3.0.

As before, the changes are all in a gist here.

This uses the client credential flow with ADAL i.e.

result = authContext.AcquireToken(todoListResourceId, clientCredential); 

where clientCredential is a combination of the clientID and the secret key. There is no authentication pop up where a user name and password needs to be entered.

The Azure AD sample relies on scope and NameID claims being returned in the JWT token. This is OK in Azure AD where the claims are static and Azure ID knows the ID of the application which is returned as a GUID in the NameID claim. ADFS AFAIK does not have claims rules for this so I hard coded the NameID. Hopefully, this will be resolved as more documentation is forthcoming. (But see ADFS : Server 2016 OAuth JWT is missing scope and NameID).

On to the ADFS configuration:

New Application Group - "Server Application and Web API".

For the Server Application:


You also need to generate a secret key. Copy this key before you leave the page because you can't get back to it. (You can, however, generate a new one).

The Client ID and the secret key need to be copied into the daemon app.config here:

 <add key="ida:ClientId" value="bee24b9a-13ac-45fc-988c-8cce06160c07" />
 <add key="ida:AppKey" value="wp...nE" />

For the Web API:






The custom claim rule for NameID is:

 => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Value = "abcd");


Now run up the sample and the console application will show something like:

Posting to To Do list at 28/04/2016 9:03:27 p.m.
Successfully posted new To Do item:  Task at time: 28/04/2016 9:03:27 p.m.

Retrieving To Do list at 28/04/2016 9:03:33 p.m.
Task at time: 28/04/2016 9:02:10 p.m.
Task at time: 28/04/2016 9:03:27 p.m.
Task at time: 28/04/2016 9:02:36 p.m.
Total item count:  3

Posting to To Do list at 28/04/2016 9:03:46 p.m.


Enjoy!