In my last article, Security in Angular: Part 1, you learned to create a set of Angular classes to support user authentication and authorization. You used these classes to log in a user and make menus and buttons visible and invisible. In this article, you'll use a .NET Core Web API project to authenticate a user against a SQL Server table. You'll return an authorization object by reading claim information from a claim table. You'll also secure your Web API methods using the JSON Web Token (JWT) standard.

There are several steps you need to perform to secure your Web API methods. You'll be guided through each of the following steps in this article:

  • Download and set up a sample application and database.
  • Create Entity Framework security classes.
  • Create a class to authenticate users and return an authorization object.
  • Create a security controller.
  • Modify the Angular application to call a security controller.
  • Add JSON Web Tokens to secure Web API calls.
  • Create an HTTP interceptor.

Download the Starting Application

In the last article, all the data for users, products, and categories were in mock classes. For this article, I've provided you with a starting application that contains an ASP.NET Core Web API project to retrieve product and category data from Web API method calls. In this article, you build the Web API security classes to authenticate a user and return an authorization object to your Angular application.

Download the starting sample for this article from the CODE Magazine website, or from http://pdsa.com/downloads. Select “PDSA/Fairway Articles” from the Category drop-down, and then choose Security in Angular - Part 2. After you've downloaded the ZIP file, there are two folders within the ZIP entitled Start and End. Extract all of the files and folders within the Start folder to a folder somewhere on your hard drive. You can then follow along with this article to build all of the Web APIs to secure your Angular application.

Load the Visual Studio Code Workspace

After extracting the Start folder from the ZIP file, double-click on the PTCApp.code-workspace file to load the two projects in this application. If you double-click on this workspace file, the solution is loaded that looks like Figure 1. There are two projects: PTC is the Angular application and PtcApi is the ASP.NET Core Web API project.

Figure 1: The starting application has two projects: the Angular project (PTC) and the .NET Core Web API project (PtcApi).
Figure 1: The starting application has two projects: the Angular project (PTC) and the .NET Core Web API project (PtcApi).

The PTC Database

There's a SQL Server Express database named PTC included in the ZIP file. Open the PtcDbContext.cs file located in the \PtcApi\Model folder. Change the path in the connection string constant to point to the folder in which you installed the files from this ZIP file. If you don't have SQL Server Express installed, you can use the PTC.sql file located in the \SqlData folder to create the appropriate tables in your own SQL Server instance.

Security Tables Overview

The PTC database has two tables in addition to the product and category tables: User and UserClaim (Figure 2). These tables are like the ones you find in the ASP.NET Identity System from Microsoft. I've simplified the structure just to keep the code small for this sample application.

Figure 2: Two security tables are needed to authenticate and authorize a user.
Figure 2: Two security tables are needed to authenticate and authorize a user.

User Table

The user table generally contains information about a specific user, such as their user name, password, first name, last name, etc. For the purpose of this article, I've simplified this table to only a unique ID (UserId), the name for the log in (UserName), and the Password for the user, as shown in Figure 3. Please note that I'm using a plain-text password in the sample for this application. In a production application, this password would be either encrypted or hashed.

Figure 3: Example data in the User table
Figure 3: Example data in the User table

User Claim Table

In the UserClaim table, there are four fields: ClaimId, UserId, ClaimType, and ClaimValue, (as shown in Figure 4). The ClaimId is a unique identifier for the claim record. The UserId is a foreign key relation to the User table. The value in the ClaimType field needs to match the exact property name you create in the AppUserAuth class from Angular, and the one you're going to create in C# in the next section of this article. The value in the ClaimValue should be a true value for any ClaimType you enter for that user.

Don't enter a record for a specific user and claim type if you don't wish to give the user that claim. For example, the CanAddProduct property in the authorization object should be set to false for the user BJones. Therefore, don't enter a record in the UserClaim table for BJones with a ClaimType equal to CanAddProduct. Later in this article, you'll learn how this process works.

Figure 4: Example data in the UserClaim table
Figure 4: Example data in the UserClaim table

Create C# Security Classes

In the Security in Angular - Part 1 article, you created Angular classes to represent a user and a user authorization object with properties to bind to. Create these same classes using C# within the PtcApi project. You're going to build the C# classes with the same class and property names as those you created in TypeScript.

AppUser Class

The AppUser class is an Entity Framework class that represents a single record you retrieve from the User table. For each property in the User table, create a corresponding property with the appropriate data annotations. Right mouse-click on the Model folder and add a new file named AppUser.cs. Add the code shown in Listing 1 into this new file.

Listing 1: Create a AppUser Entity Framework class

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace PtcApi.Model
{
    [Table("User", Schema = "Security")]
    public partial class AppUser
    {
        [Required()]
        [Key()]
        public Guid UserId { get; set; }

        [Required()]
        [StringLength(255)]
        public string UserName { get; set; }

        [Required()]
        [StringLength(255)]
        public string Password { get; set; }
    }
}

The Using statements in this file are used to supply the appropriate data annotations for the Entity Framework. The [Table] attribute specifies the name of the table this class maps to: User. The User table is located in the database schema named Security, so pass the Schema property to this attribute as well.

The [Required] attribute should be added to all fields, because all fields in the User table are marked as not allowing null values. The [StringLength] attribute can be added to the UserName and Password fields in case you want to perform some validation on the data prior to inserting or updating. This article won't cover this, but I like adding appropriate data annotations to my properties.

Create AppUserClaim Class

The AppUserClaim class is an Entity Framework class that represents a single record you retrieve from the UserClaim table. For each property in the UserClaim table, create a corresponding property with the appropriate data annotations. Right mouse-click on the Model folder and add a new file named AppUserClaim.cs. Add the code shown in Listing 2.

Listing 2: Create a AppUserClaim Entity Framework class

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace PtcApi.Model
{
    [Table("UserClaim", Schema = "Security")]
    public partial class AppUserClaim
    {
        [Required()]
        [Key()]
        public Guid ClaimId { get; set; }

        [Required()]
        public Guid UserId { get; set; }

        [Required()]
        [StringLength(100)]
        public string ClaimType { get; set; }

        [Required()]
        [StringLength(50)]
        public string ClaimValue { get; set; }
    }
}

The Using statements and attributes used are similar to what you used in the AppUser class. The [Table] attribute specifies the UserClaim table, located in the Security schema. All of the other attributes attached to the properties are self-explanatory.

Modify PtcDbContext Class

When using the Entity Framework, you typically create a class that inherits from the Entity Framework's DbContext class. In the PtcApi project, this class is named PtcDbContext. For each of the EF classes you create, add a collection property of the type DbSet<T> to this DbContext class. Open the PtcDbContext.cs file in the Model folder and add two new properties for the AppUser and AppUserClaim classes, as shown in the code snippet below.

public DbSet<AppUser> Users { get; set; }
public DbSet<AppUserClaim> Claims { get; set; }

Once you have these two properties created, you can retrieve users and claims from them. You may also use these properties to add, edit, and delete records from each table.

Create Authorization Class

If you remember from Part 1 of this article series, you created a user authorization class in TypeScript that contained properties to make menus and buttons visible and invisible. You need to create this same class in C# so it may be returned from a Web API method call and mapped to the TypeScript class in Angular. Right mouse-click on the Model folder and add a new file named AppUserAuth.cs. Add the code shown in Listing 3 into this file.

Listing 3: Create a user authorization object with properties to match those in TypeScript

namespace PtcApi.Model
{
    public class AppUserAuth
    {
        public AppUserAuth()
        {
            UserName = "Not authorized";
            BearerToken = string.Empty;
        }

        public string UserName { get; set; }
        public string BearerToken { get; set; }
        public bool IsAuthenticated { get; set; }
        public bool CanAccessProducts { get; set; }
        public bool CanAddProduct { get; set; }
        public bool CanSaveProduct { get; set; }
        public bool CanAccessCategories { get; set; }
        public bool CanAddCategory { get; set; }
    }
}

Create Security Manager Class

There are several steps that must be accomplished to authenticate a user and create an authorization object to send back to Angular. Instead of writing all of that code within a controller, build a class named SecurityManager for that logic. The controller may then create an instance of that class and call one method to authenticate the user and to retrieve an authorization object. Right mouse-click on the Model folder and add a new file named SecurityManager.cs. Add the code shown in Listing 4 into this file.

Listing 4: Build a security manager class instead of writing a lot of code in a controller class.

using System;
using System.Collections.Generic;
using System.Linq;
using PtcApi.Model;

namespace PtcApi.Security
{
    public class SecurityManager
    {
        public AppUserAuth
        AuthenticateUser(AppUser user)
        {
            AppUserAuth ret = new AppUserAuth();
            AppUser authUser = null;

            using (var db = new PtcDbContext())
            {
                // Attempt to validate user
                authUser = db.Users.Where(
                    u => u.UserName.ToLower()
                        == user.UserName.ToLower()
                       && u.Password
                       == user.Password).FirstOrDefault();
            }

            if (authUser != null)
            {
                // Build User Security Object
                ret = BuildUserAuthObject(authUser);
            }

            return ret;
        }

        protected List<AppUserClaim> GetUserClaims(AppUser authUser)
        {
            List<AppUserClaim> list = new List<AppUserClaim>();

            using (var db = new PtcDbContext())
            {
                list = db.Claims.Where(
                u => u.UserId == authUser.UserId)
                .ToList();
            }

            return list;
        }

        protected AppUserAuth
        BuildUserAuthObject(AppUser authUser)
        {
            AppUserAuth ret = new AppUserAuth();
            List<AppUserClaim> claims = new List<AppUserClaim>();

            // Set User Properties
            ret.UserName = authUser.UserName;
            ret.IsAuthenticated = true;
            ret.BearerToken = new Guid().ToString();

            // Get all claims for this user
            claims = GetUserClaims(authUser);

            // Loop through all claims and set properties of user object
            foreach (AppUserClaim claim in claims)
            {
                try
                {
                    typeof(AppUserAuth)
                    .GetProperty(claim.ClaimType)
                    .SetValue(ret, Convert.ToBoolean(claim.ClaimValue), null);
                 }
                 catch
                 {
                 }
            }

            return ret;
        }
    }
}

The AuthenticateUser() Method

The AuthenticateUser() method is the only method exposed from the SecurityManager class. The security controller you're going to create in the next section of this article accepts an AppUser object from the Angular application via a Web API call. You're going to pass this AppUser object to the AuthenticateUser() method.

The AuthenticateUser() method creates an instance of a PtcDbContext class. Once this object is created, the Users property is accessed and a where filter is applied to locate a user in the User table where the UserName field matches the UserName property passed in from the Angular application. In addition, the password must match the value in the User table as well.

If a valid user is located in the User table, the AppUser object retrieved from the Entity Framework is passed on to the BuildUserAuthObject() method. It's in this method that the user authorization object is created.

The BuildUserAuth() Method

This method is responsible for building the user authorization object that's sent back to Angular with all the appropriate Boolean properties set. The first thing this method does is create a new AppUserAuth object. The UserName property is set with the authenticated user name passed in. The IsAuthenticated property is set to true to signify that the user is valid. The BearerToken property is going to be set to a random GUID for now. Later in this article, you'll fill this in with a real bearer token generated using the JSON Web Token system.

Next, all the claims in the UserClaim table for this user are gathered by calling a method named GetUserClaims(). Once you have that list of claims, you loop through each claim one-by-one. Each time through the loop, use reflection on the AppUserAuth object to attempt to access the property name that's in the ClaimType property read in from the UserClaim table. This is why I mentioned earlier that the names you add to the UserClaim table must match the properties in the AppUserAuth class, as it allows you to use .NET reflection to set each property value to the value contained in the ClaimValue field.

Once this method has looped through all claims for the authenticated user, the AppUserAuth properties have either been set to a true value, or, if the property didn't exist in the UserClaim table, then the default value of false is in the other properties.

NOTE: This method of setting properties is fine when you don't have a lot of security in your Angular application. For an application with many security claims, you should return the list of claims as part of the user authorization object. Of course, once you have a list of claims, and not individual properties, you can't take advantage of binding properties to a menu or a button. Instead, you'll have to create a custom structural directive in Angular to make menus and buttons visible and invisible. Creating this type of security will be the topic of discussion in the third part of this series on Angular security.

The GetUserClaims() Method

The GetUserClaims() method is where you retrieve a list of claims for the user who's been authenticated. In this method, you create a new instance of the PtcDbContext class. Access the Claims property and apply a Where filter to only retrieve those claims that match the UserId property from the authenticated user to those records in the UserClaim table that have that same UserId value. The list of claims is returned from this method and used in the BuildUserAuth() method to set the appropriate properties in the user authorization object.

Add Security Controller

It's now time to build the security controller that the Angular application calls. Angular passes the user credentials in an AppUser object to the Web API method. The method returns an AppUserAuth object with the appropriate properties set by calling the AuthenticateUser() method. Right mouse-click on the Controllers folder and add a new file named SecurityController.cs. Add the code shown in Listing 5 into this file.

Listing 5: The Security Controller authenticates the user and returns the authorization object

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using PtcApi.Security;
using PtcApi.Model;

namespace PtcApi.Controllers
{
    [Route("api/[controller]")]
    public class SecurityController : BaseApiController
    {
        [HttpPost("login")]
        public IActionResult Login([FromBody]AppUser user)
        {
            IActionResult ret = null;
            AppUserAuth auth = new AppUserAuth();
            SecurityManager mgr = new SecurityManager();

            auth = mgr.AuthenticateUser(user);
            if (auth.IsAuthenticated)
            {
                ret = StatusCode(StatusCodes.Status200OK, auth);
            }
            else
            {
                ret = StatusCode(StatusCodes.Status404NotFound, "Invalid User Name/Password.");
            }

            return ret;
        }
    }
}

The Login() Method

The Login() method accepts an AppUser object from the Angular application. Next, it creates an instance of the SecurityManager and passes the AppUser object to the AuthenticateUser() method. The AppUserAuth object returned from the AuthenticateUser() method has the IsAuthenticated property set to a true value if the user has been validated in the User table, and the security properties have been set.

If the user is authenticated, call the StatusCode() method passing in an enumeration to specify a status code of 200 (OK), and passing in the user authorization object. This method generates an IActionResult object to tell Angular that the call to the Login() method succeeded and that the resulting data is in the payload passed back.

If the user isn't authenticated, pass in an enumeration of 404 (NotFound) to the StatusCode() method, and a string specifying that the user name and password combination wasn't valid. When this code is returned to Angular, it signifies an error condition, and your Angular code needs to handle this, as appropriate.

Call the Web API from Angular

It's now time to replace the mock calls for authentication and authorization in Angular with calls to the new Web API Login() method. You need to add the HttpClient, HttpHeaders, and the tap classes to your SecurityService class. Go back to the PTC project and open the security.service.ts file located in the \src\app\security folder and add the following import statements.

import {HttpClient, HttpHeaders}
from '@angular/common/http';
import { tap } from 'rxjs/operators/tap';

Add two constants just under the import statements. The first constant is the location of the Web API controller. The second constant holds header options required by the HttpClient when POSTing data to a Web API call. You're going to use an HTTP POST to send the AppUser object containing the user name and password to the Login() method.

const API_URL = "http://localhost:5000/api/security/";
const httpOptions = {
    headers: new HttpHeaders({'Content-Type': 'application/json'})
};

Have Angular inject the HttpClient service into your security service class so you can make the HTTP call to the Login() method. Dependency injection is used extensively throughout Angular to provide services such as HttpClient to different components. The HttpClientModule has already been imported in the AppModule class. This module must always be imported to allow components to use the HttpClient service.

constructor(private http: HttpClient) { }

Modify the Login() Method

Within the security service class, you need to remove the hard-coded logic in the login() method. Modify the login() method as shown in Listing 6 to call the Login() method in the SecurityController class. If valid data is returned from this call, the pipe() method is used to pass that data to the tap() method. Within the tap() method, assign the data returned to the securityObject property.

Listing 6: Modify the login() method to make the Web API call.

login(entity: AppUser): Observable<AppUserAuth> {
    // Initialize security object
    this.resetSecurityObject();

    return this.http.post<AppUserAuth>(
        API_URL + "login",
        entity, httpOptions).pipe(
        tap(resp => {
           // Use object assign to update the current object
           Object.assign(this.securityObject, resp);

           // Store into local storage
           localStorage.setItem("bearerToken",
               this.securityObject.bearerToken);
        }));
}

It's very important that you don't use the equal sign to assign to the securityObject property. If you do, bound references to the securityObject are wiped out, and your menus and other UI elements bound to properties on the securityObject no longer work. Instead, use the Object.assign() method to copy the data from one object to another.

It's very important that you don't use the equal sign to assign to the securityObject property.

Within the tap() method, store the bearer token into local storage. The reason for storing this token is used when building an HTTP Interceptor class. You'll learn to create this class a little later in this article.

Modify the Login Component

Because it's possible that the Web API may return a status code of 404 (not found), be sure to handle this code so you can display the Invalid User Name/Password error message to your user. Open the login.component.ts file and add the error block in the subscribe() method. The login HTML contains a Bootstrap alert that has the words Invalid User Name/Password. in a <p> tag. This alert only shows up if the securityObject is not null and the isAuthenticated property is set to false. In the error block, assign a new instance of the AppUserAuth class to the securityObject property.

login() {
    this.securityService.login(this.user)
        .subscribe(resp => {
            this.securityObject = resp;
            if (this.returnUrl) {
               this.router.navigateByUrl(this.returnUrl);
            }
        }, () => {
            // Display error message
            this.securityObject = new AppUserAuth();
        });
}

Try It Out

Save all of your changes and make sure that the Web API project and the Angular project is running. Go to the browser and attempt to log in using either “psheriff” or “bjones” with a password of "P@ssw0rd. If you've done everything correctly, the Web API is now being called for authenticating the user name and password. Try logging in with an invalid login ID and password to make sure the error handling is working.

Remove Mock Login File

Now that you're retrieving data from a Web API call and not the mock data, you can remove \security\login-mocks.ts file. Once this file is removed, open the \security\security.service.ts file and remove the import statement for the login-mocks file.

Authorizing Access to the Web API Call

Now that you've created these Web API calls, you need to ensure that only those applications authorized to call your APIs can do so. In .NET, use the [Authorize] attribute to secure a controller class, or individual methods within a controller class. However, there must be some authentication/authorization component in the .NET runtime to provide data to this attribute. The [Authorize] attribute must be able to read that data to decide if the user has permissions to call the method.

There are many different authentication/authorization components you can use, such as Microsoft Identity, OAuth, and JSON Web Token (JWT), just to mention a few. In this article, you're going to use JSON Web Token.

Secure the Product Get() Method

To show you what happens when you apply the [Authorize] attribute to a method, open the ProductController.cs file and add the [Authorize] attribute to the Get() method, as shown in the code snippet below. You need to add a Using statement to use the [Authorize] attribute. The Using statement is using Microsoft.AspNetCore.Authorization.

[HttpGet]
[Authorize]
public IActionResult Get()
{
    // REST OF THE CODE
}

Try It Out

If you haven't already done so, stop the PtcApi Web API project from running. Save your changes and run the PtcApi project again. Log in as “psheriff” and click on the Products menu. No product data is displayed because you attempted to call a method that was secured with the [Authorize] attribute. Press the F12 key to bring up the developer tools and you should see something that looks like Figure 5.

Figure 5: You receive a status code of 500 when you use the Authorize attribute without registering an authentication service.
Figure 5: You receive a status code of 500 when you use the Authorize attribute without registering an authentication service.

Notice that you're getting a status code of 500 instead of a 401 (Unauthorized) or 403 (Forbidden). The reason is that you haven't registered an authentication service with .NET Core so the [Authorize] attribute doesn't get any data and throws a generic exception. You must register and configure an authentication system such as Microsoft Identity, OAuth, or JWT to return a 401 instead of a 500.

Add the JWT Configuration

JSON Web Token (JWT) is a standard method to securely transmit data between your client and your server as a JSON object. The data contained within the object is digitally signed, which means that it can be verified and trusted. You can read more about JWT at http://www.jwt.io. To use the JSON Web Token system in .NET Core and Angular, there are a few steps you must perform.

  • Add the JWT package to .NET Core.
  • Add JWT bearer token checking package to .NET Core.
  • Store default JWT settings in a configuration file.
  • Read JWT settings from the configuration file and place into a singleton class.
  • Register JWT as the authentication service.
  • Add bearer token options to validate incoming tokens.
  • Build a JWT token and add it to the user security object.
  • Pass the JWT token back to Angular.

Add JWT Package

The first thing you must do in your .NET Core Web API project is to add some packages to use the JSON Web Token system. Open a terminal window in your PtcApi project and enter the following command all on one line.

dotnet add package System.IdentityModel.Tokens.Jwt

After running this command, you'll be prompted to execute the restore command. Click on the Restore button to execute this command.

Add Bearer Token Check

In addition to the JWT package, add a package to ensure the bearer token is passed in from the client. Add this package to your PtcApi project using the following command in your terminal window.

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

After running this command, you'll be prompted to execute the restore command. Click on the Restore button to execute this command.

Store JWT Information in appsettings.json

For the bearer token to be verifiable and trusted, you're going to need to generate a JSON Web Token object. There are four key pieces of information needed to create this object.

  • A secret key used for hashing data sent to the client
  • The name of the issuer of the token
  • The intended audience of the token
  • How many minutes to allow the token to be valid

You're going to need all of the listed items in two places in your code: once when you configure JWT in the .NET Core Startup class, and once when you generate a new token specifically for a user. You don't want to hard-code data into two places, so use the .NET Core configuration system to retrieve this data from the appsettings.json file located in the root folder of the PtcApi project. Open the appsettings.json file and add a new entry named JwtSettings and add a new literal object, as shown in Listing 7.

Listing 7: Add your JWT information to the appsettings.json file.

{
    "Logging": {
        "IncludeScopes": false,
        "Debug": {
            "LogLevel": {
                "Default": "Warning"
            }
        },
        "Console": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    },
    "JwtSettings": {
        "key": "This*Is&A!Long)Key(For%Creating@A$SymmetricKey",
        "issuer": "http://localhost:5000",
        "audience": "PTCUsers",
        "minutesToExpiration": "10"
    }
}

Add JwtSettings Class

You need a way to get the settings read in from the appsettings.json file. An instance of a Configuration object can be injected into any class to allow you to read settings from a JSON file. However, I prefer to use my own class with real properties for each property I create in the JSON file. Right mouse-click on the Model folder and add a new file named JwtSettings.cs and add the following code.

public class JwtSettings {
    public string Key { get; set; }
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public int MinutesToExpiration { get; set; }
}

Read the Settings

Open the Startup.cs file and add a new method named GetJwtSettings(). In this method, create a new instance of the JwtSettings class and use the Configuration object, injected into the Startup class, to set each property from the corresponding JSON property in the appsettings.json file. After adding this method, you must add a using statement for the System namespace.

public JwtSettings GetJwtSettings() {
    JwtSettings settings = new JwtSettings();
    settings.Key = Configuration["JwtSettings:key"];
    settings.Audience = Configuration["JwtSettings:audience"];
    settings.Issuer = Configuration["JwtSettings:issuer"];
    settings.MinutesToExpiration = Convert.ToInt32(Configuration
        ["JwtSettings: minutesToExpiration"]);
    return settings;
}

Create a Singleton for the JWT Settings

Locate the ConfigureServices() method and create an instance of the JwtSettings class near the top of the method. Call the GetJwtSettings() method to read all of the settings into the JwtSettings object. Add the instance of the JwtSettings object as a singleton to the .NET core services so you can inject the JwtSettings object into any controller.

public void ConfigureServices(IServiceCollection services)
{
    // Get JWT Settings from JSON file
    JwtSettings settings;
    settings = GetJwtSettings();
    services.AddSingleton<JwtSettings>(settings);
    // REST OF THE CODE HERE
}

Register JWT as the Authentication Provider

Below the code you just added, add the code shown in Listing 8 to register JWT as an authentication provider. Call the AddAuthentication() method on the services object and set the two properties on the options, DefaultAuthenticateScheme and DefaultChallengeScheme, to the value JwtBearer.

Listing 8: Add JWT as the authentication service to your Web API project

public void ConfigureServices(IServiceCollection services)
{
    // Get JWT Token Settings from JSON file
    JwtSettings settings;
    settings = GetJwtSettings();
    services.AddSingleton<JwtSettings>(settings);

    // Register Jwt as the Authentication service
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "JwtBearer";
        options.DefaultChallengeScheme = "JwtBearer";
    })
    .AddJwtBearer("JwtBearer", jwtBearerOptions =>
    {
        jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(settings.Key)),
            ValidateIssuer = true,
            ValidIssuer = settings.Issuer,

            ValidateAudience = true,
            ValidAudience = settings.Audience,

            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromMinutes(settings.MinutesToExpiration)
        };
    });

// REST OF THE CODE HERE
}

Next, call the AddJwtBearer() method and set options for the bearer token using a TokenValidationParmeters object. It's in this object that you set properties using the values read in from the appsettings.json file. Most of these properties are self-explanatory. For additional information on what each one does, you can do a Google search on JWT and read one of any several articles on how to configure JWT.

After adding this code, you're going to have to add a couple more Using statements. Namely; using Microsoft.IdentityModel.Tokens and using System.Text.

The last thing to do in the Startup class is to modify the Configure() method and tell it to use authentication, as shown in the following code snippet.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // REST OF THE CODE HERE
    app.UseAuthentication();
    app.UseMvc();
}

Inject Settings into SecurityController

Besides using the settings from the JSON file in the startup file, you also need them in the SecurityManager class to build the bearer token. Because you create an instance of the SecurityManager in the SecurityController class, inject the JwtSettings class into the SecurityController class, as shown in Listing 9.

Listing 9: Inject settings and pass them to the SecurityManager object

public class SecurityController : Controller
{
    private JwtSettings _settings;
    public SecurityController(JwtSettings settings)
    {
        _settings = settings;
    }

    [HttpPost("login")]
    public IActionResult
    Login([FromBody]AppUser user)
    {
        IActionResult ret = null;
        AppUserAuth auth = new AppUserAuth();
        SecurityManager mgr = new SecurityManager(_settings);

        auth = (AppUserAuth)mgr.AuthenticateUser(user);
    }
    // REST OF THE CODE HERE
}

Create a private field named _settings in this class and set this field from the value injected in the constructor. Pass the _settings value to the SecurityManager constructor in the Login() method. You haven't modified the constructor of the SecurityManager to accept this yet, so VS Code will display an error, but you're going to add that code in the next section.

Accept Settings in SecurityManager

Open the SecurityManager.cs file and add a private field and a constructor just like you did in the SecurityController class. You're going to learn what you do with these settings in the next section of this article.

private JwtSettings _settings;
public SecurityManager(JwtSettings settings)
{
    _settings = settings;
}

Build a JSON Web Token

You're finally ready to build a bearer token you can add to the BearerToken property in the user security object. Add a new method to the SecurityManager class named BuildJwtToken(). The first thing this method does is create a new SymmetricSecurityKey object using the key you placed into the appsettings.json file. Next, add two claims that are needed when generating a JSON Web Token; Subject (Sub) and JSON Token ID (Jti). Into the Sub claim, place the user name, and into the Jti claim, you just need a unique identifier, so generate a GUID.

Next, add the custom claims you need for your application. In this simple example, create one claim for each property in the AppUserAuth class. Notice the use of the ToLower() method to convert the True and False values to lower case. JavaScript and TypeScript always use lower case for the values true and false.

A new JwtSecurityToken object is created and set with the properties from the JwtSettings class. You must use the same values from the JwtSettings class, just like you used in the Startup class. If you don't, the token generated here won't match when it's passed back by Angular and is attempted to be verified by the code you wrote in the Startup class.

The WriteToken() method of the JwtSecurityTokenHandler class is called to Base64 to encode the resulting string. It's this Base64 encoded string that's placed into the BearerToken property in your user security object that's passed to your Angular application. See Listing 10.

Listing 10: Build a JSON Web Token using the JSON settings and your claim data

protected string BuildJwtToken(AppUserAuth authUser)
{
    SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Key));
    List<Claim> jwtClaims = new List<Claim>();

    // Create standard JWT claims
    jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Sub, authUser.UserName));
    jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));

    // Add custom claims
    jwtClaims.Add(new Claim("isAuthenticated", authUser.IsAuthenticated.ToString().ToLower()));
    jwtClaims.Add(new Claim("canAccessProducts", authUser.CanAccessProducts.ToString().ToLower()));
    jwtClaims.Add(new Claim("canAddProduct", authUser.CanAddProduct.ToString().ToLower()));
    jwtClaims.Add(new Claim("canSaveProduct", authUser.CanSaveProduct.ToString().ToLower()));
    jwtClaims.Add(new Claim("canAccessCategories", authUser.CanAccessCategories.ToString().ToLower()));
    jwtClaims.Add(new Claim("canAddCategory", authUser.CanAddCategory.ToString().ToLower()));

    // Create the JwtSecurityToken object
    var token = new JwtSecurityToken(
        issuer: _settings.Issuer,
        audience: _settings.Audience,
        claims: jwtClaims,
        notBefore: DateTime.UtcNow,
        expires: DateTime.UtcNow.AddMinutes(_settings.MinutesToExpiration),
        signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
    );

    // Create string representation of Jwt token
    return new JwtSecurityTokenHandler().WriteToken(token);
}

After typing in all this code, you must add the following using statements at the top of the SecurityManager class.

using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;

Call the BuildJwtToken() Method

Locate the BuildUserAuthObject() method and add code to call this BuildJwtToken() method and assign the string result to the BearerToken property.

protected AppUserAuth
BuildUserAuthObject(AppUser authUser)
{
    AppUserAuth ret = new AppUserAuth();
    List<AppUserClaim> claims = new List<AppUserClaim>();

    // Set User Properties
    ret.UserName = authUser.UserName;
    ret.IsAuthenticated = true;
    ret.BearerToken = BuildJwtToken(ret);
    
    // Rest of the code here

Try It Out

If you haven't already done so, stop the PtcApi Web API project from running. Save your changes and run the PtcApi project again. If you're logged into the Angular application, click the Logout menu. Enter either “psheriff” or “bjones” and a password of “P@ssw0rd” and you should see a screen that looks like Figure 6.

Figure 6: A Base64 encoded string is returned in the bearerToken property
Figure 6: A Base64 encoded string is returned in the bearerToken property

If you wish to see what makes up the bearerToken property, you can decode the value at the www.jwt.io website. Copy the bearer token displayed on the log in page to the clipboard. Open a browser window and go to www.jwt.io. Scroll down on the page until you see a box labeled Encoded. Delete what's in there and paste in your bearer token. You should immediately see the payload data with all your data, as shown in Figure 7.

Figure 7: Decode your bearer token at <a href=
Figure 7: Decode your bearer token at

Try It Out

Click on the Products menu and you should now see a generic 401 Unauthorized message in the developer tools console window that looks like Figure 8. The reason for this error is that the server doesn't know that you're the same person that just logged in. You must pass back the bearer token on each Web API call to prove to the server that you have permission to call the API.

Figure 8: Unless you pass the bearer token back to the server, you'll get a 401 error from any secured Web API method.
Figure 8: Unless you pass the bearer token back to the server, you'll get a 401 error from any secured Web API method.

Add Headers to Product Service

To avoid receiving the status code 401, pass the bearer token each time you make a Web API call from your Angular code. You're going to learn how to automatically add the token to every call in the next section of this article, but first, modify the Get() method in the product service class. Open the product.service.ts file and modify the constructor to inject the SecurityService.

constructor(private http: HttpClient, private securityService: SecurityService) { }

Add code in the getProducts() method to create a new HttpHeaders object. Once you've instantiated this object, call the set() method and pass in Authorization as the header name. The data to pass in this header is the word Bearer followed by a space, then the bearer token itself. Add a second parameter to the HttpClient's get() method and pass an object with a property named headers and the HttpHeaders object you created as the value for that property.

getProducts(): Observable<Product[]> {
    let httpOptions = new HttpHeaders()
    .set('Authorization', 'Bearer ' + this.securityService.securityObject.bearerToken);
    return this.http.get<Product[]> (API_URL, { headers: httpOptions });
}

Try It Out

Save your changes, go to the browser and login as “psheriff”, and click on the Products menu. You should see the product data displayed. This is because the server has authenticated your token and knows who you are. Thus, you're granted access to the Get() method in the ProductController.

HTTP Interceptor

You're going to have many Web API calls in a typical Angular application. Instead of having to write the code you just wrote into every method call, Angular allows you to create an HTTP Interceptor class to place custom headers into each Web API call. Open a command prompt in the PTC project and enter the following command to create a module, and have it registered in the app.module.

ng g m security/httpInterceptor -m
app.module --flat

Open the newly created file, http-interceptor.module.ts, and replace all of the code with the code shown in Listing 11. All requests that are sent to a Web API method using the HttpClient are now routed through this interceptor class.

Listing 11: Add an HTTP Interceptor class to add a header to each Web API call

import { Injectable, NgModule } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';

@Injectable()
export class HttpRequestInterceptor
    implements HttpInterceptor {
        intercept(req: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
            var token = localStorage.getItem("bearerToken");

            if(token) {const newReq = req.clone( {
                headers: req.headers.set('Authorization','Bearer ' + token)
            });

            return next.handle(newReq);
            }
            else {
                return next.handle(req);
            }
       }
    };

@NgModule({
    providers: [
        { provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true }
    ]
})
export class HttpInterceptorModule { }

In the intercept() method, you retrieve the bearer token from local storage, and if the token exists, you clone the existing request that's being sent. You then create a new header on this request setting the headers property with the Authorization header followed by the word "Bearer " and the token after this word. This is the new Web request that's returned from this method. If the token doesn't exist in local storage, the original Web request is returned. You can read more about HTTP Interceptors at https://angular.io/guide/http#intercepting-requests-and-responses.

You now finally see the reason why you stored the bearer token in local storage in the login() method of the SecurityService class. You might be wondering why you don't just inject a SecurityService object into this class and retrieve it from the SecurityService object like you do in all the other components. The reason is that you can't have an HTTP interceptor injected with any class that uses HttpClient as this causes a recursion error.

Open the product.service.ts file and change the getProducts() method back to what you had originally. Remove the code that creates the HttpHeaders, and pass this as a second parameter to the get() method. The getProducts() method should now look like the following.

getProducts(): Observable<Product[]> { return this.http.get<Product[]>(API_URL); }

Try It Out

Save your changes, go to your browser, log in as “psheriff”, and try accessing the Products menu. You should still be getting data from the product controller. This verifies that the HTTP Interceptor class is working.

Add Security Policy

The [Authorize] attribute ensures that a user is authenticated. However, for some Web API methods, you may wish to restrict access based on the claims you created. This is accomplished by adding authorization to the services of the .NET Core Web API project.

Open the Startup.cs file and add the following code in the ConfigureServices method, just under the code you added earlier for adding authentication. For each property you have in your AppUserAuth object, you can add policy objects and specify the value the user must have.

services.AddAuthorization(cfg =>
{
    // The claim key and value are case-sensitive
    cfg.AddPolicy("CanAccessProducts", p =>
    p.RequireClaim("CanAccessProducts", "true"));
});

Once you create your claims, you may add the Policy property to the [Authorize] attribute to check for any of the claim names. For example, on the ProductController.Get() method, you can add the following code to the [Authorize] attribute to restrict usage on this method to only those users whose CanAccessProducts property is set to true.

[Authorize(Policy = "CanAccessProducts")]

Try It Out

Open SQL Server and open an Edit window for the UserClaim table. Modify the claim CanAccessProducts to a false value for the “PSheriff” user. Open the app-routing.module.ts file and remove the route guard for the products path. Your route for the products path should now look like the following code snippet.

{
    path: 'products', component: ProductListComponent
},

Save your changes, go back to the browser, and login as “psheriff”. Type http://localhost:4200/products directly into the address bar and you should now get a 401-Unauthorized error. Put the route guard back and reset the CanAccessProducts claim to a true value in the UserClaim table.

Summary

In this article, you built Web API calls to authenticate users and provide an authorization object back to Angular. In addition, you configured your .NET Core Web API project to use the JSON Web Token system to secure your Web API calls. You learned to send bearer tokens to Angular and have Angular send those tokens back using an HTTP Interceptor class. In the next article, you'll learn to use an array of claims to secure your Angular application. To accomplish this, you build a custom structural directive in Angular.