Click here to Skip to main content
15,508,376 members
Articles / Web Development / ASP.NET
Article
Posted 3 Nov 2015

Stats

100.2K views
23 bookmarked

Simple Authentication using Jwt in Asp.Net Web Api 2.2

Rate me:
Please Sign up or sign in to vote.
4.85/5 (17 votes)
3 Nov 2015CPOL4 min read
In this article we examine how to use Json Web tokens as an authentication mechanism in Asp.Net Web Api 2.2.

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

                //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.

C#
[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.

C#
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);
}

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.

Image 1

And after logging I was able to access the super secure Books resource.

Image 2

Conclusion

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

Thanks

History

Published on 03 November 2015

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Kube
South Africa South Africa
Software developer out of Cape Town, originally from Zimbabwe. I speak Shona fluently and I was born a mathematician.

Comments and Discussions

 
Questionproblem with JWT.encoded Pin
Member 1412906216-Apr-20 14:28
Member 1412906216-Apr-20 14:28 
QuestionWhy the server response with 401 if i send the Token via axios? Pin
Member 1248631522-Jun-18 15:31
Member 1248631522-Jun-18 15:31 
QuestionChanges in JWT library Pin
Member 1372598914-Mar-18 11:35
Member 1372598914-Mar-18 11:35 
QuestionJsonWebToken Pin
beyazcennet28-Sep-17 7:01
beyazcennet28-Sep-17 7:01 
AnswerRe: JsonWebToken Pin
Stewart Mbofana2-Oct-17 4:52
professionalStewart Mbofana2-Oct-17 4:52 
QuestionAwesome!!! Pin
Member 1025087820-Mar-17 9:36
Member 1025087820-Mar-17 9:36 
QuestionThe AuthorizeAttribute is not working for me Pin
distonefis16-Feb-17 17:17
distonefis16-Feb-17 17:17 
AnswerRe: The AuthorizeAttribute is not working for me Pin
Stewart Mbofana21-Feb-17 5:44
professionalStewart Mbofana21-Feb-17 5:44 
QuestionQUick question. Where is the CreateToken() method defined? Pin
Member 127791516-Oct-16 0:48
Member 127791516-Oct-16 0:48 
AnswerRe: QUick question. Where is the CreateToken() method defined? Pin
Stewart Mbofana10-Oct-16 1:49
professionalStewart Mbofana10-Oct-16 1:49 
GeneralRe: QUick question. Where is the CreateToken() method defined? Pin
Member 1277915112-Oct-16 11:44
Member 1277915112-Oct-16 11:44 
GeneralRe: QUick question. Where is the CreateToken() method defined? Pin
Member 1277915112-Oct-16 20:28
Member 1277915112-Oct-16 20:28 
GeneralRe: QUick question. Where is the CreateToken() method defined? Pin
Stewart Mbofana13-Oct-16 2:22
professionalStewart Mbofana13-Oct-16 2:22 
Questionthanks Pin
hcchavez15-Sep-16 18:13
hcchavez15-Sep-16 18:13 
QuestionSimple and great example, but how to refresh token? Pin
Farhan Ghumra1-Sep-16 0:23
professionalFarhan Ghumra1-Sep-16 0:23 
AnswerRe: Simple and great example, but how to refresh token? Pin
Stewart Mbofana13-Oct-16 2:26
professionalStewart Mbofana13-Oct-16 2:26 
GeneralRe: Simple and great example, but how to refresh token? Pin
Farhan Ghumra13-Oct-16 2:46
professionalFarhan Ghumra13-Oct-16 2:46 
GeneralRe: Simple and great example, but how to refresh token? Pin
Stewart Mbofana17-Oct-16 5:59
professionalStewart Mbofana17-Oct-16 5:59 
GeneralRe: Simple and great example, but how to refresh token? Pin
Farhan Ghumra17-Oct-16 21:21
professionalFarhan Ghumra17-Oct-16 21:21 
Thanks Stewart for understanding me Smile | :)

GeneralRe: Simple and great example, but how to refresh token? Pin
Kostas_T24-Apr-17 4:56
Kostas_T24-Apr-17 4:56 
GeneralRe: Simple and great example, but how to refresh token? Pin
Stewart Mbofana25-Apr-17 0:29
professionalStewart Mbofana25-Apr-17 0:29 
QuestionNot validating properly Pin
ganku0079-May-16 4:25
ganku0079-May-16 4:25 
AnswerRe: Not validating properly Pin
Stewart Mbofana9-May-16 10:09
professionalStewart Mbofana9-May-16 10:09 
GeneralMy vote of 2 Pin
Member 1241810728-Mar-16 5:01
Member 1241810728-Mar-16 5:01 
GeneralRe: My vote of 2 Pin
Stewart Mbofana9-May-16 10:09
professionalStewart Mbofana9-May-16 10:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.