DEV Community

Dominic Pascasio
Dominic Pascasio

Posted on • Updated on

ASP.NET Core - Custom Token Authentication

This is a quick reference on how to create a custom token authentication.

For a simple demonstration, we will check the header of the request if it contains our token.

Warning! In real world, using plain text as your token is not the proper way to secure your application.

  1. Create custom authentication scheme options and handler:

    using Microsoft.AspNetCore.Authentication;
    
    public class MyAuthenticationOptions :         
        AuthenticationSchemeOptions
    {
        public const string DefaultScheme = "MyAuthenticationScheme";
        public string TokenHeaderName { get; set; } = "MyToken";
    }
    

    For authentication handler:

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Options;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    
    public class MyAuthenticationHandler :         
        AuthenticationHandler<MyAuthenticationOptions>
    {
        public MyAuthenticationHandler         
            (IOptionsMonitor<MyAuthenticationOptions> options, 
            ILoggerFactory logger, UrlEncoder encoder, 
            ISystemClock clock)
            : base(options, logger, encoder, clock) 
        { }
       protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            //check header first
            if (!Request.Headers 
                .ContainsKey(Options.TokenHeaderName))
            {
                return AuthenticateResult.Fail($"Missing header: {Options.TokenHeaderName}");
            }
    
            //get the header and validate
            string token = Request 
                .Headers[Options.TokenHeaderName]!;
    
            //usually, this is where you decrypt a token and/or lookup a database.
            if(token!= "supersecretecode")
            {
                return AuthenticateResult
                    .Fail($"Invalid token.");
            }
            //Success! Add details here that identifies the user
            var claims = new List<Claim>()
            {
                new Claim("FirstName", "Juan")
            };
    
            var claimsIdentity = new ClaimsIdentity
                (claims, this.Scheme.Name);
            var claimsPrincipal = new ClaimsPrincipal 
                (claimsIdentity);
    
            return AuthenticateResult.Success
                (new AuthenticationTicket(claimsPrincipal, 
                this.Scheme.Name));
        }
    }
    
  2. Register the service in DI:

    builder.Services.AddAuthentication
        (MyAuthenticationOptions.DefaultScheme)
        .AddScheme<MyAuthenticationOptions, MyAuthenticationHandler>
        (MyAuthenticationOptions.DefaultScheme, 
            options => { });
    

    This does the same thing as AddJwtBearer and AddCookie, you can create an extension method if you like.

  3. Add authentication to the pipeline. Make sure it comes before authorization and endpoints.

    app.UseAuthentication();
    app.UseAuthorization();
    
    app.MapControllers();
    
  4. You can now set your endpoint to accept only authenticated users.

    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController
    

    You can also access the claims you set in your authentication handler via User in your controllers:

    var claim = User.Claims.Where(c => c.Type == "FirstName").FirstOrDefault();
    

    In other classes, you can inject ClaimsPrincipal to access the user. Register in DI where to get the ClaimsPrincipal when required:

    builder.Services.AddHttpContextAccessor();
    builder.Services.AddTransient<ClaimsPrincipal>(s => s.GetService<IHttpContextAccessor>().HttpContext.User);
    

    Then in your class, add ClaimsPrincipal to your constructor:

    public class MyService
    {
        public MyService(ClaimsPrincipal user) 
        { 
    

Top comments (0)