Thursday, December 22, 2016

AAD : The V2.0 endpoints

I've been helping a few people lately with this and I see the same few issues that continually trip people up.

Table to help decide whether to use the v2.0 endpoints or not.

And note that currently the "Resource owner password credentials" grant is not supported.

Have a look at the current restrictions.

The endpoints are different:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize
https://login.microsoftonline.com/common/oauth2/v2.0/token

The scopes are different. They now take the form of a URI e.g.

"scope": "https://outlook.office.com/mail.read https://outlook.office.com/mail.send"

You can use the default scope e.g.

https://graph.microsoft.com/.default

This means that the application uses the pre-defined scope.

You need to use the Application Registration Portal to register the application. Don't use the usual Azure AD Applications screens.

You need to cater for the admin. consent endpoint if you want to grant consent for the application to anyone who can authenticate.

Use the MSAL library rather than the ADAL library.

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!