User Authentication in ASP.NET WEB API 2 with RSA-signed JWT Tokens (part 4)

published in Android Development, iOS Development, Tutorials
by Michał Zawadzki

After creating, signing and verifying the JWT Token, we can move on to programming the login controller and testing our application.

Login controller: MembershipController

In this part we’ll create the MembershipController with one GET method, which will return a JWT token as a compact string after receiving user’s login data.

Our endpoint URL is:


/api/Membership/Authenticate?username=admin93&password=password

using System;
using System.Web.Http;
using System.Threading.Tasks;
using Zaven.Practices.Auth.JWT.Services.Interfaces;
...
public class MembershipController : ApiController
    {
        private readonly IAuthService _authService;
        
        public MembershipController(IAuthService authService)
        {
            _authService = authService;
        }

        // GET: Membership
        [HttpGet]
        public async Task Authenticate(String username, String password)
        {
            string Token = await _authService.GenerateJwtTokenAsync(username, password);
            return Token;
        }
    }

After receiving the token our web app should store it in browser data and place it in the header of every request.

Authorization: Bearer

Exemplary controller: ValuesController

To check if the authentication process works, let’s create a simple ApiController ValuesController, which methods were applied with filter as a TokenAuthenticate attribute.


using JWTAuth.Filters;
using System.Collections.Generic;
using System.Web.Http;
...
[TokenAuthenticate]
    public class ValuesController : ApiController
    {
	...
        // GET api/values/5 
        public string Get(int id)
        {
            return "You have access to this data:\n- Very important data number 5";
        }
	... 
    }

TokenAuthentication filter

Finally we have to implement the TokenAuthenticate filter.


using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using Zaven.Practices.Auth.JWT.Services.Interfaces;
...
public class TokenAuthenticate : Attribute, IAuthenticationFilter
    {
       
        public bool AllowMultiple
        {
            get
            {
                return false;
            }
        }

        public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
        {
            try
            {
                IAuthService _authService = context.ActionContext.ControllerContext.Configuration
                                  .DependencyResolver.GetService(typeof(IAuthService)) as IAuthService;

                HttpRequestMessage request = context.Request;
                AuthenticationHeaderValue authorization = request.Headers.Authorization;
                
                if (authorization == null)
                {
                    context.ErrorResult = new AuthenticationFailureResult("Missing autorization header", request);
                    return;
                }
                if (authorization.Scheme != "Bearer")
                {
                    context.ErrorResult = new AuthenticationFailureResult("Invalid autorization scheme", request);
                    return;
                }                   
                if (String.IsNullOrEmpty(authorization.Parameter))
                {
                    context.ErrorResult = new AuthenticationFailureResult("Missing Token", request);
                    return;
                }

                Boolean correctToken = await _authService.ValidateTokenAsync(authorization.Parameter);
                if(!correctToken)
                    context.ErrorResult = new AuthenticationFailureResult("Invalid Token", request);                
            }
            catch (Exception ex)
            {
                context.ErrorResult = new AuthenticationFailureResult("Exception: \n" + ex.Message, context.Request);
            }            
        }
...
    }

The most interesting is the AuthenticateAsync which, in this case, is responsible for reading the token from the HTTP request header and validating it (calling the ValidateTokenAsync() from AuthService). Depending on the result, the are three outcomes: a positive authentication, moving on to controllers method or sending the response with a 401 code.

401 response: AuthenticationFailureResult

In case the HTTP request header lacks the “Authorization: Bearer” part (or basically any other kind of error), context.ErrorResult will filter it out and set the IHttpActionResult. This part is responsible for providing error response codes (e.g. 401 if unauthorized) as a type of AuthenticationFailureResult. Below, you can see the code in action.


using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
...
public class AuthenticationFailureResult : IHttpActionResult
    {
        public string ReasonPhrase { get; private set; }
        public HttpRequestMessage Request { get; private set; }

        public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
        {
            ReasonPhrase = reasonPhrase;
            Request = request;
        }

        public Task ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(Execute());
        }

        private HttpResponseMessage Execute()
        {
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
            response.RequestMessage = Request;
            response.ReasonPhrase = ReasonPhrase;
            return response;
        }
    }

Application test

When running our app in the console, you should see the initial request being sent. In response you should get a JWT token, which is then added to the HTTP headers of any further requests. If the token is accepted, we get access to our protected data.

JWT token

Summary

JWT token is a quick and easy way to authenticate access to server resources. The transmitted data can be signed and thanks to that they’re safe and resistant to man-in-the-middle attacks. RSA signng is fast, it doesn’t slow down your application and the token itself doesn’t have to be stored in the database.

Check out other tutorials on our blog.

Sources:

Michał Zawadzki Back-end Developer

Michał is at the Back-end site of our software team, specializing in .NET web apps. Apart from being an excellent table tennis player, he’s very much into sci-fi literature and computer games.

Popular posts

Effortless deployment of Azure CosmosDB JS functions with GitLab-CI

Effortless deployment of Azure CosmosDB JS functions with GitLab-CI

Working with CosmosDB JavaScript triggers and stored procedures can be problematic, especially when your application consists of three or four separate development environments. The solution we need is found in the effortless deployment of Azure CosmosDB JS functions with GitLab-CI.

Read more
Interviews with startup founders: Fonn Byggemappen

Interviews with startup founders: Fonn Byggemappen

Fonn Byggemappen is an iOS, Android and web application for construction industry projects. The tool’s co-founder Jan Tore Grindheim told us about the beginnings and crucial moments of the app.

Read more
Core ML: Machine Learning for iOS

Core ML: Machine Learning for iOS

You have probably heard phrases like “Machine Learning” or “Artificial Intelligence” without knowing exactly what they mean. In this article, I will shed some light on “What Machine Learning is” by showing you a Core ML iOS sample.

Read more
Mobile Apps

Get your mobile app in 3 easy steps!

1

Spec out

with the help of our
business analyst

2

Develop

design, implement
and test, repeat!

3

Publish

get your app out
to the stores

Contact us

Hire us now!

back to top