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

Beginning Test-Driven Development in .NET Core

My previous two posts focused on making better software in asp.net core by applying dependency injection and unit testing to our software design. Today, we'll extend that theme by looking at test-driven development in .NET Core. My goal is to show you how it can be used to create better applications and improve your productivity as a developer - sound good?


What is test-driven development?

TDD is not a specific framework or technology - it is an approach. It's a thought process where we think of testing our code from a different perspective by writing the test code first before a single line of related functional code exists!

In a typical scenario, you first write some code, then write some tests - rinse and repeat. In TDD, you write the test case before you write a single line of code. Naturally, the test fails. Next, you write just enough code to satisfy the test so it passes. After that, we refactor as necessary to improve the design while ensuring that all tests still pass. This cycle is commonly known as Red, Green, Refactor.

TDD Cycle
You repeat this cycle many times. Typically, the initial cycles are very quick but gradually slow down as more time is spent refactoring.

Project setup

I'm using Visual Studio 2017 but will begin by setting up my project using and the new dotnet core tooling.

From the command line in my project folder, I first create a new solution file:

dotnet new sln -n dotnetCoreTDD

Next, I made a new console app project.

dotnet new console -n TDDDemoApp

Then, a new test project based on xUnit unit testing tool.

dotnet new xunit -n TDDDemoAppTests

Finally, I added both projects to my new solution.

dotnet sln dotnetCoreTDD.sln add tdddemoapp/tdddemoapp.csproj
dotnet sln dotnetCoreTDD.sln add tdddemoapptests/tdddemoapptests.csproj

TDD process in action

Opening the solution, I'm starting with a completely clean slate - an empty console and test project. Let's change that.

I'm thinking about a component for my program that can analyze a piece of text and return some useful information about it like the total word count, repeated words, maybe even more advanced things like grammar and spelling.

With a few requirements in mind for this new component, I jump straight to my test project and make a new class for its tests: TextAnalyzerUnitTests.cs

To begin, I'd like a function to simply return the total word count of a given piece of text. My first step is to write a test for it. Here's what I came up with:

The test case simply news up TextAnalyzer, calls the method and checks for the expected result. As I have not written a single line of code for TextAnalyzer yet, I can't run this test or even compile the project.

We are definitely in the red.

The next step is to create the TextAnalyzer component. To start, I created a new TextAnalyzer.cs class in the console project and added the minimal amount of code to get everything compiling successfully.

At this point, we don't care about the purity of our design or elegant code. We'll take care of that when we refactor. For now, GetTotalWordCount() only throws an exception but with this stubbed out everything compiles and I can at least run the test.

Our test fails as expected because we're just throwing an exception but we're ready to fix that now by refactoring GetTotalWordCount() with the necessary code to count the words in a given string.

public int GetTotalWordCount(string text)
{
  int wordCount = 0, index = 0;

  while (index < text.Length)
  {
  // check if current char is part of a word
  while (index < text.Length && !char.IsWhiteSpace(text[index]))
  index++;

  wordCount++;
 
  // skip whitespace until next word
  while (index < text.Length && char.IsWhiteSpace(text[index]))
  index++;
  }

  return wordCount;
}

With the method properly implemented, I rerun the test and success - a green check! The method is working correctly and returns the expected word count in the test case.

We've just created a test-driven class and method - sweet! This process is very iterative. So at this point, we might spend more time refactoring GetTotalWordCount() to improve its design by adding things like validation, error handling etc. or we might move on to our next requirement. Either way, we follow the same process of writing the failing test case first, adding a minimal amount of code to pass it, and finally an exercise of refactoring and testing to ensure everything still passes.

Benefits of TDD

  • Intentions are clear - We didn't write a line of code until we knew exactly what we wanted to do because we let our tests drive our code design. This results in no unnecessary code and a laser focused design.

  • You have to eat your own dog food - TDD forces you to write your tests as a consumer of your own code. This makes you continuously think about its API.

  • Iterative cycles - First we started with no code, then we added the bare minimum to get things compiling, finally we implemented the real functionality. We quickly did 3 iterations in this guide but in a real-world project, we'll perform many more iterative rounds of development and testing as our project evolves over time. The iterative process of TDD means we can react to new or changing requirements with confidence.

  • Defects are identified early - Testing early and often means many bugs will surface as soon as they're introduced. The earlier a bug is caught, the cheaper (in time and money) it is to fix.

  • Testing guaranteed - Despite our best intentions, we all know adequate unit testing just doesn't always happen. TDD forces us into the habit of writing unit tests for production code - no excuses.

Wrapping up

Applying TDD requires a fundamental change in how we think about writing code. So if you're planning to try it out on your next project (or within an existing one) be patient and approach it in baby steps. To get a feel for it, try and isolate a single class or component and apply the process of thinking through the design from a testing perspective. If you find yourself simultaneously changing the code and tests it's usually a sign of a misstep in your design - no big deal. Keep iterating and you'll get it. 🙂

Thanks for reading!

Source code