ASP.Net MVC: Creating an OAuth client credentials grant type token endpoint

Hackered
Monday, April 7, 2014
by Sean McAlinden

The new OWIN compatible middleware built into ASP.Net makes creating OAuth endpoints very straight forward.

The client credentials grant type is most commonly used for granting applications access to a set of services.

 

The client credential grant type gets access token by posting a client id and client secret to a dedicated token endpoint.

 

The request requires 3 parameters:

1. client_id

2. client_secret

3. grant_type

 

Usually sent using the x-www-form-urlencoded content type, the request body would look similar to the following:

GET https://mydomain.com/token HTTP/1.1
Content-type:application/x-www-form-urlencoded

client_id=987459827985&client_secret=lkfjldsfjkld&grant_type=client_credentials

So, making the request is pretty straight forward, it's time to take a look at implementing the token endpoint.

 

Startup.Auth.cs

Spin up an ASP.Net MVC site and leave the Authentication set to Individual User Accounts.

Open the Startup.Auth.cs file which can be found in the App_Start folder.

 

Clear the code from the class or merge the following (up to you):

public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

static Startup()
{
    OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/Token"),
        Provider = new ApplicationOAuthProvider(),
        AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
        AllowInsecureHttp = true,
        AuthenticationMode = AuthenticationMode.Active
    };
}

public void ConfigureAuth(IAppBuilder app)
{
    app.UseOAuthBearerTokens(OAuthOptions);
}

There are loads of different configuration options, the above are some of the most common.

Token Endpoint Path: does what it says, it is the endpoint you need to call to get a token.

 

Provider:  this instantiates the class for validating and granting tokens, we will be creating the ApplicationOAuthProvider custom class within this post.

 

AccessTokenExpireTimeSpan: this is the default amount of time a token will last. This can be overridden within the ApplicationOAuthProvider custom class.

 

AllowInsecureHttp: this just allows non https traffic to get a token, useful during development.

 

AuthenticationMode: we will be using active authentication in this example.

 

UseOAuthBearerTokens: The use of bearer token is a pretty standard way of passing tokens within a request header.

Once you have a token, you can call a secured endpoint with the following header to gain access:

Authorization: Bearer {THE TOKEN}

ApplicationOAuthProvider

The following class is by no means production code, it is just to illustrate the main players involved in creating a usable token.

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    private ClientService clientService;

    public ApplicationOAuthProvider()
    {
        this.clientService = new ClientService();
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        string clientId;
        string clientSecret;
        context.TryGetFormCredentials(out clientId, out clientSecret);

        if (clientId == "1234" && clientSecret == "12345")
        {
            context.Validated(clientId);
        }

        return base.ValidateClientAuthentication(context);
    }

    public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    {
        var client = clientService.GetClient(context.ClientId);
        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);
        return base.GrantClientCredentials(context);
    }
}

ValidateClientAuthentication

This method is for validating the input, you can used this method to verify the client id and secret are valid.

 

The main thing to note is that unless you call context.Validated(clientId), the request will be considered unauthorized.

 

GrantClientCredentials

This method is where we will create the client access token.

  1. First we get the client, we can place values from the client record into the tokens claim collection.
  2. We then create a new ClaimsIdentity.
  3. We add some claims, in the example I've just added the client name but you can add multiple claims of anything you like into the claims collection.
  4. Create an AuthenticationTicket using our claims identity.
  5. Validate the ticket (you do need to do this or the client will be considered unauthenticated)

Lets pull it all together

 

Request a Token

POST http://localhost:19923/Token
Content-Type: Application/x-www-form-urlencoded

client_id=987459827985&client_secret=lkfjldsfjkld&grant_type=client_credentials

Token Response

{"access_token":"_slaaSSj9UH-UoXaqHMJx4ULscGH-5sKR_qZmuM-TGGQxEGEgB8biMre9-BWrTm2xzzYDRz7IIWgpwjxcRVyvdyLyOPYPJWUy0tsTAoJ8d5Sjn5vvHFrGZrPw1X_XEPhLqvjHzrzK9flR7MLTL8lUH09TwKM08xZsdBj5oTsOTF6LQIrTjluF2oLz_olByG6YO0_hMAMowdVrehA6SCxtA","token_type":"bearer","expires_in":3599}

Call a secured endpoint

By secure, I mean an endpoint that is decorated by the standard Authorize attribute.

GET http://localhost:19923/MySecuredEndpoint
Authorization: Bearer _slaaSSj9UH-UoXaqHMJx4ULscGH-5sKR_qZmuM-TGGQxEGEgB8biMre9-BWrTm2xzzYDRz7IIWgpwjxcRVyvdyLyOPYPJWUy0tsTAoJ8d5Sjn5vvHFrGZrPw1X_XEPhLqvjHzrzK9flR7MLTL8lUH09TwKM08xZsdBj5oTsOTF6LQIrTjluF2oLz_olByG6YO0_hMAMowdVrehA6SCxtA

The Bearer token is the access_token value from the Token Response.

 

And We're Authenticated

Not only that, we also have access to the claims on the server so we can use the claims to personalise or provide authorization rules to our endpoints.

To access the claims, cast the usual principal identity to a claims identity and check out the Claims collection.

var claims = ((ClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims


Anyway, I hope this has been useful, claims based security is an excellent approach to security and identity and the new OWIN compatible libraries have made it much easier to implement.