Token Based Authentication in ASP.Net using JWTs Part 1

AJLately, I’ve been doing quite a bit of front-end development with the latest version of Angular and that requires token based authentication. So in this article let’s discuss how to create tokens in .Net.  I’ll go over setting up tokens in regular .Net, and in a later article, I’ll discuss how to setup tokens in .Net core.

All of the code can be obtained from Github Here.

Explanation

This system will use all of the latest features from the .NET framework.  The features that we will use are:

  • Entity Framework to access a SQL database
  • Entity Framework Migrations to handle changes to our Database models and make the SQL changes
  • Web API for housing the rest calls
  • Owin for web setup and hosting
  • Asp.Net Identity for user authentication

Base System Setup

First things first, open Visual Studio and create a new Asp.Net Web Application Project.  Name it what you’d like:

new-project

Select “Ok” and then select a Web API Project.  And select “No Authentication” for now.  We’ll setup authentication using Identity later:

new-project-2

Now you have a base application.

Next, we need to install Entity Framework so right-click on the project and select “Manage Nuget Packages”:

select-nuget

From there, select the “Browse” tab and type in “Entity Framework”, then select the Entity framework library and click “install”:

select-ef.PNG

Next, we need to add the following libraries as well:

  • Microsoft.AspNet.Identity.EntityFramework (Identity libraries for entity framework)
  • Microsoft.AspNet.Identity.Owin (Identity libraries to work with Owin)
  • Microsoft.Owin.Cors  (allows us to setup cross origin resource sharing)
  • Microsoft.Owin.Security.Jwt (allows us to setup JWT consumption in Owin)
  • Microsoft.Owin.Host.SystemWeb (starts up Owin via an attribute)
  • System.IdentityModel.Tokens.Jwt v4.0.0 – NOTE: the latest version seems to be still beta and does not work properly.

We also need to add a reference to System.IdentityModel.  To do this, right-click on ‘References’ and select ‘Add References’:

add-reference

Next, scroll down to System.IdentityModel and select the checkbox and press Ok:

add-reference2.png

Now, let’s add a valid connection string in the web.config file.  I like to place this directly under ‘ConfigSections’:


   <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=localhost\SQLEXPRESS; Initial Catalog=api3; Integrated Security=True; MultipleActiveResultSets=True;" providerName="System.Data.SqlClient"></add>
  </connectionStrings>

... (more stuff below)

Authentication Using Asp.Net Identity

Identity (with Owin) handles the authentication and it requires a bit of code to get setup.  Let’s get it working:

Create a Folder in the root of the project and name it “Models”, and then add a class in there called “MyDbContext”.  This will be our Entity framework context.  Paste in the following code:

using api.Models;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace WebApplication1.Models
{
    public class MyDbContext : IdentityDbContext<User>
    {
        public MyDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }

        public static MyDbContext Create()
        {
            return new MyDbContext();
        }
    }

}

That tells that creates the main db context for identity to use.  Next, we need the user model.  Create a file named “User.cs” in the Models folder and paste the following:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;

namespace api.Models
{
    public class User : IdentityUser
    {

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager, string authenticationType)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
            // Add custom user claims here
            return userIdentity;
        }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string ContactAddress { get; set; }
        public bool IsActive { get; set; }

    }
}
 

That sets up the user for identity to use for authentication.  Next, we need a user manger.  Create a folder under the base application and name it “Utils”.  Under there, create a class named “ApplicationUserManager” and paste the following:

using api.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.Identity.Owin;
using WebApplication1.Models;

namespace api.Utils
{
    public class ApplicationUserManager : UserManager<User>
    {
        public ApplicationUserManager(IUserStore<User> store)
            : base(store)
        {
        }

        public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
        {
            var manager = new ApplicationUserManager(new UserStore<User>(context.Get<MyDbContext>()));
            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<User>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 4,
                //RequireNonLetterOrDigit = true,
                //RequireDigit = true,
                //RequireLowercase = true,
                //RequireUppercase = true,
            };
            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider =
                    new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("ASP.NET Identity"))
                    {
                        TokenLifespan = TimeSpan.FromDays(14)
                    };

            }
            return manager;
        }
    }
}

This sets up the user manager with things like:

  • Password validation (require upper case, password length, etc)
  • Token lifespan for user token (this is used when the user resets his password, I’m not explaining how to perform this here)

We’re going to use Entity Framework migrations, which allows for automatic database updates (detecting changes, etc).  Let’s set that up next.

Entity Framework Migrations

Open up the package manager console, but navigation to Tools -> Nuget Package Manager -> Package Manager Console.

Once that’s open, type in “Enable-Migrations” and press enter.  This will start the process of setting up migrations and it will create a file under the Migrations folder named “Configuration.cs”.  Let’s modify the seed function in that file to automatically seed a few users whenever migrations are executed:

namespace api.Migrations
{
    using api.Models;
    using api.Utils;
    using Microsoft.AspNet.Identity.EntityFramework;
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;
    using Microsoft.AspNet.Identity;
    using WebApplication1.Models;

    internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(MyDbContext context)
        {
            SeedUsers(context);
        }

        private void SeedUsers(MyDbContext db)
        {
            var userManager = new ApplicationUserManager(new UserStore<User>(db));

            var admins = new[]
            {
                new User { UserName = "admin", Email = "testuser@unknown.com", EmailConfirmed = true, IsActive = true },
                new User { UserName = "admin2", Email = "testuser2@unknown.com", EmailConfirmed = true, IsActive = true }
            };

            foreach (var user in admins)
            {
                if (userManager.Users.Any(i => i.UserName == user.UserName))
                    continue;
                userManager.Create(user, "Admin1234!");
            }

        }
    }
}

So whenever we execute our migrations, the seed will be ran.  Currently, it’s setup to add 2 users if they don’t already exist.

Next, we must create the first migration.  From the Package Manger Console, type in “add-migration” and type in “initial” when it asks for a name.  This should create a file in the migrations folder and it’ll contain the name “initial” along with a timestamp.

Finally, we must update our database.  From the Package Manager Console, type in “update-database”.  This will update the database to the latest and execute the seed method, which will create a few users in the database.  (In our case, this should create a new database with the necessary tables.)

Ok, that’s enough of setting up our database with identity,   (except for initial setup, which I’ll discuss later), let’s get into items needed for JWT (token) authentication.

JWT Authentication

Now, we must create a few classes used in the token authentication process.

Create a folder in the root project, named “Providers”, and create a class named “ApplicationOAuthProvider”.  Paste in the following code:

using api.Models;
using api.Utils;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;

namespace api.Providers
{
    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        private const string SelectedCustomerSettingName = "selectedCustomerId";

        private readonly string _publicClientId;

        public ApplicationOAuthProvider(string publicClientId)
        {
            if (publicClientId == null)
            {
                throw new ArgumentNullException("publicClientId");
            }

            _publicClientId = publicClientId;
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            try
            {
                var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
                User user = await userManager.FindAsync(context.UserName, context.Password);

                if (user == null)
                {
                    context.SetError("invalid_grant", "The user name or password is incorrect.");
                    return;
                }

                if (!user.IsActive)
                {
                    context.SetError("invalid_grant", "Error logging in user.");
                    return;
                }

                var roles = userManager.GetRoles(user.Id);

                ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
                    OAuthDefaults.AuthenticationType);
                // TODO: add claims here to oAuthIdentity
                ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                    CookieAuthenticationDefaults.AuthenticationType);

                AuthenticationProperties properties = CreateProperties(user.UserName, user.Id, roles);

                AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
                context.Validated(ticket);
                context.Request.Context.Authentication.SignIn(cookiesIdentity);
            }
            catch (Exception ex)
            {
                context.SetError("Critical Error", "Critical Error logging in");
            }

        }

        public override Task TokenEndpoint(OAuthTokenEndpointContext context)
        {
            foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            {
                context.AdditionalResponseParameters.Add(property.Key, property.Value);
            }

            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            // Resource owner password credentials does not provide a client ID.
            if (context.ClientId == null)
            {
                context.Validated();
            }
            return Task.FromResult<object>(null);
        }

        public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
        {
            if (context.ClientId == _publicClientId)
            {
                Uri expectedRootUri = new Uri(context.Request.Uri, "/");

                if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                {
                    context.Validated();
                }
            }

            return Task.FromResult<object>(null);
        }

        public static AuthenticationProperties CreateProperties(string userName, string userId, IEnumerable<string> roles)
        {
            IDictionary<string, string> data = new Dictionary<string, string>
            {
                { "userName", userName },
                { "userId", userId },
                { "roles", string.Join(",", roles.Select(x=>x.ToLower())) },
            };
            return new AuthenticationProperties(data);
        }
    }

}

This component does quite a bit but ultimately, the most import part is inside of GrantResourceOwnerCredentials which takes the provided username and password and logs actually in the user.

Next, we need to define our JWT format.  Create a file under Providers, named “JWTFormat.cs” and paste the following:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using System.IdentityModel.Tokens;

namespace api.Utils
{
    public class JWTFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private readonly string _issuer = string.Empty;

        public JWTFormat(string issuer)
        {
            _issuer = issuer;
        }

        public string SignatureAlgorithm
        {
            get { return "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; }
        }

        public string DigestAlgorithm
        {
            get { return "http://www.w3.org/2001/04/xmlenc#sha256"; }
        }

        public string Protect(AuthenticationTicket data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = Utils.Configuration.TokenAudienceId;
            string symmetricKeyAsBase64 = Utils.Configuration.TokenAudienceSecret;
            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
            var signingKey = new SigningCredentials(new InMemorySymmetricSecurityKey(keyByteArray),
                                                            SignatureAlgorithm,
                                                            DigestAlgorithm);

            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;
            var token = new System.IdentityModel.Tokens.JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
            var handler = new JwtSecurityTokenHandler();
            var jwt = handler.WriteToken(token);

            return jwt;
        }

        public AuthenticationTicket Unprotect(string tokenString)
        {
            throw new NotImplementedException();
        }
    }

}

This handles creating the actual JWT.  The algorithms are pretty much standard, and the token contains things such as when it was issued and when it expires.

I created a helper class which grabs the JWT specific information from the web.config file.  So let’s create that.  Under the Utils folder, create a file named “Configuration.cs” and paste the following:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;

namespace api.Utils
{
    public class Configuration
    {
        public static string TokenIssuer => ConfigurationManager.AppSettings["Token:Issuer"];
        public static string TokenAudienceId => ConfigurationManager.AppSettings["Token:AudienceId"];
        public static string TokenAudienceSecret => ConfigurationManager.AppSettings["Token:AudienceSecret"];

        public static int TokenExpireTimeMinutes => Convert.ToInt32(ConfigurationManager.AppSettings["Token:TokenExpireTimeMinutes"]);
    }
}

This will require us to modify our web.config file.  Add the following under “appSettings” in the web.config file:

    <add key="Token:Issuer" value="http://localhost" />
    <add key="Token:AudienceId" value="e6b0f93b602544089d3436fabc5b4ab0" />
    <add key="Token:AudienceSecret" value="kW9ys7uuoUfiY8pQyBke7WMhZ2DhuyntGPCVPySuSvc" />
    <add key="Token:TokenExpireTimeMinutes" value="1048"/><span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

These settings are as follows:

  • Token Issuer: Usually, this is just the site name that issued the token
  • AudienceId: The site specific audience id.  This can just be a Guid.
  • Audience Secret:  This is the secret key that’s used to generate the JWT.
  • TokenExpireTimeMinutes: This is the duration of the token.

NOTE: I created a unit test project which contains one unit test that creates a new Audience Id as well as Audience Secret.  Use that to create these items.

Now, we need to handle application startup and wire things together.

Application Startup

In the project root, create a file named “Startup.cs” and copy the following:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OAuth;
using Owin;
using api.Providers;
using api.Models;
using api.Utils;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using WebApplication1.Models;

[assembly: OwinStartup(typeof(api.Startup))]
namespace api
{
    public partial class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }

        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }

        public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context and user manager to use a single instance per request
            app.CreatePerOwinContext(MyDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

            //Configure the application for OAuth based flow
            ConfigureOAuthForJWT(app);
            ConfigureJWTConsumption(app);
        }

        public void ConfigureOAuthForJWT(IAppBuilder app)
        {
            var expireTime =
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/api/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(api.Utils.Configuration.TokenExpireTimeMinutes),
                // In production mode set AllowInsecureHttp = false
                AllowInsecureHttp = true,
                AccessTokenFormat = new JWTFormat(api.Utils.Configuration.TokenIssuer),
            };
            app.UseOAuthAuthorizationServer(OAuthOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }

        private void ConfigureJWTConsumption(IAppBuilder app)
        {
            var issuer = api.Utils.Configuration.TokenIssuer;
            string audienceId = Utils.Configuration.TokenAudienceId;
            byte[] audienceSecret = TextEncodings.Base64Url.Decode(Utils.Configuration.TokenAudienceSecret);

            // Api controllers with an [Authorize] attribute will be validated with JWT
            app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { audienceId },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                    }
                });
        }
    }
}

There’s quite a bit going on here:

  • line 19 tells owin to use our startup class
  • lines 37-38 create an instance of our database and application user manager inside of Owin.
  • line 39 tells the system to allow calls from anywhere (CORS)
  • line 46-62 sets up the JWT for use, including
    • path of token
    • length of token
    • format of token (setup earlier)
  • lines 64-81 handle consuming a JWT.  This allows us to use [Authorize] attributes on our controllers (and it just works).

Testing

Once the project builds, go ahead and run it.  To test the system, we’ll need to use a test rest client.  I usually use Postman inside of Chrome.  Here’s what it looks like:

postman-1.png

  • The port of the url (in this case 49944) will be different for you.  Set it to whichever port that the API starts on.
  • data must be passed as  x-www-form-urlencoded data
  • the ‘grant_type’ must be set to ‘passsword’
  • the username / password come from the seed that we used to generate our users.

Once you have this setup and press ‘Send’, you should see something like the following:

postman-2.png

The token is set in the field ‘access token’ and can now be used to access our system.  To try this, let’s make it so the values controller requires authorization.

Under ‘Controllers’, select the ‘ValuesController’ and add ‘[Authorize]’ just above the class.  If you don’t have a values controller then right-click on controllers and select Add -> Controller and Select “Web API 2 Controller – Empty” and call it ValuesController.  Paste in the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace WebApplication1.Controllers
{
    [Authorize]
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        public void Post([FromBody]string value)
        {
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
    }
}

Let’s try and access that controller from within Postman but let’s not set the token:

postman-3.PNG

Ok, so authorization is working and we’re not allowed in.  Now, let’s use the previous token and see if we can get access.  To do this, add a header type of “Authorization” then for the value add “Bearer ” plus the token right after:

postman-4.png

Success!  We’ve successfully authenticated to the system and are receiving values.  This is the end of the article.  In part 2, I’ll discuss how to implement refresh tokens.  Please post any questions.

2 thoughts on “Token Based Authentication in ASP.Net using JWTs Part 1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s