:::: MENU ::::

Tuesday, August 22, 2017

When ASP.NET Core 2 shipped the early previews, I knew one large change was going to be the Identity subsystem. The Identity for ASP.NET Core 1 worked ok, but the setup was very confusing with identical configuration is more than one place.
I’m happy to say that in ASP.NET Core 2 it’s much better. Implementing JWT Tokens for APIs was more confusing than I liked back when I wrote my Implementing an API in ASP.NET Core course for Pluralsight. I was hoping that it changed to simplify the way it works.
Now that I’m re-writing my ASP.NET Core End-to-End course for Pluralsight, I wanted to be able to both Cookies and JWT without having to split the projects. While this should work in ASP.NET Core 1, I couldn’t figure it out.
With some help from the Issues tab in Github and some gentle nudges I got it to work. Here’s a short tutorial about how it works. Be clear, this is is for ASP.NET Core 2 only!

ASP.NET Core Identity

If you build the full Model-View-Controller and use Individual Accounts for your project, it will generate a whole Login/Logout/Register set of views and such:
This enables the default behavior of Cookies being supported. The only setup is already done for you. That is calling UseAuthentication():
?
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
public void Configure(IApplicationBuilder app, IHostingEnvironment env, DataSeeder seeder)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
    app.UseBrowserLink();
    app.UseDatabaseErrorPage();
  }
  else
  {
    app.UseExceptionHandler("/Home/Error");
  }
 
  app.UseStaticFiles();
 
  app.UseAuthentication();
 
  app.UseMvc(routes =>
  {
    routes.MapRoute(
              name: "default",
              template: "{controller=Home}/{action=Index}/{id?}");
  });
 
  seeder.SeedAsync().Wait();
}
But if we want to configure the cookies, we can use AddAuthentication to add options to the cookies like so:
?
1
2
services.AddAuthentication()
  .AddCookie(cfg => cfg.SlidingExpiration = true);
If we don’t use AddAuthentication, the default behavior of using Cookies will happen since that is such a common path (not sure how I feel about this since I like the opt-in model of ASP.NET Core in general, but nonetheless).
Once this is all setup, if we have an API, we can visit the API in the browser once we’re logged in and it just works:
This is pretty insecure. For users we want to have decent length cookies to make login easier, for APIs, hanging on the top of the cookies for authentication is nasty. So we’d like to use tokens to authenticate the APIs. Welcome to JSON Web Tokens.

Using JSON Web Tokens (JWTs)

Using JWTs is pretty simple. We first need to call AddJwtBearer and setup the configuration for how our tokens will work. I won’t repeat how this works since the documentation is pretty good on this.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
services.AddAuthentication()
  .AddJwtBearer(cfg =>
  {
    cfg.RequireHttpsMetadata = false;
    cfg.SaveToken = true;
     
    cfg.TokenValidationParameters = new TokenValidationParameters()
    {
      ValidIssuer = Configuration["Tokens:Issuer"],
      ValidAudience = Configuration["Tokens:Issuer"],
      IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
    };
 
  });
Note that all the configuration for the Identity middleware is now when we add the services, not in the Configure method any longer. This is the big change in the new version of Identity.
In order to issue JWTs, we need an anonymous method for issuing the JWT Token (again, I won’t belabor how this works, see the documentation):
?
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
[AllowAnonymous]
[HttpPost]
public async Task GenerateToken([FromBody] LoginViewModel model)
{
  if (ModelState.IsValid)
  {
    var user = await _userManager.FindByEmailAsync(model.Email);
 
    if (user != null)
    {
      var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
      if (result.Succeeded)
      {
 
        var claims = new[]
        {
          new Claim(JwtRegisteredClaimNames.Sub, user.Email),
          new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        };
 
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
 
        var token = new JwtSecurityToken(_config["Tokens:Issuer"],
          _config["Tokens:Issuer"],
          claims,
          expires: DateTime.Now.AddMinutes(30),
          signingCredentials: creds);
 
        return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
      }
    }
  }
 
  return BadRequest("Could not create token");
}
The trick here is to allow callers to send in a credential (in this case, username/pwd) and we can issue an expiring token that you would use.
For example, you can just use the token and set an Authorization header:
?
1
2
3
4
GET /api/customers HTTP/1.1
Host: localhost:8889
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2JAYW9sLmNvbSIsImp0aSI6ImZmNjhhMDA0LTlmNjYtNGIxNC05OTE4LWZmZWVhMDZiNTllYyIsImV4cCI6MTUwNTY5MTc1MiwiaXNzIjoiaHR0cDovL215Y29kZWNhbXAuaW8iLCJhdWQiOiJodHRwOi8vbXljb2RlY2FtcC5pbyJ9.CyD7nDAvlgt37C3wDcRG6DnlLOx3a7ih5UskL3xSUH0
This would work fine. But, what about cookies?

Dual Authorization

In order to add support for JWT, we replaced the AddCookie with AddJwtBearer. Having websites require the token in the header would be a headache, especially for projects that aren’t purely SPA or API. So what I really wanted was support for both Cookies and JWTs.
At first I didn’t get what should happen, but David Fowler came to my rescue.  he explained we could just chain the authorization types:
?
1
2
3
4
5
6
7
services.AddAuthentication()
  .AddCookie(cfg => cfg.SlidingExpiration = true)
  .AddJwtBearer(cfg =>
  {
    // REMOVED FOR BREVITY, SEE ABOVE FOR IMPL
 
  });
Cool, we now have cookies and bearer token. We should be good. So when I get a token and go to my API, it tries and redirect me to the login page. It’s because I’m not logged in. Seems to be that the cookies are taking precedence. Kinda.
When we use the Authorize attribute, it actually binds to the first authentication system by default. The trick is to change the attribute to specify which auth to use:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("/api/customers")]
public class ProtectedController : Controller
{
  public ProtectedController()
  {
 
  }
 
  public IActionResult Get()
  {
    return Ok(new[] { "One", "Two", "Three" });
  }
 
}
Note that we’re specifying which schemes to use. The Cookies and JwtBearer both have default scheme names so unless we’re renamed the scheme (which we could do in Startup.cs), we can just use the scheme name to tell the API to use JWT only, not cookies at all.
If we try again after this, it works with a JWT Token *only*. If you did want to support both (but don’t), the property AuthenticationSchemes takes a comma delimited list of scheme names.
You can get the code here if you want to play with my very naïve implementation (the validation key is the appconfig file and you really should make it much stronger, especially in production. You’ve been warned!) 

More

Tuesday, July 18, 2017

Having been playing around with the ASP.NET Core 2.0 preview for a little while now, one cool feature I stumbled upon was the addition of the new ITagHelperComponent interface and its use.

What problem does the ITagHelperComponent solve?

Pre .NET Core 2.0, if you're using a library that comes bundled with some static assets such as JavaScript or CSS, you'll know that in order to use the library you have to manually add script and/or link tags (including a reference to the files in your wwwroot folder), to your views. This is far from ideal as not only does it force users to jump through additional hoops, but it also runs the risk of introducing breaking changes when a user decides to remove the library and forgets to remove the JavaScript references, or if you update the library version but forget to change the appropriate JavaScript reference.

This is where the ITagHelperComponent comes in; it allows you to inject content into the header or footer of your application's web page. Essentially, it's dependency injection for your JavaScript or CSS assets! All that's required of the user is they register the dependency with their IoC Container of choice within their Startup.cs file.

Enough talk, let's take a look at how it works. Hopefully a demonstration will clear things up.

Injecting JavaScript or CSS assets into the head or body tags

Imagine we have some JavaScript we'd like to include on each page, this could be from either:

A JavaScript and/or CSS library we'd like to use (Bootstrap, Pure etc)
Some database driven JavaScript code or value that needs to be included in the head of your page
A JavaScript file that's bundled with a library that our users need to include before the closing
tag.In our case, we'll keep it simple - we need to include some database drive JavaScript in our page in the form of some Google Analytics JavaScript.

Creating our JavaScript tag helper component

Looking at the contract of the ITagHelperComponent interface you'll see it's a simple one:

public interface ITagHelperComponent{
    int Order { get; }
    void Init(TagHelperContext context);
    Task ProcessAsync(TagHelperContext context, TagHelperOutput output);
}
We could implement the interface ourselves, or we could lean on the existing TagHelperComponent base class and override only the properties and methods we require. We'll do the later.

Let's start by creating our implementation which we'll call CustomerAnalyticsTagHelper:

// CustomerAnalyticsTagHelper.cs

CustomerAnalyticsTagHelper : TagHelperComponent {}
For this example the only method we're concerned about is the ProcessAsync one, though we will touch on the Order property later.

Let's go ahead and implement it:

// CustomerAnalyticsTagHelper.cs

public class CustomerAnalyticsTagHelper : TagHelperComponent
{
    private readonly ICustomerAnalytics _analytics;

    public CustomerAnalyticsTagHelper(ICustomerAnalytics analytics)
    {
        _analytics = analytics;
    }

    public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        if (string.Equals(context.TagName, "body", StringComparison.Ordinal))
        {
            string analyticsSnippet = @"
            ";
           
            output.PostContent.AppendHtmlLine(analyticsSnippet);
        }

        return Task.CompletedTask;
    }
}
As you can see, the TagHelperContext argument gives us context around the tag we're inspecting, in this case we want to look for the body HTML element. If we wanted to drop JavaScript or CSS into the tags then we'd inspect tag name of "head" instead.

The TagHelperOutput argument gives us access to a host of properties around where we can place content, these include:

PreElement
PreContent
Content
PostContent
PostElement
IsContentModified
Attributes
In this instance we're going to append our JavaScript after the content located within the tag, placing it just before the closing tag.

Dependency Injection in our tag helper

With dependency injection being baked into the ASP.NET Core framework, we're able to inject dependencies into our tag helper - in this case I'm injecting our database driven consumer UA (User Analytics) code.

Registering our tag helper with our IoC container

Now all that's left to do is register our tag helper with our IoC container of choice. In this instance I'm using the build in ASP.NET Core one from the Microsoft.Extensions.DependencyInjection package.

// Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(); // Data source containing UA code
    services.AddSingleton(); // Our tag helper
    ...
}
Now firing up our tag helper we can see our JavaScript has now been injected in our HTML page without us needing to touch any of our .cshtml Razor files!

...

    ...
   


Ordering our output

If we needed to include more than one script or script file in our output, we can lean on the Order property we saw earlier, overriding this allows us to specify the order of our output. Let's see how we can do this:

// JsLoggingTagHelper.cs

public class JsLoggingTagHelper : TagHelperComponent
{
    public override int Order => 1;

    public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        if (string.Equals(context.TagName, "body", StringComparison.Ordinal))
        {
            const string script = @"";
            output.PostContent.AppendHtmlLine(script);
        }

        return Task.CompletedTask;
    }
}
// CustomerAnalyticsTagHelper.cs

public class CustomerAnalyticsTagHelper : TagHelperComponent
{
    ...
    public override int Order => 2; // Set our AnalyticsTagHelper to appear after our logger
    ...
}
// Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton();
    services.AddSingleton();
    services.AddSingleton();
    ...  
}
When we we launch our application we should see the following HTML output:





Conclusion

Hopefully this post has highlighted how powerful the recent changes to tag helpers are and how using the ITagHelperComponent interface allows us to inject content into our HTML without having to touch any files. This means as a library author we can ease integration for our users by simply asking them to register a type with their IoC container and we can take care of the rest!

More