Web API Asp.Net Core + JSON Web Token + Angular 6 Web App - Part 1

Today, I am going to build a Web API project using Asp.Net Core and implement authentication with JWT Web Token. Furthermore, I will build a Web Application using Angular 6 which will have a couple of pages and a restricted area.
I know that there are lots of posts about it and it is a pretty simple project. Actually, this post is dedicated to my co-workers, VB.Net Developers, who are looking to get out from their comfort zone digging into new technologies. In part 1 of this article, I will focus on backend project. In part 2, let's build the frontend project.

That said, let's get started.

Steps:
1 - Creating the Web API project
2 - Configuring JWT
3 - Installing and configuring Swagger
4 - Installing and using Dapper
5 - Creating classes to authenticate users from an existing database

1 - Creating the Web API project
From Visual Studio, select "New Project". Select the Visual C# | ASP.NET Core Web Application

In the next dialog, select the API project type. Uncheck the Configure for HTTPS checkbox(for instance, our project will not be deployed using SSL).Click OK.

With just a couple of mouse clicks, the API is ready!!! If we run the application, we will get something like this...

By default, ValuesController has added automatically to the project. Feel free to remove it if you want.

So far so good! Let's configure JWT Web Token.

2 - Configuring JWT
To configure JWT in WebAPI .NET Core, we just need to modify Startup.cs file, which is a class that runs when the application starts.

Option 1 - The following configuration is pretty simple. There is no Issuer and Audience validation. Everything that we need is to check the token's expiration date and the secret key. By specifying a key, the token can be validated without any need for the issuing server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySecretKey010203"))
        };
    });
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Line 3 - Registering JWT authentication middleware by calling AddAuthentication method
Line 12 - Bad practice - Do not hardcode your secret key. I would recommend keeping it in appsettings.json. Or, could just come from a certificate store instead of a file(best way!).

Option 2 - Validating Issuer, Audience and specifying the certificate location.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true, 
            ValidIssuer = "http://localhost", // could be a domain, server name, whatever...
            ValidateAudience = true,
            ValidAudiences = new List<string> // multiple audience
            {
                "http://localhost",
                "http://client11.mydomain.com",
                "http://client22.mydomain.com",
                "clientId_xxx",
                "clientId_yyz",
                "whatever"
            },
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new X509SecurityKey(new X509Certificate2("MySelfSignedCertificate.pfx", "password"))
 
        };
    });
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

In order to make our authentication middleware available, let's add the app.UseAuthentication() in the Configure method.

1
2
3
4
5
6
7
8
9
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseAuthentication();
    app.UseMvc();
}

It's done!!! With few steps, we get a Web API running with JSON Web Token authentication.

3 - Installing and configuring Swagger
Swagger is an open source project for generating useful documentation and help pages for Web API projects. Swashbuckle.AspNetCore is a package tools for documenting APIs built on ASP.NET Core. You can install it by executing the following command inside Package Manager Console.

PM> Install-Package Swashbuckle.AspNetCore

Now, let's add the Swagger generator in the Startup.ConfigureServices method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySecretKey010203"))
        };
    });
    services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1",
            new Info
            {
                Title = "Demo JSON Web Token",
                Version = "v1",
                Description = "Web API Asp.Net Core + JSON Web Token + Angular 6 Web App",
                Contact = new Contact
                {
                    Name = "Alexandre Miranda",
                    Url = "http://alexandreomiranda.com"
                }
            });
    });
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

In the Startup.Configure method, let's enable the middleware for serving the generated JSON document and the Swagger UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseAuthentication();
 
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "Web API V1");
    });
    app.UseMvc();
}

That's all. Run the application and access the URL: http://localhost:21018/swagger (maybe another port number...)

5 - Installing Dapper
Dapper is a micro ORM which helps to map the native query output to a domain class. To install it, execute the following command inside Package Manager Console.

PM> Install-Package Dapper

6 - Creating classes to authenticate users from an existing database
In order to make our project structure a little more organized, let's create the folders "Repository" and "Models". By way of example only, our project contains a simple structure and the Repository layer will be called directly from Controllers. A Service layer between both would be the best approach in a complex scenario.

Database

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE [dbo].[Users](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Username] [varchar](50) NOT NULL,
	[Password] [varchar](50) NOT NULL,
	[IsAdmin] [bit] NOT NULL,
	[Status] [int] NOT NULL,
 CONSTRAINT [PK_dbo.Users] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
 
INSERT INTO Users (Username,Password,IsAdmin,Status)
VALUES('admin','1234',1,1);
 
INSERT INTO Users (Username,Password,IsAdmin,Status)
VALUES('user','1234',0,1);

Add two new solution folders: "Models" and "Repositories". Under the Models folder, create a new class "User", which represents the Users table in the database

1
2
3
4
5
6
7
8
public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public bool IsAdmin { get; set; }
    public int Status { get; set; }
}

Under the Repositories folder, create a new class "UserRepository".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//add namespace
using Dapper;
 
public class UserRepository
{
    private IConfiguration _configuration;
    public UserRepository(IConfiguration configuration) => _configuration = configuration;
 
    public User Login(string username, string password)
    {
        using (SqlConnection conn = new SqlConnection(
            _configuration.GetConnectionString("WebApiJWT")))
        {
            var qry = "SELECT * FROM Users WHERE Username = @usr AND Password = @pwd AND Status = 1";
            return conn.QueryFirstOrDefault<User>(qry , new { usr = username, pwd = password });
        }
    }
}

Create a new controller - "AuthenticationController"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
    private UserRepository _userRepository;
    public AuthenticationController(UserRepository userRepository) => _userRepository = userRepository;
 
    [HttpPost("login")]
    [ProducesResponseType(200, Type = typeof(JwtSecurityTokenHandler))]
    [ProducesResponseType(400)]
    public IActionResult Login([FromBody] User userParam)
    {
        var user = _userRepository.Login(userParam.Username, userParam.Password);
 
        if (user == null)
            return BadRequest(new { message = "Username or password is incorrect" });
 
        var userRole = (user.IsAdmin) ? "admin" : "user";
 
        var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySecretKey010203"));
        var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
 
        var tokenOptions = new JwtSecurityToken(
            claims: new List<Claim> {
                new Claim(ClaimTypes.Name, user.Username),
                new Claim(ClaimTypes.Role, userRole)
            },
            expires: DateTime.Now.AddDays(2),
            signingCredentials: signinCredentials
        );
 
        var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions);
        return Ok(new { Token = tokenString });
 
    }
}

Lines 5 and 6: We are injecting UserRepository into AuthenticationController constructor. In this case, the framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.
So, let's register this dependency in the service container. Change Startup.ConfigureServices method adding the following line:

1
services.AddTransient<UserRepository, UserRepository>();

This is the final project structure:

Final Step: Running and testing authentication controller
Run the application and execute a test using Swagger.

Access the URL: http://localhost:21018/swagger and expand "/api/Authentication/login" method. Click on button "Try it out".

Change JSON text and click on "Execute"

Received token:

That's all for now. In the next part, I will build a frontend application and consume the generated token by this API.

Project available on my Github.
Source Code - Github

See you!!!

Cuide bem do seu domínio! Parte 4

Seguindo com a série de posts sobre TDD, vamos implementar a última regra de negócios do nosso projeto que valida a idade do aluno de acordo com a faixa etária para o curso no momento da matrícula. Darei sequência a partir do código fonte disponibilizado na parte 3 deste tutorial.

Passo 23 - Vamos começar criando um nova especificação para a entidade Matricula. Crie uma nova classe na pasta \Specifications\Matriculas\MatriculaFaixaEtariaPorCursoSpecification.cs Continue reading

Cuide bem do seu domínio! Parte 3

Seguindo com o projeto baseado no TDD, vamos continuar os testes restantes e finalizar a implementação das regras de negócio na camada de domínio.

Até aqui testamos apenas as especificações da entidade Aluno que eram: Ter idade entre 6 e 18 anos e CPF único na base.
Utilizamos Rhino Mock onde criamos um repositório falso simulando uma consulta de CPF existente e Specification Pattern com o pacote DomainValidation. Talvez não houvesse necessidade aplicar specification pattern para um projeto pequeno. A ideia é apenas mostrar como podemos implementá-lo para atender n regras de negócio de forma simples.

Continue reading

Cuide bem do seu domínio! Parte 2

Dando sequência ao post anterior vamos iniciar os testes de validação das nossas entidades com base nas especificações de Alunos e Matriculas.

Passo 13 - Abra o arquivo AlunoTests.cs no projeto de testes. Na barra de menu do Visual Studio clique em TEST -> RUN -> ALL TESTS. A imagem abaixo mostra o resultado na guia Test Explorer, retornando False na classe onde informamos dados inválidos(Aluno_Consistente_False), e retornando True para a classe onde passamos dados válidos(Aluno_Consistente_True).

Continue reading

Cuide bem do seu domínio! Parte 1

Irei abordar nesse post e nos próximos uma forma de iniciar o desenvolvimento de uma aplicação web ASP.NET MVC 5 com base na metodologia do TDD - Test Driven Development, Specification Pattern, DDD e SOLID. Iremos focar na criação das entidades e serviços do domínio aplicando os testes de unidade que irão validar as regras de negócio da aplicação. Portanto, sem CRUD e sem camada de apresentação.

Vamos ao exemplo:
Será criada uma aplicação para cadastro de cursos, alunos e matrículas. Considerar como regras de negócio os dados abaixo:

Continue reading

Entity Framework - DetectChanges

Conforme prometido no post anterior, vou resumir o funcionamento do método DetectChanges do EntityFramework. Para isso, vamos tomar como exemplo a classe de uma entidade Aluno conforme abaixo:

public class Aluno
{
    public int AlunoId { get; set; }
    public string Nome { get; set; }
    public int TurmaId { get; set; }
    public virtual Turma Turma { get; set; }    
}

Não há nenhum trecho no código acima que notifique e mantenha o controle de alteração dessa entidade.

Continue reading

EntityFramework vs NHibernate

UPDATE
Pessoal, fiz uma alteração no código, pois eu estava incrédulo com os números e resolvi estudar um pouco mais o funcionamento do EF. E sim, ele pode ser rápido!

Desabilitei o controle automático de alteração conforme sugerido no site da Microsoft - Entity Framework Automatic Detect Changes

O tempo que era de em média 40 segundo pra inserir 10.000 registros, passou a ser de 3 segundos aproximadamente. No contexto do meu teste, desabilitar esse controle automático é válido. Pode haver alguma implicação quando estivermos tratando de um ambiente mais complexo e principalmente quando estivermos lidando com operações de entidade relacional. Farei uma análise mais aguda sobre o comportamento do EF ao desabilitarmos o Auto Detect Changes. Assunto para um próximo post.

Continue reading

Customizando layout de login no ASP.NET Identity

O ASP.NET Identity é perfeito para gerenciamento de usuários e pode ser customizado facilmente. O MVP Eduardo Pires tem bastante material sobre o Identity que estão me ajudando bastante com os estudos em ASP.NET. Por sinal, tive a oportunidade de fazer o curso online de ASP.NET MVC 5 com ele e recomendo a todos. Ótima didática e conteúdo muito bom.

De fato, é muito simples implementar o gerenciamento de usuários. Por padrão o Identity será configurado se você não remover o recurso de autenticação durante a criação do projeto.

O meu intuito com esse post, é criar um projeto de autenticação customizando o comportamento da aplicação pensando numa aplicação corporativa. Para isso, irei fazer algumas customizações no template de login e também na área restrita contendo painel de admin, registro de usuários e alguns CRUD's básicos.

Continue reading