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.
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}
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); } }
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.
This method is where we will create the client access token.
POST http://localhost:19923/Token Content-Type: Application/x-www-form-urlencoded client_id=987459827985&client_secret=lkfjldsfjkld&grant_type=client_credentials
{"access_token":"_slaaSSj9UH-UoXaqHMJx4ULscGH-5sKR_qZmuM-TGGQxEGEgB8biMre9-BWrTm2xzzYDRz7IIWgpwjxcRVyvdyLyOPYPJWUy0tsTAoJ8d5Sjn5vvHFrGZrPw1X_XEPhLqvjHzrzK9flR7MLTL8lUH09TwKM08xZsdBj5oTsOTF6LQIrTjluF2oLz_olByG6YO0_hMAMowdVrehA6SCxtA","token_type":"bearer","expires_in":3599}
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.
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.