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

Hackered
Saturday, April 5, 2014
by Sean McAlinden

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

The password grant type is perhaps the simplest of the grant types, it is however utilised a great deal.

One of the key features of this grant type is that the resulting token represents an actual user.

 

So lets go over the basics first, the password grant type exists for getting an access token by posting a username and password to a dedicated token endpoint.

 

The request requires 3 parameters:

1. username

2. password

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

username=jbloggs&password=qwerty123&grant_type=password

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 UserService userService;

    public ApplicationOAuthProvider()
    {
        userService = new UserService();
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Should really do some validation here :)
        context.Validated();
        return base.ValidateClientAuthentication(context);
    }

    public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var user = userService.GetUser(context.UserName, context.Password);
        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);
        return base.GrantResourceOwnerCredentials(context);
    }
}

ValidateClientAuthentication

So, this method is where you should perform some validation, you can go as a far as you like but at least validate a username and password has been received.

 

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

 

GrantResourceOwnerCredentials

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

  1. First we get the user, we can place values from the user 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 username 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)

 

Lets pull it all together

 

Request a Token

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

username=jbloggs&password=pass1234&grant_type=password

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.