Background information

Token based authentication, using Json Web Tokens (Jwt) has gained popularity with web developers recently and it  is taking over as the future of authenticating clients over the internet. 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 wanna play around with encoding and decoding JWT go here.

Ideal Solution

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.

Our Solution

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.");
}</code>

//Create user and save to database
var user = CreateUser(model);

object dbUser;

//Create token
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); // cross fingers

                var bearerToken = authHeaderValues.ElementAt(0);
                var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;

                //var secret = ConfigurationManager.AppSettings.Get("jwtKey");
                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);
        }

Testing

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.

books_auth1

 

And after logging in

books_auth2

 

Conclusion

The source code for the article is available on GitHub. Go ahead and try it.

Thanks

8 Comments


© Stewart Mbofana 2017