Better Software Design with Clean Architecture

Have you ever produced code that:

  • was bug laden
  • was painful to debug or enhance with new features
  • was hard/impossible to test without things like a database or web server
  • had presentation logic mixed with business logic or business logic mixed in with data access logic (sql)
  • was hard for other developers to understand because it did not clearly express its intent or purpose within the application it was written for

I know I have. Over time I learned about the various Gang of Four patterns and made a conscious effort to keep the SOLID principles running on a background thread in my mind as I wrote code. While these ideas certainly helped mitigate the problems listed above, they didn't eliminate them. When writing web or desktop software using MVC or MVVM I still found some of the same old symptoms showing up in my projects. Things like business logic leaking into controllers, entity models being used all over the place for different purposes and large regions of the code that had no unit test coverage because they had some sort of direct dependency on a database or http client.

The answer

One day, a colleague sent around this link introducing The Clean Architecture by Uncle Bob. It resonated with me instantly as it presented a solution for the same problems I was seeing. The best part, there's nothing mystical or complicated about Clean Architecture - it is a relatively simple and practical architecture template that can be applied to many application domains if you choose to follow just a few of its basic rules.

How Clean Architecture works

The key rule behind Clean Architecture is: The Dependency Rule. The gist of this is simply that dependencies are encapsulated in each "ring" of the architecture model and these dependencies can only point inward.

Clean Architecture keeps details like web frameworks and databases in the outer layers while important business rules and policies are housed in the inner circles and have no knowledge of anything outside of themselves. Considering this, you can start to see how it achieves a very clean separation of concerns. By ensuring your business rules and core domain logic in the inner circles are completely devoid of any external dependencies or 3rd party libraries means they must be expressed using pure C# POCO classes which makes testing them much easier.

In fact your business rules simply don’t know anything at all about the outside world.

Robert C. Martin

There are a few other important concepts that I'm going to highlight along the way with an example below but if you're interested in just the theory please go check out Uncle Bob's original post introducing Clean Architecture.

Implementing the "Course Registration" use case

Let's see how this works using a real-world use case. For the folks doing agile scrum, I realize a use case is not the most fashionable way to describe a requirement. But for this post, it's perfect because I'd like to show how all the details of the use case can be modeled within clean architecture. A user story would simply be too vague.

I've typed out the entire use case here for reference so you don't need to digest the whole thing right now. We'll cover its aspects below in detail as we walk through implementing it using clean architecture.

Title Register for courses
Description Student accesses the system and views the courses currently available for him to register. Then he selects the courses and registers for them.
Primary Actor Student
Preconditions
  • Student is logged into system
  • Student has not previously enrolled or registered
  • Student cannot register within 5 days of course start date
Postconditions Student is registered for courses
Main Success Scenario
  1. Student selects "Register New Courses" from the menu.
  2. System displays list of courses available for registering.
  3. Student selects one or more courses he wants to register for.
  4. Student clicks "Submit" button.
  5. System registers student for the selected courses and displays a confirmation message.
Extensions
  • (2a) No courses are available for this student.
    1. System displays error message saying no courses are available, and provides the reason & how to rectify if possible.
    2. Student either backs out of this use case, or tries again after rectifying the cause.
  • (5a) Some courses could not be registered.
    1. System displays message showing which courses were registered, and which courses were not registered along with a reason for each failure.
  • (5b) None of the courses could be registered.
    1. System displays message saying none of the courses could be registered, along with a reason for each failure.

This is a simple use case allowing a student to register for one or more classes and then returning either a success or error result to notify her of the outcome. We'll use clean architecture to write this use case in a fashion that meets the goals and avoids the problems I mentioned in the intro.

Creating the Entities

Entities are the heart of clean architecture and contain any enterprise-wide business rules and logic. Now, you might not be working in the context of an enterprise and that's perfectly fine. If you're writing a standalone application Uncle Bob suggests simply referring to these as Business Objects. The key is that they contain rules that are not application specific - so basically any global or shareable logic that could be reused in other applications should be encapsulated in an entity.

Inspecting our use case there are 2 entities we need: Student and Course.

Using a TDD approach I wrote a couple of tests and just enough code in the Student entity class to get them passing.

The RegisterForCourse() method implements 2 rules from our use case.

public class Student : EntityBase
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public IList<Course> RegisteredCourses { get; }
   public IList<Course> EnrolledCourses { get; set; }

   public Student()
   {
      RegisteredCourses = new List<Course>();
      EnrolledCourses = new List<Course>();
   }

   public bool RegisterForCourse(Course course)
   {
      // student has not previously enrolled
      if (EnrolledCourses.Any(ec => ec.Code == course.Code)) return false;

      // registration cannot occur with 5 days of course start date
      if (DateTime.UtcNow > course.StartDate.AddDays(-5)) return false;

      RegisteredCourses.Add(course);
      return true;
   }
}

[Fact]
public void CannotRegisterForCourseWithin5DaysOfStartDate()
{
  // arrange
  var student = new Student();
  var course = new Course { Code = "BIOL-1507EL", Name = "Biology II", StartDate = DateTime.UtcNow.AddDays(+3) };

  // act
  var result = student.RegisterForCourse(course);

  // assert
  Assert.False(result);
}

[Fact]
public void CannotRegisterForCourseAlreadyEnrolledIn()
{
  // arrange
  var student = new Student
  {
     EnrolledCourses = new List<Course>
     {
       new Course { Code = "BIOL-1507EL", Name = "Biology II" },
       new Course { Code = "MATH-4067EL", Name = "Mathematical Theory of Dynamical Systems, Chaos and Fractals" }
             }
     };

 // act
 var result = student.RegisterForCourse(new Course { Code = "BIOL-1507EL" });

 // assert
 Assert.False(result);
}

Use Cases

Moving up from the entities we have the Use Case layer. The classes that live here have a few unique features and responsibilities:

  • Contain the application specific business rules
  • Encapsulate and implement all of the use cases of the system. A good rule to start with is a class per use case
  • Orchestrate the flow of data to and from the entities, and can rely on their business rules to achieve the goals of the use case
  • Have NO dependency and are totally isolated from things like a database, UI or special frameworks
  • Will almost certainly require refactoring if details of the use case requirements change

Use case classes are typically suffixed with the word Interactor. Uncle Bob mentions in this talk that he considered calling them controllers but assumed this would be too easily confused with MVC so Interactor it is!

Our use case is modelled in RequestCourseRegistrationInteractor.cs.

There are a few important aspects of the use case class I'd like to highlight.

First off, it implements the IRequestHandler interface. This interface is an example of the mediator pattern which dictates that implementors will work with a certain request and response object in a loosely coupled fashion.

public class RequestCourseRegistrationInteractor : IRequestHandler<CourseRegistrationRequestMessage, CourseRegistrationResponseMessage>
...

There is a single TResponse Handle(TRequest message) method defined on the interface which essentially performs all the work of our use case. Pretty simple huh? Handle() takes a request object as its lone parameter which will typically contain any data passed in from the outer layer (the UI) and returns a response message with both types dictated by the IRequestHandler interface. All of our application specific logic for the use case will go into this method.

One key aspect of the request/response messages that flow in and out of use case interactors and across boundaries is that they are simple data structures meaning they contain no special types: ie. entities, or types provided by 3rd party libs etc. - they are pure C# objects.

public class CourseRegistrationRequestMessage : IRequest<CourseRegistrationResponseMessage>
{
  public int StudentId { get; private set; }
  public List<string> SelectedCourseCodes { get; private set; }

  public CourseRegistrationRequestMessage(int studentId,List<string> selectedCourseCodes)
  {
    StudentId = studentId;
    SelectedCourseCodes = selectedCourseCodes;
  }
}

The CourseRegistrationRequest object consists of only a StudentId and a list of selected course codes selected by the user.

Here's the full implementation of RequestCourseRegistrationInteractor.cs

public class RequestCourseRegistrationInteractor : IRequestHandler<CourseRegistrationRequestMessage, CourseRegistrationResponseMessage>
{
  private readonly IStudentRepository _studentRepository;
  private readonly ICourseRepository _courseRepository;
  private readonly IAuthService _authService;
  public RequestCourseRegistrationInteractor(IAuthService authService, IStudentRepository studentRepository, ICourseRepository courseRepository)
  {
    _authService = authService;
    _studentRepository = studentRepository;
    _courseRepository = courseRepository;
  }

public CourseRegistrationResponseMessage Handle(CourseRegistrationRequestMessage message)
{
   // student must be logged into system
   if (!_authService.IsAuthenticated())
   {
     return new CourseRegistrationResponseMessage(false,null,"Operation failed, not authenticated.");
   }

   // get the student
   var student = _studentRepository.GetById(message.StudentId);

   // save off any failures
   var errors = new List<string>();

   foreach (var c in message.SelectedCourseCodes)
   {
     var course = _courseRepository.GetByCode(c);

     if (!student.RegisterForCourse(course))
     {
         errors.Add($"unable to register for {course.Code}");
     }
   }

   _studentRepository.Save(student);
   return new CourseRegistrationResponseMessage(!errors.Any(), errors);
}

Note the use of _authService, _studentRepository and _courseRepository. These services are typically referred to as Gateways within clean architecture and get injected into the Use Case layer as per the dependency rule. These are the things that deal with the database, rest services or other external agencies and their implementation belongs in the Interface Adapters layer. Interactors only know what behavior these gateways offer by way of their interface definition. They have no idea how they do their work because those details are encapsulated in an outer layer which the Use Cases know nothing about.

Interface Adapters

The purpose of the interface adapter layer is to act as a connector between the business logic in our interactors and our framework-specific code. For example, in an ASP.Net MVC app, this is where the models, views, and controllers live. Gateways like services and repositories are also implemented here.

It is this layer, for example, that will wholly contain the MVC architecture of a GUI. The Presenters, Views, and Controllers all belong in here.

Also in this layer is any other adapter necessary to convert data from some external form, such as an external service, to the internal form used by the use cases and entities.

Robert C. Martin

In this example I'm using a basic console app to consume my use case so this serves as my interface adapter layer. It contains the concrete implementations of the required Gateways and has Presentation logic to format the response from the Use Case into something friendly for the UI.

In the Main() method we can see the usage of calling the use case and presenting the results.

//*************************************************************************************************
// Here we're connecting our app framework layer with our Use Case Interactors
// This would typically go in a Controller Action in an MVC context or ViewModel in MVVM etc.
//*************************************************************************************************
// 1. instantiate Course Registration Use Case injecting Gateways implemented in this layer
var courseRegistraionRequestUseCase = new RequestCourseRegistrationInteractor(authService, studentRepository, courseRepository);

// 2. create the request message passing with the target student id and a list of selected course codes 
var useCaseRequestMessage = new CourseRegistrationRequestMessage(1, new List<string> { userInput.ToUpper() });

// 3. call the use case and store the response
var responseMessage = courseRegistraionRequestUseCase.Handle(useCaseRequestMessage);

// 4. use a Presenter to convert the use case response to a user friendly ViewModel
var courseRegistraionResponsePresenter = new CourseRegistrationRequestResponsePresenter();
var vm = courseRegistraionResponsePresenter.Handle(responseMessage);

Console.Clear();

// render results

if (vm.Success)
{
  Console.BackgroundColor = ConsoleColor.DarkGreen;
  Console.ForegroundColor = ConsoleColor.White;
}
else
{
  Console.BackgroundColor = ConsoleColor.Red;
  Console.ForegroundColor = ConsoleColor.White;
}
Console.WriteLine();
Console.WriteLine(vm.ResultMessage);
Console.WriteLine();

Presentation

We'd like to show something friendly to the user when we get a response back from the interactor. To accomplish this, I created CourseRegistrationResponsePresenter which has the single responsibility of converting a CourseRegistrationResponseMessage into a CourseRegistrationResponseViewModel. I'll mention again that the response message and viewmodel are POCO objects containing no special types or data structures, just everyday collection and value types.

public class CourseRegistrationResponsePresenter
{
  public CourseRegistrationResponseViewModel Handle(CourseRegistrationResponseMessage responseMessage)
  {
    if (responseMessage.Success)
    {
         return new CourseRegistrationResponseViewModel(true,"Course registration successful!");
    }

    var sb = new StringBuilder();
    sb.AppendLine("Failed to register course(s)");
    foreach (var e in responseMessage.Errors)
    {
       sb.AppendLine(e);
    }

    return new CourseRegistrationResponseViewModel(false,sb.ToString());
  }
}

Frameworks and Drivers

This layer contains tools like databases or frameworks. By default, we don’t write very much code in this layer, but it’s important to clearly state the place and priority that those tools have in the architecture.

Summary

Clean Architecture provides a simple and effective framework for separating the different aspects of our system producing a highly decoupled, testable architecture.

Let's recap some key benefits:

  • Use Cases are encapsulated in one place meaning they are very visible and easier to understand. Business rules are not scattered all over the place making debugging and modification of the code painful.

  • The Dependency Rule and use of abstracted Gateways mean the core business logic in our Interactors and Entities is easily testable and not hampered by external things like databases and RESTful web services. The lack of 3rd party, feature-laden frameworks in our business logic also means the code there is only focused on the important rules and policies of our application.

  • Flexible and portable - because the Use Cases are completely decoupled from any UI or infrastructure it's easy to do things like switch the database or web framework or even port to an entirely new platform. Our example runs in a console app but it could just as easily work on the web, desktop or a phone.

Like most design decisions there are tradeoffs to be made when considering Clean Architecture. For the benefits I highlighted there are also a few disadvantages:

  • Your team's ability to ramp up and effectively apply Clean Architecture. There's nothing radically complex in here but there certainly is a learning curve and time required to adapt to any new design or architectural style.

  • Applying Clean Architecture adds some bloat in the form of many separate classes for all the Presenters, Use Case Request/Response dtos, Use Case Interactors, Entities, Gateways etc plus all the test cases :). Not a huge deal but a valid knock on the impact of this approach to the size of your project.

I hope this guide has provided some insight on how Clean Architecture can improve your software design and prevent many of the common pitfalls that hinder projects. Like any pattern, it takes a little familiarity with the concepts and principles before they can be effectively applied. A good exercise to start might be to think of some use cases near and dear to you currently - can you map them out mentally using Clean Architecture? Do you have a sense of the Entities, what the Use Case Interactor might look like, what data needs to flow back and forth in the request and response messages? Running your use cases through these questions can help you get started in modeling them using Clean Architecture.

Thanks for reading!

source code

Psst! If you're interested in learning more you should pickup Uncle Bob's book: Clean Architecture: A Craftsman's Guide to Software Structure and Design

User Authentication with Angular and ASP.NET Core

User authentication is a fundamental part of any meaningful application. Unfortunately, implementing it properly can be a painful exercise that steals time and energy away from more meaningful features of our application. In this post, we'll learn step by step how to add user registration and login functionality to an Angular app powered by an ASP.NET Core backend API. Our approach will use JSON Web Tokens which provide a modern, industry standard method for token-based authentication in web and mobile apps.

Note, the Angular portion of this guide is based on version >= 2.x.

The finished product

Dev environment

  • Windows 10
  • Visual Studio Code - v1.11.2
  • Angular v4.0.2
  • C# for Visual Studio Code extension
  • SQL Server Express 2016
  • .NET Core SDK v1.0.1 dotnet --version
  • node v6.10.0 node -v
  • npm v3.10.10 npm -v

Get the code

In my last post I showed how to get started with Angular and ASP.NET Core so I'm going to use the finished product of that post as the foundation for this one. If you're following along step by step you'll need to grab that code here. If you just want the code from this post, it's here.

Plan of attack

Here's a summary of the steps we'll be going through in this tutorial. The first half involve building out our ASP.NET Core backend while the second half focuses on the frontend Angular app.

Install packages

We're going to use the ASP.NET Core Identity provider with SQL Server to store our user account information. The identity provider is a membership system that allows us to easily add login capabilities to our ASP.NET Core app. Add the required packages from the command line within your project directory.

Protip: to save on typing each of these you can copy/paste the package list from my .csproj below into yours and run dotnet restore

  • dotnet add package Automapper
  • dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
  • dotnet add package Microsoft.AspNetCore.Mvc
  • dotnet add package Microsoft.EntityFrameworkCore.Design
  • dotnet add package Microsoft.EntityFrameworkCore.SqlServer
  • dotnet add package Microsoft.EntityFrameworkCore.sqlserver.Design
  • dotnet add package Microsoft.EntityFrameworkCore.Tools
  • dotnet add package Microsoft.EntityFrameworkCore.Tools.DotNet
  • dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
  • dotnet add package FluentValidation.AspNetCore
  • dotnet add package Microsoft.IdentityModel.Tokens
  • dotnet add package System.IdentityModel.Tokens.Jwt

Here's what the package list in my csproj file looks like.

 <ItemGroup>
    <PackageReference Include="AutoMapper" Version="6.0.2" />
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="2.0.1" />
    <PackageReference Include="FluentValidation.AspNetCore" Version="6.4.0" />
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
    <PackageReference Include="microsoft.aspnetcore.staticfiles" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.sqlserver.Design" Version="1.1.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.0.0" />
    <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="5.1.3" />
    <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.1.3" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
  </ItemGroup>

Note, the first time you open the project or add packages from the command line you may be prompted to restore them in the IDE so go ahead and do that when requested.

Creating a data model

We need an object to represent a user in the app. Fortunately, the ASP.NET Core Identity provider has a built-in class IdentityUser that we can use. This maps to the AspNetUsers database table and has a few expected properties out of the box like Email, UserName, BirthDate etc. Adding your own custom properties is very simple. We can just subclass IdentityUser and extend with whatever new properties we require. I'd like my app to be able to record the first and last name of a user so those are the 2 we'll add. I made a new class in the Models\Entities folder called AppUser.cs.

namespace DotNetGigs.Models.Entities
{
    // Add profile data for application users by adding properties to this class
    public class AppUser : IdentityUser
    {
        // Extended Properties
       public string FirstName { get; set; }
       public string LastName { get; set; }       
    }
}

In addition to AppUser containing the core user identity data, we'll add another class to represent a specific role in our app beyond just a generic user: JobSeeker.

namespace DotNetGigs.Models.Entities
{
    public class JobSeeker  
    {
        public int Id { get; set; }     
        public string IdentityId { get; set; }   
        public AppUser Identity { get; set; }  // navigation property
        public string Location {get; set;}
    }
}

This class has a reference to AppUser and maps to it via the IdentityId property. This is represented as a foreign key in the database. I find the approach of creating separate classes/tables for unique user roles better than polluting the IdentityUser table with a bunch of different columns when there is specific data required for each. If we were to add Admin or Client roles we can follow the same pattern by storing their universal profile data in IdentityUser/AspNetUsers and creating unique classes/tables for more specific stuff.

Creating the database context

The DatabaseContext is the primary class responsible for interacting with data as objects in Entity Framework Core. It manages the entity objects during run time, this includes populating objects with data from the database, change tracking, and persisting data to the database. We'll create our ApplicationDbContext by deriving from IdentityDbContext which is basically a regular DbContext that includes the identity-related entity sets.

namespace DotNetGigs.Data
{
 public class ApplicationDbContext : IdentityDbContext 
 {
     public ApplicationDbContext(DbContextOptions options) : base(options)
     {
     }
        
     public DbSet JobSeekers { get; set; }
  }
}

Next step is to register ApplicationDbContext in the container so we can easily inject an instance into our controllers down the road.

In Startup.cs I added the following to the ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
   // Add framework services.
   services.AddDbContext<ApplicationDbContext>(options => 
   options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
   b => b.MigrationsAssembly("DotNetGigs")));
}

The connection string is obtained from the appsettings.json file.

Creating the database

With the data model and context setup we're ready to create the actual database by using code first migrations.

We'll use the Entity Framework CLI tooling to generate and apply our migration file to create a local SQL Server database.

From the command line in the root of the project run:

dotnet ef migrations add initial

After the command completes you should see a new Migrations folder created in the project.

In addition to the migration file, a Snapshot and Designer file are also created. They are used to store the current state of the model and get updated with each subsequent migration. They help EF figure out the changes required to keep the database in sync with the model.

The next command applies the migration and creates the database.

dotnet ef database update

This creates the database with the identity-related tables and one representing the JobSeeker entity.

Creating new users

The responsibility of creating new users will belong to an action method on the AccountsController. It accepts a RegistrationViewModel, performs some validation on it and calls the UserManager service to create the user account in the database.

// POST api/accounts
[HttpPost]
public async Task<IActionResult> Post([FromBody]RegistrationViewModel model)
{
    if (!ModelState.IsValid)
    {
         return BadRequest(ModelState);
    }

    var userIdentity=_mapper.Map<AppUser>(model);
    var result = await _userManager.CreateAsync(userIdentity, model.Password);

    if (!result.Succeeded) return new BadRequestObjectResult(Errors.AddErrorsToModelState(result, ModelState));

    await _appDbContext.JobSeekers.AddAsync(new JobSeeker{IdentityId=userIdentity.Id, Location=model.Location});
    await _appDbContext.SaveChangesAsync();

    return new OkResult();
}

It's also worth mentioning the use of AutoMapper here to map the RegistrationViewModel to an AppUser. Secondly, there is additional validation happening implicitly on the viewmodel thanks to FluentValidation. I won't go into the setup of both of these right now but if you're interested please take a closer look at the code or drop me a question in the comments. If you're new to these libraries they're both great to have in your toolbox as they can really help to keep your code clean and DRY.

At this point, I tested the new user endpoint by running the project and using Postman to send a request and received a 200 OK success response - cool. Checking the database also confirmed a record was created in the AspNetUsers and JobSeekers tables.

Postman is a great tool for quickly doing manual testing of your api. Sending the same request again I get an error response from the method because the email is already in use. We'll look at proper error handling in the Angular app but for now, you can see how easy it is to quickly poke your api and ensure it responds as expected.

Sending an empty location in the request body triggers the fluent validation validator and results in an error.

Implement JSON Web Tokens

The final thing our ASP.NET Core server requires is the ability to authorize users using JSON Web Tokens. In a nutshell, JWT is a method for 2 parties to securely pass tokens back and forth that contain properties known as claims about a subject. Wikipedia has a decent summary of this usage. There's a bit more to claims but starting out a basic understanding of the concept is all you need.

For example, a server could generate a token that has the claim "logged in as admin" and provide that to a client. The client could then use that token to prove that it is logged in as admin.

I adopted most of the steps in this section from William Hallatt's excellent guide on Issuing and authenticating JWT tokens in ASP.NET Core WebAPI

Before we write any code to support JWT we need to ensure we have the required packages added to the ASP.NET Core project as mentioned at the start of the post. Make sure you have these 3 in your csproj file:

  • Microsoft.AspNetCore.Authentication.JwtBearer
  • Microsoft.IdentityModel.Tokens
  • System.IdentityModel.Tokens.Jwt

The first class we'll add is JwtIssuerOptions.cs which will provide the JWT claim properties for our generated tokens.

using System;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;

namespace DotNetGigs.Models 
{
public class JwtIssuerOptions
{
        /// <summary>
        /// 4.1.1.  "iss" (Issuer) Claim - The "iss" (issuer) claim identifies the principal that issued the JWT.
        /// </summary>
        public string Issuer { get; set; }

        /// <summary>
        /// 4.1.2.  "sub" (Subject) Claim - The "sub" (subject) claim identifies the principal that is the subject of the JWT.
        /// </summary>
        public string Subject { get; set; }

        /// <summary>
        /// 4.1.3.  "aud" (Audience) Claim - The "aud" (audience) claim identifies the recipients that the JWT is intended for.
        /// </summary>
        public string Audience { get; set; }

        /// <summary>
        /// 4.1.4.  "exp" (Expiration Time) Claim - The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
        /// </summary>
        public DateTime Expiration => IssuedAt.Add(ValidFor);

        /// <summary>
        /// 4.1.5.  "nbf" (Not Before) Claim - The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing.
        /// </summary>
        public DateTime NotBefore { get; set; } = DateTime.UtcNow;

        /// <summary>
        /// 4.1.6.  "iat" (Issued At) Claim - The "iat" (issued at) claim identifies the time at which the JWT was issued.
        /// </summary>
        public DateTime IssuedAt { get; set; } = DateTime.UtcNow;

        /// <summary>
        /// Set the timespan the token will be valid for (default is 5 min/300 seconds)
        /// </summary>
        public TimeSpan ValidFor { get; set; } = TimeSpan.FromMinutes(5);    

        /// <summary>
        /// "jti" (JWT ID) Claim (default ID is a GUID)
        /// </summary>
        public Func<Task<string>> JtiGenerator =>
          () => Task.FromResult(Guid.NewGuid().ToString());

        /// <summary>
        /// The signing key to use when generating tokens.
        /// </summary>
        public SigningCredentials SigningCredentials { get; set; }
    }
}

Next I added a related configuration section to my appsettings.json config.

...
"JwtIssuerOptions": {
    "Issuer": "dotNetGigs",
    "Audience": "http://localhost:5000/"
  }

The next thing we'll do is use the built-in Configuration API to read the JwtIssuerOptions settings from the config file and register them with IoC container.

I added the following to the ConfigureServices method in Startup.cs.

...
// jwt wire up
// Get options from app settings
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));

// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
  options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
  options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
  options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});

We need to add some additional code in Startup.cs to tell the ASP.NET Core middleware that we want to use JWT authentication on incoming requests. In the Configure method I added the following code:

...
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
var tokenValidationParameters = new TokenValidationParameters
{
  ValidateIssuer = true,
  ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],

  ValidateAudience = true,
  ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],

  ValidateIssuerSigningKey = true,
  IssuerSigningKey = _signingKey,

  RequireExpirationTime = false,
  ValidateLifetime = false,
  ClockSkew = TimeSpan.Zero
};

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
  AutomaticAuthenticate = true,
  AutomaticChallenge = true,
  TokenValidationParameters = tokenValidationParameters
});
...

The last major piece I added was the JwtFactory class that will perform the task of creating encoded tokens.

public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
{
   var claims = new[]
   {
      new Claim(JwtRegisteredClaimNames.Sub, userName),
      new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
      new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64), identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Rol),
      identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Id)
   };

  // Create the JWT security token and encode it.
     var jwt = new JwtSecurityToken(
         issuer: _jwtOptions.Issuer,
         audience: _jwtOptions.Audience,
         claims: claims,
         notBefore: _jwtOptions.NotBefore,
         expires: _jwtOptions.Expiration,
         signingCredentials: _jwtOptions.SigningCredentials);

     var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

     return encodedJwt;
}

The most interesting part of this class is the GenerateEncodedToken method which creates an encoded token containing the claims we'd like to exchange between the client and backend. Note we're using the JwtIssuerOptions we configured in the previous step by injecting them into the factory.

Authenticating users and issuing JSON Web Tokens

With the JWT infrastructure in place we're ready to put it into action using the AuthController. There's a single action/route at /api/auth/login that will authenticate the given credentials using the UserManager api and if successful return a new security token using the JwtFactory that can be used for subsequent authenticated requests by the user.

// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
       if (!ModelState.IsValid)
       {
            return BadRequest(ModelState);
        }

        var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
        if (identity == null)
        {
                return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
         }

         // Serialize and return the response
         var response = new
          {
                id=identity.Claims.Single(c=>c.Type=="id").Value,
                auth_token = await _jwtFactory.GenerateEncodedToken(credentials.UserName, identity),
                expires_in = (int)_jwtOptions.ValidFor.TotalSeconds
          };

         var json = JsonConvert.SerializeObject(response, _serializerSettings);
         return new OkObjectResult(json);
 }

With the action created, we can use postman once again to test our authentication endpoint and see how things look.

I used the mark@fullstackmark.com account we created in the previous test and POSTed the credentials to /api/auth/login and success! - the authentication passed and the server returned a JWT token.

Securing a controller with claims-based authorization

One of the claims we store as part of our token is Rol which is just a string representing a role named ApiAccess. This gets added in the JwtFactory GenerateClaimsIdentity helper method.

public ClaimsIdentity GenerateClaimsIdentity(string userName,string id)
{
  return new ClaimsIdentity(new GenericIdentity(userName, "Token"), new[]
  {
     new Claim(Helpers.Constants.Strings.JwtClaimIdentifiers.Id, id),
     new Claim(Helpers.Constants.Strings.JwtClaimIdentifiers.Rol, Helpers.Constants.Strings.JwtClaims.ApiAccess)
  });
}

With this role stashed in our token, we can use a claim based authorization check to give the role access to certain controllers and actions so that only users possessing the role claim can access those resources.

All I had to do to enable the claim check was register it in a policy in the ConfigureServices method in Startup.cs.

...
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
...

With the policy in place I created a new DashboardController.cs decorated with [Authorize(Policy = "ApiUser")] meaning that only users with the ApiAccess role claim as part of the ApiUser policy can access this controller.

[Authorize(Policy = "ApiUser")]
[Route("api/[controller]")]
public class DashboardController : Controller
{       
   public DashboardController()
   {
        
   }

   // GET api/dashboard/home
   [HttpGet("home")]
   public IActionResult GetHome()
   {
       return new OkObjectResult(new { Message = "This is secure data!" });
   }
}

I tested once again with postman by creating a GET request to the http://localhost:5000/api/dashboard/home endpoint but before sending it I also included a request header containing the JWT token that was created for us in the previous user authentication test. The header key is Authorization and the value is in the format of Bearer xxx where xxx is the JWT. Looking at the response I get back a 200 OK status and some secure data in the body.

We tested the happy path but what happens if a request is made with an invalid, missing or expired token? To test that I changed just a single character in the token making it invalid then sent the request again and received a 401 Unauthorized code as expected - looks like JWT claims authorization is working!

Angular app setup

With the backend complete, we can now turn our attention to building out the Angular frontend to see how JWT authentication works in a real world app. The app has 3 major functions:

Structuring the app with modules

Modules have existed since AngularJS 1.x and provide an effective mechanism to group related components, directives, and services, in a way that they may be combined with other modules to assemble an application. In this project, I created two modules to house the major features.

From within the src\app project folder on the command line I used the CLI to generate account and dashboard modules.

ng g module account
ng g module dashboard

After running the commands, new folders representing the modules are added to the project. We'll wire up the code in these shortly but we have a few more components to add first.

Creating a registration form component

Next step is to create a new form component so users can create a new account. To do that I went back to the command line and ran the following from within the src\app\account module folder.

ng g component registration-form

A new registration-form folder is generated containing associated .ts, .scss and .html files.

Creating additional components

I repeated the steps above to generate the remaining components we'll require including:

  • The login-form
  • A home component representing the default view for the app
  • A spinner comonent to display while the UI is busy

Communicating with the backend via UserService

The key functions for registering and authenticating users on the backend live in the UserService class.

register(email: string, password: string, firstName: string, lastName: string,location: string): Observable<UserRegistration> 
{    
   let body = JSON.stringify({ email, password, firstName, lastName,location });
   let headers = new Headers({ 'Content-Type': 'application/json' });
   let options = new RequestOptions({ headers: headers });

    return this.http.post(this.baseUrl + "/accounts", body, options)
      .map(res => true)
      .catch(this.handleError);
}

login(userName, password) 
{
 let headers = new Headers();
 headers.append('Content-Type', 'application/json');

 return this.http.post(
      this.baseUrl + '/auth/login',
      JSON.stringify({ userName, password }),{ headers })
      .map(res => res.json())
      .map(res => {
        localStorage.setItem('auth_token', res.auth_token);
        this.loggedIn = true;
        this._authNavStatusSource.next(true);
        return true;
      })
      .catch(this.handleError);
}

Note we're storing the authorization token issued by the server in the users's local storage via the localStorage.setItem('auth_token', res.auth_token) call. We'll see shortly how to use this token to make authenticated requests to the backend api.

Completing the registration form

With my component and service in place, I have the necessary pieces to complete the user registration feature. The major steps were to add the form markup to registration-form.component.html and add bind the submit button on the form to a method in the registration-form.component.ts class.

registerUser({ value, valid }: { value: UserRegistration, valid: boolean }) {
  this.submitted = true;
  this.isRequesting = true;
  this.errors='';
  if(valid)
  {
     this.userService.register(value.email,value.password,value.firstName,value.lastName,value.location)
       .finally(() => this.isRequesting = false)
       .subscribe(result  => {if(result){
          this.router.navigate(['/login'],{queryParams: {brandNew: true,email:value.email}});                         
          }},
       errors =>  this.errors = errors);
  }      
}

The method is pretty simple, it calls userService.register() passing along the user data and handles the observable response accordingly. If the server-side validation returns an error it is displayed to the user. If the request succeeds, the user is routed to the login view. The isRequesting property flag triggers the spinner so the UI can indicate that the app is busy while the request is in flight.

Completing the login form

The login form is nearly identical to the registration form. I added required markup to login-form.component.html and setup an event handler method in in the login-form.component.ts class.

login({ value, valid }: { value: Credentials, valid: boolean })
{
    this.submitted = true;
    this.isRequesting = true;
    this.errors='';
    if (valid) {
      this.userService.login(value.email, value.password)
        .finally(() => this.isRequesting = false)
        .subscribe(
        result => {         
          if (result) {
             this.router.navigate(['/dashboard/home']);             
          }
        },
        error => this.errors = error);
    }
}

The pattern here is identical, call userService.login() to make a request to the server with the given user credentials and handle the response accordingly. Again, either display any errors returned by the server or route the user to the Dashboard component if they've authenticated successfully.

Protected routes

Right now in our application, any user can navigate anywhere, let's change this by restricting access to certain areas to logged-in users only. The Angular router provides a feature specifically for this purpose in Navigation Guards.

A guard is simply a function added to your route configuration that returns either true or false.

true means navigation can proceed. false means navigation halts and the route is not accessed.

Guards are registered using providers so they can be injected into your component routing modules where needed.

In this app, I created auth.guard.ts to protect access to the dashboard which acts as an administrative feature only logged in users can see.

// auth.guard.ts
import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { UserService } from './shared/services/user.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private user: UserService,private router: Router) {}

  canActivate() {

    if(!this.user.isLoggedIn())
    {
       this.router.navigate(['/account/login']);
       return false;
    }

    return true;
  }
}

The AuthGuard is simply an @Injectable() class that implements CanActivate. It has a single method that checks the logged in status of the user by calling the isLoggedIn() method on the UserService.

isLoggedIn() is a little naive as it simply checks for the presence of the JWT token in local storage and if it exists we assume the user is logged in by returning true. If it is not found the user is redirected to the login page.

...
this.loggedIn = !!localStorage.getItem('auth_token')
...

To implement the guard in the dashboard routing module I simply imported and updated the root dashboard route with a CanActivate() guard property references it.

import { ModuleWithProviders } from '@angular/core';
import { RouterModule }        from '@angular/router';

import { RootComponent }    from './root/root.component';
import { HomeComponent }    from './home/home.component'; 

import { AuthGuard } from '../auth.guard';

export const routing: ModuleWithProviders = RouterModule.forChild([
  {
      path: 'dashboard',
      component: RootComponent, canActivate: [AuthGuard],

      children: [      
       { path: '', component: HomeComponent },
       { path: 'home',  component: HomeComponent },
      ]       
    }  
]);

The dasbhoard feature is now protected by the guard!

Making authenticated API requests

The last thing we need to do is pass our JWT back to the server for API calls that require authentication. This is where we'll be utilizing the authorization policy we created and applied previously to the DashboardController in our ASP.NET Core api.

I created a new dashboard service with a single method that retrieves some data for the Home page by making an authenticated HTTP call to the backend and passing the authorization token in the request header.

...
getHomeDetails(): Observable<HomeDetails> {
      let headers = new Headers();
      headers.append('Content-Type', 'application/json');
      let authToken = localStorage.getItem('auth_token');
      headers.append('Authorization', `Bearer ${authToken}`);
  
    return this.http.get(this.baseUrl + "/dashboard/home",{headers})
      .map(response => response.json())
      .catch(this.handleError);
  }

The method simply retrieves the auth_token from localStorage and includes it as the value for the Authorization header in the format Bearer '{auth_token}'.

With authenticated requests in place, I ran the project again and was able to complete an end to end test by creating a new user, logging in, and navigating to a protected route in the dashboard which displayed a piece of super-secure data!

Wrapping up

I mentioned in the intro that adding authentication in most systems is often a pain and I think that's proven by the length of this post but I hope at very least I've saved you some time and effort by outlining a clear plan of the major steps required to implement token based authentication with Angular and ASP.NET Core.

Security is a serious concern so please don't take every code sample in this tutorial verbatim and copy/paste into production-bound projects without careful review to ensure your implementation meets the standards required for securing your application.

If you have a question or are building something cool using Angular and ASP.NET Core please let me know in the comments below.

Thanks for reading!

source code

Get Started with Angular 2 and ASP.NET Core in Visual Studio Code

Getting off the ground with Angular 2 and ASP.NET Core right now can be a little intimidating. There's multiple ways to combine these two powerful frameworks to make something awesome. The goal of today's post is to clear up some of the confusion by showing you a sane, cross-platform approach to getting an Angular 2/ASP.NET Core app started using Visual Studio Code, .NET Core CLI and angular-cli.


Dev environment

I'm using Windows 10 with the latest versions of:

  • Visual Studio Code - v1.11.1
  • .NET Core SDK v1.0.1 dotnet --version
  • node v6.10.0 node -v
  • npm v3.10.10 npm -v

Finally, I had to install the C# for Visual Studio Code extension.

Angular CLI

Angular 2 comes with an awesome new tool in angular-cli that can create your project, add files, and perform a number of other development tasks such as testing, bundling, and deployment. It is the new swiss army knife of angular development.

To get started make sure you're running Node 6.9.0 or higher and NPM 3 or higher. From a command prompt install it using npm.

npm install -g @angular/cli

Next, we'll create a new project using ng new. This command creates a new folder for the project using our app name DotNetGigs. The --style argument tells the generator we want the app to pre-compile our css using sass with scss syntax. The documentation contains all the commands and switches available for working with angular-cli. I highly recommend you check it out.

ng new DotNetGigs --style=scss

After the generator creates the app you can open it from the command line by launching Visual Studio Code directly from the project folder.

cd dotnetgigs
code .

Adding Bootstrap

The app template generated by the CLI has no styling so I'd like to jazz things up a bit with bootstrap. Back at the command prompt I use npm to install Bootstrap 4 alpha in the app directory:

npm install bootstrap@next --save

Then I modified the .angular.cli.json file to add the required script files to the scripts array.

"scripts": [
  "../node_modules/jquery/dist/jquery.js",
  "../node_modules/tether/dist/js/tether.js",
  "../node_modules/bootstrap/dist/js/bootstrap.js"
],

Finally, I added the bootstrap css to the styles array.

"styles": [
  "../node_modules/bootstrap/dist/css/bootstrap.css",
  "styles.scss"
],

A new component

I want to build out the layout of the UI a bit by adding a new component. Using the CLI I generate a new header component within the app folder.

cd src/app
ng generate component header

The command created a new folder and associated files for the html, css, test and class code. It also updated our app.module so other components will be aware of it. Notice the same naming convention is used as in the rest of the app. Using the CLI provides a consistent and modular approach to extending our application following baked-in best practices and patterns. This is one of its biggest benefits.

Next I'll add the bootstrap navbar markup to the header.component.html file.

<nav class="navbar navbar-toggleable-md navbar-inverse fixed-top bg-inverse">
  <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
  <span class="navbar-toggler-icon"></span>
  </button>
  <a class="navbar-brand" href="#">dotnetGigs</a>
  <div class="collapse navbar-collapse" id="navbarCollapse">
  <ul class="navbar-nav mr-auto">
  <li class="nav-item active">
  <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
  </li>
  <li class="nav-item">
  <a class="nav-link" href="#">Link</a>
  </li>
  <li class="nav-item">
  <a class="nav-link disabled" href="#">Disabled</a>
  </li>
  </ul>
  <form class="form-inline mt-2 mt-md-0">
  <input class="form-control mr-sm-2" type="text" placeholder="Search">
  <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
  </form>
  </div>
  </nav>

And finally, add the new header component to the main app.component.html template.

<!-- app.component.html -->
<app-header></app-header>

<!-- container -->
<div class="container-fluid">
	<!-- Begin page content -->
	<p>Content here!</p>
</div>
<!-- /container -->

With everything in place, I'm ready to smoke test my app to see how things look. Back on the command line I issue a simple ng serve to build and fire up the app on the node dev server @ http://localhost:4200

Voila our angular app with bootstrap navbar is working!


ASP.NET Core backend

Time to build our backend. To get started in the root project folder I ran the dotnet CLI command to generate a new, empty ASP.NET Core application.

dotnet new web

This command generates a few files necessary to get a bare-minimum ASP.NET Core server on its feet: Program.cs, Startup.cs and dotnetGigs.csproj.

Note: when opening one of *.cs files for the first time in Visual Studio Code you'll be prompted to restore packages required by the project so be sure to accept those requests.

Now we need to use the CLI to add static file support to serve the index.htm, css, images and javascript files required by our SPA.

dotnet add package microsoft.aspnetcore.staticfiles

After executing this, you'll likely see another "unresolved dependencies" message in Visual Studio Code. Simply choose to restore the package.

With the package added, we just need a small change in Startup.cs to add static file support in the middleware.

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

  if (env.IsDevelopment())
  {
  app.UseDeveloperExceptionPage();
  }

  app.UseDefaultFiles();
  app.UseStaticFiles();
}

Trigger the Angular build from ASP.NET Core

Add a build step in the .csproj file to run the angular-cli build command after our ASP.NET Core project builds.

<Target Name="AngularBuild" AfterTargets="Build">
  <Exec Command="npm run build" />
</Target>

Serve the Angular app from ASP.NET Core

Finally, we need to let Angular know that its build output should go to the wwwroot folder of our server. In the .angular.cli.json file simply change the "outDir" to "wwwroot":

{
      "root": "src",
      "outDir": "wwwroot",
      "assets": [
      ...

Run it!

Hit F5 in Visual Studio Code to launch the debugger. Your Angular app should now be hosted by ASP.NET Core and running on port 5000 instead of 4200 which is the angular-cli default - sweet! 🙂

Wrapping up

Properly integrating Angular client-side code with server-side code can be complicated and time-consuming. But by using the new CLI tools with just a few simple config changes we're able to very quickly create a solid foundation for an Angular 2/ASP.NET Core solution managed entirely by Visual Studio Code.

If you have a question or are building something cool using this approach please let me know in the comments below.

Thanks for reading!

Source code