Introduction
In an enterprise settings the best way to implement token based authentication is to use a separate Security Token Service (STS). When users log in to the client application, the client application then sends those credentials together with its own identity information to the STS for verification. The STS examines these credentials and checks if the user has permissions on the requested resource. If everything is OK, the STS then issues a token with information about what claims or permissions the user has on requested resources, back to the client application. After receiving the token, the client application then presents the token to the resource holding server which in turn if the user has the right permissions, let them access the secure resource.
An example of a real life STS you can use is Windows Azure Active Directory or you can implement your own STS using the ThinkTecture identity server. But sometimes as developers we won't have the resources and time to implement our own STS or host a separate Identity Server, let alone dig into the inner workings of ThinkTecture identity server. Most of the time we just want a simple forms authentication like infrastructure to handle our authentication.This is what this article is about, how to implement a token based authentication mechanism with JWTs without using a separate identity server.
Background
In this article we are going to explore how we can use JWTs' in Asp.Net Web Api to implement token based authentication. We are going to take a practical approach and not dwell on the inner details of token based authentication. There is a lot of material on the internet r to read more about it. So what are Json Web Tokens? They are a standards based token authentication mechanism, based on the Internet Engineering Task Force (IETF) specification which you can find here that are used by web applications to pass claims between relying parties.
In simple terms, quoting the W3C team, token based authentication,
Allow users to enter their username and password in order to obtain a token which allows them to fetch a specific resource - without using their username and password. Once their token has been obtained, the user can offer the token - which offers access to a specific resource for a time period - to the remote site. Using some form of authentication: a header, GET or POST request, or a cookie of some kind, the site can then determine what level of access the request in question should be afforded.
How does a JWT look like? It is a JSON encoded string consisting of three base64 encoded strings separated by dots. The three parts are the header, the payload and the signature. For more information about the structure of a JWT read this article. If you want to play around with encoding and decoding JWTs go here.
Implementation
We are going to use Asp.Net Web Api and a library called Jwt to implement a basic authentication solution.
Register
When a user registers on our application with an email and password, we save their details to our database, create a token (which is a jwt) using the saved info and send back to the client application, the token together with details of the new user. The new user details will be information that allow us to access the user again, in a REST fashion. Now that they have the token, the client application will include this token in every request that the user makes to the server via an Authorization header. If want to access the secure resource, we will check the token to see if they have permissions and then let them access the resource.
[AllowAnonymous]
[Route("signup")]
[HttpPost]
public HttpResponseMessage Register(RegisterViewModel model)
{
HttpResponseMessage response;
if (ModelState.IsValid)
{
var existingUser = db.Users.FirstOrDefault(u => u.Email == model.Email);
if (existingUser != null)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "User already exist.");
}
var user = CreateUser(model);
object dbUser;
var token = CreateToken(user, out dbUser);
response = Request.CreateResponse(new {dbUser, token});
}
else
{
response = Request.CreateResponse(HttpStatusCode.BadRequest, new {success = false});
}
return response;
}
Login
Similar to the register method when a returning user logs in to our application we check the database if their credentials are valid and if they are, create a token and send it back again to the client application together with their user details. From then on wards the token is included in every request they make via the Authorization header. See code below.
[AllowAnonymous]
[Route("signin")]
[HttpPost]
public HttpResponseMessage Login(LoginViewModel model)
{
HttpResponseMessage response = null;
if (ModelState.IsValid)
{
var existingUser = db.Users.FirstOrDefault(u => u.Email == model.Email);
if (existingUser == null)
{
response = Request.CreateResponse(HttpStatusCode.NotFound);
}
else
{
var loginSuccess =
string.Equals(EncryptPassword(model.Password, existingUser.Salt),
existingUser.PasswordHash);
if (loginSuccess)
{
object dbUser;
var token = CreateToken(existingUser, out dbUser);
response = Request.CreateResponse(new {dbUser, token});
}
}
}
else
{
response = Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
return response;
}
Authentication Mechanism
So how do we check if they can access the secure resource? We do this via an Asp.Net Web Api message handler. Basically what the message handler will do is check if the request sent from the client application has an Authorization header with a valid token if not then propagate it as a normal request. If it has an authorization header, we check for any claims or roles that were passed in the token, and set the executing identity Principal to a new ClaimsPrincipal with claims contained in our token. As always propagate the request down the chain.
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
HttpResponseMessage errorResponse = null;
try
{
IEnumerable<string> authHeaderValues;
request.Headers.TryGetValues("Authorization", out authHeaderValues);
if (authHeaderValues == null)
return base.SendAsync(request, cancellationToken);
var bearerToken = authHeaderValues.ElementAt(0);
var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
var secret = "secretKey";
Thread.CurrentPrincipal = ValidateToken(
token,
secret,
true
);
if (HttpContext.Current != null)
{
HttpContext.Current.User = Thread.CurrentPrincipal;
}
}
catch (SignatureVerificationException ex)
{
errorResponse = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex.Message);
}
catch (Exception ex)
{
errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message);
}
return errorResponse != null
? Task.FromResult(errorResponse)
: base.SendAsync(request, cancellationToken);
}
Points of Interest
To test this I created a Secure resource with a list of books. I added an Authorize attribute to the controller. Without logging in, I got the following.
And after logging I was able to access the super secure Books resource.
Conclusion
The source code for the article is available on GitHub. Go ahead and try it.
Thanks
History
Published on 03 November 2015