Securing ASP.NET Web API endpoints – Using OWIN, OAuth 2.0 and Claims

In this post we’re going to create some simple endpoints using ASP.NET Web API, OWIN and OAuth 2.0. To secure Controller endpoints we are using a custom claims attribute. We will issue a JSON Web Token, JWT, containing claims, that the client will use when calling the API.

OAuth 2.0 specifies four roles, Resource Owner, Client, Resource Server and Authorization Server. In our solution we combine the two latter roles in one single server. Other solutions separate these roles allowing the same Authorization Server to be used by multiple applications.

We’re going to implement OAuth 2.0 without using any of the default templates (SPA, Web API).

Scenario

We have a JavaScript web client that should be able to talk to our API. The API requires all requests to be authorized.

There are many options for securing our API.

We could use a static API key distributed to every client. However, a static API key is not ideal for our use case, since it would be easy for anyone to get hold of the API key in clear text from the client. Instead we have to get an API key per client user.

We could implement a custom API key solution, but why implement a custom one when there are standards like OAuth 2.0. OAuth 2.0 is an authorization framework that allows us to issue and consume tokens in standardized and interoperable manner.

In the templates for SPA or Web API there are a lot of helper classes to get you up and running with authentication from a mix of providers. In our case we also have different levels of privileges for the resource endpoints. Thus we also have need for authorization. This could be achieved by using claims-based authorization.

  • We want our resource endpoints to be configured to authorize through claims.

The next question is how to decide what set of privileges a user has. And how does the client get the claims? The first step is to identify the user.

  • We want users to be able to authenticate with OpenID Connect providers like Google or Azure AD.

Since it is a JavaScript client application, OAuth 2.0 implicit grant flow is suitable.

The authorization flow start

The client makes an access token request, using OAuth 2.0 by navigating with the user agent (web browser).

A request looks like this:

Note that with OAuth, the client specifies its callback endpoint uri by setting the redirect_uri query parameter. This is where the authorization flow will end.

The authentication flow

When our server receive the access token request we first have to ensure the user to be authenticated via an identity provider. Here we start an authentication flow with OpenID Connect which redirects the user agent to the identity provider. Eventually the user agent will make a request to the API servers OpenID Connect callback URI,https://myapiserver.com/openid, containing a signed JWT with the identity claims for the user.

This ends the authentication flow. However we have to get back to the authorization flow with the provided identity.

Contiunation of authorization flow

After retrieving the OpenID Connect JWT, another JWT is created by our implementation by wrapping the original OpenID Connect JWT. This makes it possible to verify that we are the issuer of the wrapped token. Other ways of transferring the identity to ourselves for later use could be using cookies. This should be considered an implementation detail.

When we have the identity of the user we show a user consent HTML page asking the user to confirm authorization for the client to use our API.

At the final stage of authentication, we issue a redirect to our consent page. If the user accepts the grant request, the user-agent makes a request to the original OAuth request URI with an additional JWT and a consent answer attached.

where {0} is the original authorization URI:

The user submits the HTML form with the consent answer:

POST request body:

Now we are back in the access token request and we know the identity of the user and we have a consent answer. Here we decide what claims are to be issued to the client based on the identity claims and a rule set.

With a set of claims we create a signed JWT containing the identity of the user and additional claims to be used when authorizing API calls.

Our server responds the client by sending a redirect response to the user agent based on the redirect_uri the client provided in the first place, now with an access token attached in the fragment part of the URI.

Note implicit vs explicit In this scenario we’re using the implicit grant flow, to keep it simple. You may want a an authorization server with full support for all OAuth 2.0 flows, like server to server and the ability to renew tokens and validate them from the issuer. See Implicit flow AKA the client flow or the OAuth 2.0 specification specification.

The full source code for the solution presented in this post could be found @ GitHub. The solution is also available as aNuGet package.

Let’s take a look at the main parts of the implementation.

Claims authorization

We’re going to create an attribute for authorization to be used in our Web API controllers.

OWIN startup

To support this scenario we’re going to utilize several middleware components. Let’s list them and their corresponding NuGet packages.

  • CORS (Microsoft.Owin.Cors)app.UseCors(...)

The resource endpoint needs to be configured for CORS.

  • OAuthAuthorizationServer (Microsoft.Owin.Security.OAuth)app.UseOAuthAuthorizationServer(...)

Used to setup our custom authorization server provider. Details in next section.

  • JWT Bearer Authentication (Microsoft.Owin.Security.Jwt)app.UseJwtBearerAuthentication(jwtOptions);

Used for accepting bearer token and setting the IClaimsPrincipal according to the token.

  • OpenIdConnect (Microsoft.Owin.Security.OpenIdConnect)app.UseOpenIdConnectAuthentication(openIdConnectAuthenticationOptions);

Used to configure our authentication provider, ex Azure AD or Google.

The core parts for our implementation is around OpenIdConnect and the OAuthAuthorizationServer. Let’s focus on them next.

OWIN OAuthAuthorizationServer middleware

To use OAuth for authorization we can utilize the UseOAuthAuthorizationServer provided by the OAuth middleware. We’re going to use a custom authorization provider.

Note that we’re using two sets of custom options – jwtOptions and providerOptions.

CustomOAuthProvider

The provider is responsible for issuing access tokens. This is the main part of our custom authorization implementation.

Our custom provider inherits from OAuthAuthorizationServerProvider. And takes our CustomProviderOptions.

It then overrides two methods form the base OAuthAuthorizationServerProvider class. ValidateClientRedirectUri andAuthorizeEndpoint.

The AuthorizeEndpoint method is central here. The flow described in the scenario section is implemented here. We’re checking the scope, if not valid we return an OAuth error in the fragment part and complete the request. Then we check for an external token (if the user is authenticated), if not, we use the challenge method in the authentication middleware. If the user is authenticated we transform (add our custom claims) and sign the token.

Let’s take a look on the options (JwtOptions and CustomProviderOptions) the provider needs.

Options

The provider options allow you to transform claims.

The provider aids you with the setup of authorization, letting you issue own claims, in a JWT token based on the JwtOptions.

To get a user authenticated, we configure the OpenID Connect middleware – up next.

OpenID Connect OWIN middleware

Since we’re using OWIN, we could use the OpenID Connect middleware package. The middleware is configured in the OWIN startup class. Ex. with Azure AD;

Note that the we create the notfications to handle consent.

CreateConsentOptions

This is a utlility to hook-up when authentication is done, with consent page support.

By default, there is an implicit consent if no implementation is provided by setting CreateConsentAsync. In this case we redirect to a consent view that will allow the user to POST the consent result back to the authorization URI.

The notifications is the used in the OpenIdConnectAuthorizationOptions, shown in the OpenIdConnect middleware section(above)

Resource endpoints

All resource endpoints should be secured by checking claims via our custom attribute. If the client doesn’t have a valid token, the API responds with a 401, to tell the client to get a token. All resource endpoints could also be configured for CORS.

Conclusion

In this post we explored what needs to be done for moving from the pure authentication scenarios often seen in the templates, towards authorization via OAuth 2.0. We implemented the OAuth 2.0 Implicit Grant flow, by using the OAuth 2.0 middleware writing our own OAuthProvider and ClaimsAuthorize attribute. It might seem as if there is a lot of custom code, but there are few core parts, that might be reused. Our server is also one step closer to a future scenario with federated authorization through an external authorization server.

Our scenario is catered for a JavaScript app, but also works with apps using WebAuthenticationBroker and alike. But we have not implemented the flow (Authorization Code Grant) for server to server scenarios. This would be the next step to explore…

Full source code @ https://github.com/jayway/JayLabs.Owin.OAuthAuthorization The NuGet package.

Also check out Dominick Baier’s blog and the projects IdentityServer and AuthorizationServer by Thinktecture.

Enjoy!

This Post Has 7 Comments

  1. In the line below, how are you defining the CustomClaims?

    [ClaimAuthorize(CustomClaims.CanChangeAddress)]

    Something like this?

    // using System.IdentityModel.Claims;
    Claim c = new Claim(“http://mysite.com/claims/mystring”, “My String Value”, Rights.PossessProperty);

    I checked your project on GitHub but could not find reference to the code, so if you could share that, it would be great. Thanks.

  2. Nice Article. Is there any sample client example which has used the above library? Just to get an idea as to how exactly the library has to be used?

  3. Thanks! Great article! Can you say something about the (dis)advantages of bearer token? Which are the other possiblity for secure WEB API and ASP.NET MVC?
    Thanks!

  4. Fastidious response in return of this matter with genuine arguments and explaining
    everything about that.

Leave a Reply

Close Menu