Overview
This blog posts demonstrates how a custom authentication scheme can be used in DotNet Core Web API.
Recap the basics
We all know this but it's always good to remind ourselves of this - authentication is the proces of determing whothe person is or determining if the user is really who he says he is. Authorization is determining if the user is allowed to perform a certain operation.
The six guiding principles of RESTful APIs talk nothing of authentication nor authorization. The example discussed here will break the stateful principle by storing an authentication state of users. That's OK since the main aim is to discuss how a custom authentication scheme can be implemented. The technique discussed here can be used without maintaining state on the server which is how JWTs work.
The authentication machanism
The service has two endpoints:
- WeatherForecast - requires the caller to be already authenticated. Returns weather forecast data
- authenticate - used by a caller for authenticating itself with the service
The authentication flows is depicted in the image below.
Protecting the weather forecast route
The Get() method is protected by adding an Authorize attribute. We also specify the name of the authentication scheme.[HttpGet] [Authorize(AuthenticationSchemes = TokenAuthenticationSchemeOptions.Name)] public IEnumerableGet() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); }
The important thing to note is we are specifying the custom authentication scheme name here.
Creating the custom authentication scheme
DotNet Core handles authentication by calling registered authentication handlers. This means we need to create our own authentication handler. Each authentication handler may have its own configuration. Authentication handlers and their configuration options are called authentication schemes.
The scheme options
The first step is to define the options related to our custom authentication scheme. This is done by deriving a class from the AuthenticationSchemeOptions class.
using Microsoft.AspNetCore.Authentication; namespace AuthenticatedWebApi.Security { public class TokenAuthenticationSchemeOptions : AuthenticationSchemeOptions { public const string Name = "TokenAuthenticationScheme"; } }
We don't have any custom options as such. The class contains only the name of the scheme. It's not necessary to declare the name here.
The authentication handler
The authentication handler is the main class that gets used at the time of authentication. The HandleAuthenticationAsync method is called every time an endpoint is accessed that is protected using the Authorize attribute. The implementation of HandleAuthenticationAsync looks for a header named 'AuthToken', extracts the value and checks if the value is present in an internal list. If either the header is absent or the value is not present in-memory, it returns 401 i.e. unauthorized status code. Storing & checking the token value is abstracted into the TokenService class.
The authenticaton endpoint
The /Authenticate endpoint is used to authenticate a client. The AuthenticationController class provides a Post method which accepts the username and password, it forwards this information to the TokenService class which checks if the username and password matches. The actual list of username and passwords are stored in the appseeting.json file. Needless to say you can implement any scheme for persisting user information e.g. a database. Note, the passwords are stored as SHA256 hashes. You can use the hash utility to generate SHA256 hashes.
The token service
The TokenService class contains all the logic required for:
- Reading the users from the application settings file (appsettings.json)
- Checking if a username, password combination exists
The cool factor
I always find it cool to write UseXYZ() type of methods to add an entire capability to the ASP.NET AspNetCore processing pipeline. We'll use a similar style to register our custom authentication scheme with ASP.NET core. The TokenAuthenticationExtensions class is written just for this reason.
using Microsoft.Extensions.DependencyInjection; namespace AuthenticatedWebApi.Security { public static class TokenAuthenticationExtensions { public static void UseTokenAuthentication(this IServiceCollection services) { services.AddAuthentication(options => { options.DefaultScheme = TokenAuthenticationSchemeOptions.Name; }) .AddScheme( TokenAuthenticationSchemeOptions.Name, option => {} ); } } }
The most important point to note is that we use the AddScheme method to register our custom authentication handler & options. Note, we've also set our custom authentication scheme as the default one so there is no need to specify our authentication scheme in the Authorize attrbitue.
Using the APIs
Download the code from GitHub. Build and run the service. We'll use the curl program to invoke the APIs, you can use other tools like Postman if you like.
Access the API without authentication.
Let's see what happens if one accesses the API without having an authtoken.
The output is the 401 (unauthorized) status code.
Authenticate
Let's authenticate using a username and password
The result is 200 (OK) and the response is the token string.
Access the API with the token
Let's authenticate using a username and password
The result is 200 (OK) and the response is the weather forecast.
Conclusion
Though the sample is contrived it demonstrates how a custom authentication scheme is implemented
in ASP.NET Core. The
References
- Overview of ASP.NET Core authentication
- A blog post I found useful
- Another related blog post
- Source code on GitHub