How to create a WebAPI authenticated by JWT?


JSON Web Token (JWT) is an open standard (RFC 7519) to exchange information securely via a signed token. For example, a server could issue a token with the claim “user identified as an administrator” and provide it to the client. The client could then check the token to prove that the user is logged in as an administrator.

Personally, it is a solution that I find elegant because

  • Simple to set up: a few configuration lines and the use of the [Authorize] attribute,
  • Easily manage my own security rules when checking the username and password,
  • Very easy to test in client applications: just add an HTTP header “Authorization: Bearer [Token]“.

General flow

The principle is to call a method that generates a JWT Token, containing an expiration date and some meta-data, and signed to avoid alterations. This token is then sent to all requests via the HTTP header.

Once you have generated a JWT token, you can easily validate it from http://jwt.io.

Contents of the JWT token

A JWT token consists of three parts: a header, a payload and a signature:

  • The header indicates which algorithm is used to generate the signature (e. g. HMAC-SHA256).
  • The Payload is content according to the application. It is indicated in the JWT specifications to include a time stamp in the creation: nbf (not before – date and time after of use) and exp (expiration date and time).
  • The signature is obtained via the algorithm specified in the header applied to the token and key. It ensures that the Token has not been modified since its creation.

Configuring WebAPIs

The first step is to create an ASP.NET Core project, via Visual Studio or VSCode. It will be able to host your WebAPIs.

  1. 1. First of all, it is necessary to add the Nuget packages IdentityModel.Tokens.Jwt and System.Runtime.Serialization.Json.

2. Next, adapt the Startup.cs class to configure the JWT service.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // Configure the JWT Authentication Service
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = "JwtBearer";
            options.DefaultChallengeScheme = "JwtBearer";
        })
        .AddJwtBearer("JwtBearer", jwtOptions =>
        {
            jwtOptions.TokenValidationParameters = new TokenValidationParameters()
            {
                // The SigningKey is defined in the TokenController class
                IssuerSigningKey = TokenController.SIGNING_KEY,
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateIssuerSigningKey = true,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromMinutes(5)
            };
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseAuthentication();  // Must be before app.UseMvc
        app.UseMvc();
    }
}

3. Create the TokenController.cs class to create the method to generate the token.

public class TokenController : Controller
{
    private const string SECRET_KEY = "TQvgjeABMPOwCycOqah5EQu5yyVjpmVG";
    public static readonly SymmetricSecurityKey SIGNING_KEY = new 
                  SymmetricSecurityKey(Encoding.UTF8.GetBytes(SECRET_KEY));

    [HttpGet]
    [Route("api/Token/{username}/{password}")]
    public IActionResult Get(string username, string password)
    {
        if (username == password)
            return new ObjectResult(GenerateToken(username));
        else
            return BadRequest();
    }

    // Generate a Token with expiration date and Claim meta-data.
    // And sign the token with the SIGNING_KEY
    private string GenerateToken(string username)
    {
        var token = new JwtSecurityToken(
            claims:    new Claim[] { new Claim(ClaimTypes.Name, username) },
            notBefore: new DateTimeOffset(DateTime.Now).DateTime,
            expires:   new DateTimeOffset(DateTime.Now.AddMinutes(60)).DateTime,
            signingCredentials: new SigningCredentials(SIGNING_KEY, 
                                                SecurityAlgorithms.HmacSha256)
            );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

4. The last step is to add [Autorize] attribute to reduce access to some methods.


[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    [Authorize]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

Verification of secured methods

When running your WebAPI project, you can no longer call the action “api/values”: you will receive an error like “401. Unauthorized“.
To be able to perform this action without error, you must first obtain a Token and use it in the request header.

To test your queries, you can use Postman or the cURL command

Example with cURL (see the video to use Postman). The flag “-i” include the HTTP response headers in the output.

> curl -i http://localhost/api/values

  HTTP/1.1 401 Unauthorized
  WWW-Authenticate: Bearer
  X-Powered-By: ASP.NET
  Content-Length: 0
> curl -i http://localhost/api/token/denis/mypassword

  HTTP/1.1 200 OK
  X-Powered-By: ASP.NET

  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2Fw
  Lm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGVuaXMiLCJuYmYiO
  jE1MTM3MDM5OTIsImV4cCI6MTUxMzcwNzU5Mn0.LepDs7Rm6bvcANUDHI2QaKW0A8ooN5
  iuALl7k9400HU
> curl -i http://localhost/api/values -H "Authorization: Bearer eyJhbGciOi...”

  HTTP/1.1 200 OK
  Content-Type: application/json; charset=utf-8
  X-Powered-By: ASP.NET

  ["value1","value2"]

Conclusion

JSON Web Token (JWT) is an open standard that requires only a few lines of configuration and makes it easy to secure WebAPIs developed in C#. This protocol being standardized, it is compatible with other platforms and languages: Java, Python, JavaScript, Go, Ruby, …

The token used during data exchanges is signed and can contain your own meta-data. It is also very easy to integrate WebAPIs into unit test projects and decrypt values if necessary.

In a future article, we will see how to consume these secure WebAPIs, in Angular 4 projects: creating a HttpInterceptor class that automatically adds the token to all web queries.

Advertisements
Posted in ASPNET, JWT

TargetInvocationException – How to break on the correct line


In many modern applications, you use Task or Threads to optimize your code. But when an exception occurred, you receive a generic exception like System.Reflection.TargetInvocationException: ‘Exception has been thrown by the target of an invocation.’

To avoid that in Visual Studio, go to Option / Debugging / General, and check Enable Just My Code. The result will be like that.

Posted in MVVM, Thread, Tips and others

[Tip] Windows 10 – Display Menu to the Left or to the Right of your hand.


If like me, your PC is configured to display Visual Studio menu to the left of my hand.
Of course, your are using Visual Studio with a mouse and not with your hand.
So, you want to change that. You can display menu to the right using this command, and select the correct choice in the “Other” tab.

   explorer shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}

On my PC, the Table PC Settings window is installed but not available via the windows search bar.

Posted in Tips and others, Visual Studio

Quick tip about SqlCmd.exe 13


Some days ago, I’ve installed Visual Studio 2017 and today, I’ve try to execute a simple SQL query with SQLCMD.EXE.

What’s the problem ? No really one… except that I’ve received an “ODBC Driver 13 for SQL Server” error and “Login timeout expired“.
After some hours to find why this error occurred (on a script already used before) and how to solve that… And I’ve found that it’s working with the previous version of SQLCMD but not with the new version. I don’t know why.

So, my workaround is to add a new item in the “PATH System Environment Variables” (follow this link to explain how to), and to move up this item before SQL Server 130 (my SQL Server 2016)… see screenshots below.

PS: you can check the version of SqlCmd.exe, using the /? argument.

Posted in SQL Server, Tips and others

Tip to optimize SQL Queries


Identification of the problem.

Some days ago, a customer call me to try to solve a performance problem into an existing application connected to SQL Server. Using SQL Server Profile, we found some queries with unexpected times. For example, to find one item in a large table, the SQL query use many seconds (between 4 and 10). Next, after multiple checks, we see that this C# code (using Parameters.AddWithValue) is executed like that by SQL Server and is responsible of this extra time in the application.

using (var cmd = new SqlDatabaseCommand(connection))
{
    cmd.CommandText.AppendLine(" SELECT TOP 1 MyString "):
    cmd.CommandText.AppendLine("   FROM MyTable ");
    cmd.CommandText.AppendLine("  WHERE MyString = @MyValue ");
    cmd.Parameters.AddWithValue("@MyValue", "abc");
    var data = cmd.ExecuteTable();
}
exec sp_executesql N' SELECT TOP 1 MyString 
                        FROM MyTable 
                       WHERE MyString = @MyValue 
',N'@MyValue nvarchar(3)',@MyValue=N'abc'

My first reaction is “that’s correct and I don’t see how to optimize this query.”… But the question “Why this query is so slow” is always there !

The solution.

Many searches and checks later, we found this command CONVERT in SQL Server Profiler.

sqlprofileconvert

Eureka… The problem come from the AddWithValue method where we set a C# String parameter value… So the Unicode value is converted later by SQL Server to @MyValue nvarchar(3)’,@MyValue=N‘abc’.

The query can be optimized by setting the correct SqlType (VarChar and not NVarChar) when we define the parameter (or you can change the database structure using NVarchar, NChar and NText… but your database size will be increase).

var param = new SqlParameter()
{
    ParameterName = "@MyValue",
    SqlDbType = SqlDbType.VarChar,
    Value = "def"
};
cmd.Parameters.Add(param);

And without other changes, my application (on this query) is 3 times more fast.

Try your self.

If you want to try your self.

1. First, create a new table in a sample database.

CREATE TABLE MyTable (
  ID INT,
  MyString VARCHAR(80)
)

2. Execute this script to generate 1 million of rows.

DECLARE @row INT;
DECLARE @string VARCHAR(80), @length INT, @code INT;
SET @row = 0;
WHILE @row < 1000000 BEGIN SET @row = @row + 1; -- Build the random string SET @length = ROUND(80*RAND(),0); SET @string = ''; WHILE @length > 0 BEGIN
      SET @length = @length - 1;
      SET @code = ROUND(32*RAND(),0) - 6;
      IF @code BETWEEN 1 AND 26 
         SET @string = @string + CHAR(ASCII('a')+@code-1);
      ELSE
         SET @string = @string + ' ';
   END 

   -- Ready for the record
   SET NOCOUNT ON;
   INSERT INTO MyTable VALUES (@row, @string)
END

3. Create a C# Console project and use this code to execute a query with NVarchar (Unicode) parameter and with Varchar parameter.

const string CONNECTION_STRING = "Server=(localdb)\\ProjectsV12;Database=SCOTT;Trusted_Connection=True;";
const decimal NB_REQUESTS = 100;

var watcher = Stopwatch.StartNew();
Console.WriteLine(" Starting first request... using NVarChar.");
for (int i = 0; i < NB_REQUESTS; i++)
{
    using (var cmd = new SqlDatabaseCommand(CONNECTION_STRING))
    {
        cmd.CommandText.AppendLine(" SELECT TOP 1 MyString FROM MyTable WHERE MyString = @MyValue ");
        cmd.Parameters.AddWithValue("@MyValue", "abc");
        var data = cmd.ExecuteTable();
    }
}
Console.WriteLine($"{watcher.ElapsedMilliseconds / NB_REQUESTS} ms by request.");

watcher.Restart();
Console.WriteLine(" Starting second request... using VarChar.");
for (int i = 0; i < NB_REQUESTS; i++)
{
    using (var cmd = new SqlDatabaseCommand(CONNECTION_STRING))
    {
        cmd.CommandText.AppendLine(" SELECT TOP 1 MyString FROM MyTable WHERE MyString = @MyValue ");
        var param = new SqlParameter()
        {
            ParameterName = "@MyValue",
            SqlDbType = SqlDbType.VarChar,
            Value = "def"
        };
        cmd.Parameters.Add(param);
        var data = cmd.ExecuteTable();
    }
}
Console.WriteLine($"{watcher.ElapsedMilliseconds / NB_REQUESTS} ms by request.");

And the result is… Amazing 😉

sqlprofileconvertresult

In a future version of SqlDatabaseCommand, I’ll add a global property to automatically convert NVarChar, NChar and NText to equivalent VarChar, Char and Text. Your queries will be optimized easily.

Posted in General, SQL Server, SqlDatabaseCommand, Tips and others

Développer avec un Simple Object Mapping Toolkit pour SQL Server


La majorité des applications actuelles ont besoin d’enregistrer des informations dans une base de données locale ou serveur. Plusieurs outils existent dont Entity Framework, le plus fréquemment proposés par Microsoft, ou ADO.NET, le plus performant mais le plus complexe à exploiter. Depuis plusieurs années, nous avons construit un ensemble d’outils simples afin de nous aider dans la fabrication de la DAL de nos projets, et plus particulièrement dans la recherche de données en les transformant facilement en objets .NET.

En septembre dernier, j’ai eu la chance de présenter ces toolkits dans le cadre des 24 Hours of PASS.

Le dernier toolkit présenté est SqlDatabaseCommand que j’ai déjà décrit dans un précédent article et que vous pouvez télécharger gratuitement via NuGet.

Posted in francais, SQL Server, SqlDatabaseCommand

Retour d’expérience ‘TFS Online dans une solution industrielle’


frenSome days ago, I had the chance to present a session in the Microsoft Experiences of Paris (in french) to explain how we’ve created a complete industrial application using VSTS (Visual Studio Team Services… or TFS Online).

msexperiences2016

In this presentation, I’ve explain how we’ve create a project for a aeronautic company:

  • The Workforce management web portal can plan, distribute, manage times, validate data and generate reports or send data to external systems.
  • We used the Agile Scrum methodology:
    • Definition of the Team (Product owner, Developers team,  Scrum master)
      Creation of this team in VSTS.
    • Creation of the Product Backlog using Features, User Stories and Tasks in VSTS.
    • Review and planning of Sprints.
    • Development with Visual Studio connected to VSTS.
    • Installation of Build and Deployment (Release Management) modules.
  • I’ve also presented some tools and frameworks used technically in this project:
    • To develop the BackEnd services
    • To develop the FrontEnd services
    • To tests all blocks

You can download the slides show on SlideShare.net.

Let me a comment if you need more details.

Posted in francais, General, Presentation, Team Foundation Server, Visual Studio
Follow me
   RSS Feed    Twitter    Linked In

Error: Twitter did not respond. Please wait a few minutes and refresh this page.

Write you email address to subscribe at this blog and to receive new posts by mail.

Join 648 other followers

Who I am?