Introduction
The Open Web Interface for .NET (OWIN) defines a standard interface between .NET web servers and web applications. Katana is open-source components for building and hosting OWIN-based web applications. It provides the implementation of the OWIN specification. The OAuth authorization framework enables a third-party application to obtain limited access to a HTTP service.
Currently the preferred approach to authenticate the users is to use a signed token and this token is sent to the server with each request. The following are the benefits for using this approach.
- Scalability of Servers
the token itself contains all the information of the user that is needed for authentication, so Web Farm extension is an easy task. There is no dependence on shared session stores. - Loosely Coupling
Our front-end application is not coupled with a specific authentication mechanism. The token is generated from the server and our web API has a built-in way to understand this token and perform authentication. - Mobile Friendly
This type of authentication does not require cookies, so this authentication type can be used with mobile applications.
In the following demo application, the OAuth authorization server and the Web API endpoints will be hosted inside the same host.
Step 1 – Create and configure a Web API project
PM> Update-package Microsoft.AspNet.WebApi
Step 2 – Install the required OWIN component using Nuget Packages
Use the following commands to instal the OWIN server.
PM> Install-Package Microsoft.Owin.Host.SystemWeb
Step 3 – Create a DbContext class
public class OwinAuthDbContext : IdentityDbContext { public OwinAuthDbContext() : base("OwinAuthDbContext") { } }
Step 4 – Do the migrations (optional step)
Step 5 – Define an OWIN Startup Class
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(OwinAuthentication.Startup))] namespace OwinAuthentication { public class Startup { public void Configuration(IAppBuilder app) { } } }
Step 6 – Configure the OAuth Authorization Server
The Microsoft.AspNet.Identity namespace has the class UserManager that exposes the use related to the API that automatically saves the changes to the UserStore. Here we will interact with our database using the UserManager class.
The following code is required to use the UserManager class inside our OWIN component efficiently.
private void ConfigureOAuth(IAppBuilder app) { app.CreatePerOwinContext<OwinAuthDbContext>(() => new OwinAuthDbContext()); app.CreatePerOwinContext<UserManager<IdentityUser>>(CreateManager); } private static UserManager<IdentityUser> CreateManager (IdentityFactoryOptions<UserManager<IdentityUser>> options, IOwinContext context) { var userStore = new UserStore<IdentityUser>(context.Get<OwinAuthDbContext>()); var owinManager = new UserManager<IdentityUser>(userStore); return owinManager; }
The ConfigureOAuth method will be called inside the Configuration method of the OWIN startup class.
The UseOAuthAuthorizationServer extension method of OWIN is used to setup the authorization server. Following are the setup options:
- TokenEndpointPath
requests the path on which the client application directly communicates to obtain the access token. It must begin with a leading slash, for example “/oauth/token”. - AuthorizeEndpointPath
It is the request path where the client application will redirect the user-agent to obtain the user’s consent to issue a token or code. It must begin with a leading slash (the same as TokenEndpointPath). - AllowInsecureHttp
Set to true to allow authorize and token requests to arrive on HTTP URI addresses. - Provider
the object provided by the application to process events raised by the Authorization Server. It may the instance of OAuthAuthorizationServerProvider and assign delegates necessary for the OAuth flow. - AuthorizationCodeProvider
produces a single-use authorization code to return to the client application. It is required where the token is produced by the OnCreate/OnCreateAsync event. - RefreshTokenProvider
produces a refresh token that may produce a new access token when required. If this option is not provided then the authorization server will not return refresh tokens from the Token endpoint. - ApplicationCanDisplayErrors
Set to true when the web application is able to render error messages on the Authorize endpoint. This is required only in cases where the browser is not redirected back to the client application. - AccessTokenExpireTimeSpan
The time period the access token remains valid after it was generated. The default value is 20 minutes. - AccessTokenFormat
The data format used to protect the information contained by the access token. If it is not provided then the application will use the default data protection provider depending on the host server. - AccessTokenProvider
It produces a bearer token. - AuthorizationCodeExpireTimeSpan
The time period the authorization code remains valid after it was generated. The default value is 5 minutes - AuthorizationCodeFormat
The data format used to protect and unprotect the information contained in the authorization code. - RefreshTokenFormat
The data format used to protect and unprotect the information contained in the refresh token. - SystemClock
Used to know what the current clock time is when calculating or validating token expiration. The default value is DateTimeOffset.UtcNow.
private void ConfigureOAuth(IAppBuilder app) { …. …. app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/oauth/token"), Provider = new AuthorizationServerProvider(), AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30), AllowInsecureHttp = true, }); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); }
An Authorization Server uses a default implementation of IOAuthAuthorizationServerProvider to communicate with a web application while processing the request. OAuthAuthorizationServerProvider provides some default behavior like as used as a virtual base class and offers delegate properties that may be used to handle individual calls without creating the instance.
Here I just override the methods that I need here: ValidateClientAuthentication and GrantResourceOwnerCredentials.
ValidateClientAuthentication
GrantResourceOwnerCredentials
It is called when the request to the token endpoint arrives with a “grant_type” of “password”. This occurs when the user provides a user id and password directly to the client application using the client application user interface. If the web application supports the resource owner credentials grant type, it must validate the username and password property of context. To issue the access token, the request must end with the “OAuthGrantResourceOwnerCredentialsContext.Validated” method. The default behavior is to reject this grant type.
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider { public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { } }
The following is a probable implementation of the ValidateClientAuthentication method.
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId; string clientSecret; if (context.TryGetBasicCredentials(out clientId, out clientSecret)) { // validate the client Id and secret against database or from configuration file. context.Validated(); } else { context.SetError("invalid_client", "Client credentials could not be retrieved from the Authorization header"); context.Rejected(); } }
The following is an implementation of the GrantResourceOwnerCredentials method.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { UserManager<IdentityUser> userManager = context.OwinContext.GetUserManager<UserManager<IdentityUser>>(); IdentityUser user; try { user = await userManager.FindAsync(context.UserName, context.Password); } catch { // Could not retrieve the user due to error. context.SetError("server_error"); context.Rejected(); return; } if (user != null) { ClaimsIdentity identity = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ExternalBearer); context.Validated(identity); } else { context.SetError("invalid_grant", "Invalid User Id or password'"); context.Rejected(); }
Step 7 – Test the Project
using Newtonsoft.Json; namespace OWINTest { public class Token { [JsonProperty("access_token")] public string AccessToken { get; set; } [JsonProperty("token_type")] public string TokenType { get; set; } [JsonProperty("expires_in")] public int ExpiresIn { get; set; } [JsonProperty("refresh_token")] public string RefreshToken { get; set; } [JsonProperty("error")] public string Error { get; set; } } }
Program.cs code
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Formatting;
namespace OWINTest
{
class Program
{
static void Main(string[] args)
{
string baseAddress = "http://localhost:4312";
using (var client = new HttpClient())
{
var form = new Dictionary<string, string>
{
{"grant_type", "password"},
{"username", "javier"},
{"password", "user11111"},
};
var tokenResponse = client.PostAsync(baseAddress + "/oauth/token", new FormUrlEncodedContent(form)).Result;
//var token = tokenResponse.Content.ReadAsStringAsync().Result;
var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;
if (string.IsNullOrEmpty(token.Error))
{
Console.WriteLine("Token issued is: {0}", token.AccessToken);
}
else
{
Console.WriteLine("Error : {0}", token.Error);
}
Console.Read();
}
}
}
}
Using the following script, insert some dummy data into the AspNetUser table. Here I have encrypted my password using Abstraction for the password hashing methods of the Microsoft.AspNet.Identity namespace available with the UserManger class.
Output
We receive the response in the form of JSON and we convert it into our Token class using the Content.ReadAsAsync method. The Result Token class contains either the access token or an error. If we pass the wrong credentials, the system will generate the error:
In the next request we use this token for the authentication and the token will be sent in the request header. The Token is valid up to its expiry time.
To test it, I added a controller to my Web API project and created a test method as in the following.
using System.Web.Http;
namespace OwinAuthentication.Controllers
{
[Authorize]
public class POCController : ApiController
{
[HttpGet]
[Route("api/TestMethod")]
public string TestMethod()
{
return "Hello, ASPHostPortal Member. ";
}
}
}
Here we must mark our controller or action method with the Authorize attribute to check whether the request has a valid token.
The following is needed to append in a client application (console application in our case). First of all we need to get the token using the code described in the preceding section and then use this token to process the request.
using (HttpClient httpClient1 = new HttpClient())
{
httpClient1.BaseAddress = new Uri(baseAddress);
httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token.AccessToken);
HttpResponseMessage response = httpClient1.GetAsync("api/TestMethod").Result;
if (response.IsSuccessStatusCode)
{
System.Console.WriteLine("Success");
}
string message = response.Content.ReadAsStringAsync().Result;
System.Console.WriteLine("URL responese : " + message);
}
Output
Summary
Using this article we can set up our authorization server and we have a working OAuth 2.0 token endpoint that only supports “Resource Owner Password Credentials Grant” as of now.
Javier is Content Specialist and also .NET developer. He writes helpful guides and articles, assist with other marketing and .NET community work