Plain and simple token authentication in ASP.NET Core 1.x

this post is on using JWTBearer authentication and your own custom token based authentication

Note, this post is for Core 1.x. If you are looking for 2.2 please check out my other post, https://www.frakkingsweet.com/plain-and-simple-token-authentication-in-asp-net-core-2-2/.

Hello, this post is on using JWTBearer authentication and your own custom token based authentication (because basic auth will not be implemented). I am using this for authentication between 2 systems via web api. And after banging my head against the wall for a couple of days, some nice guy at stack overflow answered my question and sent me down the right path. So, here's a very, very simple solution to authenticating a user using the default entity framework identity store and a very small amount of code. There is a few pieces missing, but this was meant to be a quick and dirty example. You will most likely want to, at a minimum, add encryption to the token.

Create a new "security token" and "token validator" class. The token validator is where all of the magic happens. You will also need to update the Configure method in startup.cs. The following gists contains all of the needed files and the changes to startup.cs. The last gist is a what is used in fiddler for a request as an example.
You will also need to add the token to the database as another login option. How you want to do that is up to you. I did it manually using sql management studio and adding another row into the AspNetUserLogins table. insert into AspNetUserLogins values ('Token', 'token', 'me', 'id from the user in AspNetUsers table')

MySecurityToken.cs:

using System;
using Microsoft.IdentityModel.Tokens;

namespace ApiSite.Services.Authentication
{
    public class MySecurityToken : SecurityToken
    {
        public MySecurityToken(string id, string issuer, SecurityKey securityKey, SecurityKey signingKey, DateTime validFrom, DateTime validTo)
        {
            this.Id = id;
            this.Issuer = issuer;
            this.SecurityKey = securityKey;
            this.SigningKey = signingKey;
            this.ValidFrom = validFrom;
            this.ValidTo = validTo;

        }

        public override string Id { get; }
        public override string Issuer { get; }
        public override SecurityKey SecurityKey { get; }
        public override SecurityKey SigningKey { get; set; }
        public override DateTime ValidFrom { get; }
        public override DateTime ValidTo { get; }
    }
}

MyTokenValidator.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using ApiSite.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.DependencyInjection;

namespace ApiSite.Services.Authentication
{
    public class MySecurityTokenValidator : ISecurityTokenValidator
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly byte[] _signingSecurityKey;

        public MySecurityTokenValidator(IServiceProvider serviceProvider, byte[] signingSecurityKey)
        {
            this._serviceProvider = serviceProvider;
            this._signingSecurityKey = signingSecurityKey;
        }

        public bool CanReadToken(string securityToken)
        {
            return true;
        }

        public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
        {
            ClaimsPrincipal result = null;
            SecurityToken token = null;

            Task.WaitAll(Task.Run(async () =>
                                            {
                                                var userManager = this._serviceProvider.GetService<UserManager<ApplicationUser>>();
                                                var principalFactory = this._serviceProvider.GetService<IUserClaimsPrincipalFactory<ApplicationUser>>();
                                                var applicationUser = await userManager.FindByLoginAsync("Token", securityToken);

                                                if (applicationUser == null)
                                                {
                                                    result = null;
                                                    token = null;
                                                }
                                                else
                                                {
                                                    result = await principalFactory.CreateAsync(applicationUser);
                                                    token = new MySecurityToken(applicationUser.Id,                                                "Token",
                                                                                  new SymmetricSecurityKey(this._signingSecurityKey),
                                                                                  new SymmetricSecurityKey(this._signingSecurityKey),
                                                                                  DateTime.MinValue,
                                                                                  DateTime.MaxValue);
                                                }
                                            }));

            validatedToken = token;
            return result;
        }

        public bool CanValidateToken => true;
        public int MaximumTokenSizeInBytes { get; set; }
    }
}

Startup.cs:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));

            if (env.IsDevelopment())
            {
                loggerFactory.AddDebug();
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseIdentity();

            //new jwt bearer stuff
            var bearerOptions = new JwtBearerOptions();
            bearerOptions.SecurityTokenValidators.Add(new MySecurityTokenValidator(app.ApplicationServices, Encoding.UTF8.GetBytes("SecurityKey1234567890")));
            app.UseJwtBearerAuthentication(bearerOptions);
            //end of new jwt bearer stuff
            
            app.UseCookieAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

Headers:

Headers:
========
User-Agent: Fiddler
Host: localhost:47160
Content-Length: 58
Authorization: Bearer hello
Content-type: application/json