:::: MENU ::::

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
ASP.NET Core Identity is a membership system that lets you add user accounts to your ASP.NET Core applications. It provides the low-level services for creating users, verifying passwords and signing users in to your application, as well as additional features such as two-factor authentication (2FA) and account lockout after too many failed attempts to login.
When users register on an application, they typically provide an email/username and a password. ASP.NET Core Identity lets you provide validation rules for the password, to try and prevent users from using passwords that are too simple.
In this post, I'll talk about the default password validation settings and how to customise them. Finally, I'll show how you can write your own password validator for ASP.NET Core Identity.

The default settings

By default, if you don't customise anything, Identity configures a default set of validation rules for new passwords:
  • Passwords must be at least 6 characters
  • Passwords must have at least one lowercase ('a'-'z')
  • Passwords must have at least one uppercase ('A'-'Z')
  • Passwords must have at least one digit ('0'-'9')
  • Passwords must have at least one non alphanumeric character
If you want to change these values, to increase the minimum length for example, you can do so when you add Identity to the DI container in ConfigureServices. In the following example I've increased the minimum password length from 6 to 10, and disabled the other validations:
Disclaimer: I'm not saying you should do this, it's just an example!
services.AddIdentity<ApplicationUser, IdentityRole>(options =>  
{
    options.Password.RequiredLength = 10;
    options.Password.RequireLowercase = false;
    options.Password.RequireUppercase = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireDigit = false;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

Coming in ASP.NET Core 2.0

In ASP.NET Core Identity 2.0, which uses ASP.NET Core 2.0 (available as 2.0.0-preview2 at time of writing) you get another configurable default setting:
  • Passwords must use at least n different characters
This lets you guard against the (stupidly popular) password "111111" for example. By default, this setting is disabled for compatibility reasons (you only need 1 unique character), but you can enable it in a similar way. The following example requires passwords of length 10, with at least 6 unique characters, one upper, one lower, one digit, and one special character.
services.AddIdentity<ApplicationUser, IdentityRole>(options =>  
{
    options.Password.RequiredLength = 10;
    options.Password.RequiredUniqueChars = 6;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

When the default validators aren't good enough..

Whether having all of these rules when creating a password is a good idea is up for debate, but it's certainly nice to have the options there. Unfortunately, sometimes these rules aren't enough to really protect users from themselves.
For example, it's quite common for a sub-set of users to use their username/email as their password. This is obviously a bad idea, but unfortunately the default password rules won't necessarily catch it! For example, in the following example I've used my username as my password:
and it meets all the rules: more than 6 characters, upper and lower, number, even a special character @!
And voilà, we're logged in...
Luckily, ASP.NET Core Identity lets you write your own password validators. Let's create a validator to catch this common no-no.

Writing a custom validator for ASP.NET Core Identity

You can create a custom validator for ASP.NET Core Identity by implementing the IPasswordValidator interface:
public interface IPasswordValidator<TUser> where TUser : class  
{
    Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password);
}
One thing to note about this interface is that the TUser type parameter is only limited to class - that means that if you create the most generic implementation of this interface, you won't be able to use properties of the user parameter.
That's fine if you're validating the password by looking at the password itself, checking the length and which character types are in it etc. Unfortunately, it's no good for the validator we're trying to create - we need access to the UserName property so we can check if the password matches.
We can get round this by implementing the validator and restricting the TUser type parameter to an IdentityUser. This is the default Identity user type created by the templates (which use EF Core under the hood), so it's still pretty generic, and it means we can now build our validator.
public class UsernameAsPasswordValidator<TUser> : IPasswordValidator<TUser>  
    where TUser : IdentityUser
{
    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
    {
        if (string.Equals(user.UserName, password, StringComparison.OrdinalIgnoreCase))
        {
            return Task.FromResult(IdentityResult.Failed(new IdentityError
            {
                Code = "UsernameAsPassword",
                Description = "You cannot use your username as your password"
            }));
        }
        return Task.FromResult(IdentityResult.Success);
    }
}
This validator checks if the UserName of the new TUser object passed in matches the password (ignoring case). If they match, then it rejects the password using the IdentityResult.Failed method, passing in an IdentityError (and wrapping in a Task<>).
The IdentityError class has both a Code and a Description - the Code property is used by the Identity system internally to localise the errors, and the Description is obviously an English description of the error which is used by default.
Note: Your errors won't be localised by default - I'll write a follow up post about this soon.
If the password and username are different, then the validator returns IdentityResult.Success, indicating it has no problems.
Note: The default templates use the email address for both the UserName and Email properties. If your user entities are configured differently, the username is separate from the email for example, you could check the password doesn't match either property by updating the ValidateAsync method accordingly.
Now we have a validator, we just need to make Identity aware of it. You do this with the AddPasswordValidator<> method exposed on IdentityBuilder when configuring your app:
public void ConfigureServices(IServiceCollection services)  
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders()
        .AddPasswordValidator<UsernameAsPasswordValidator<ApplicationUser>>();

    // EF Core, MVC service config etc
}
It looks a bit long-winded because we need to pass in the TUser generic parameter. If we're just building the validator for a single app, we could always remove the parameter altogether and simplify the signature somewhat:
public class UsernameAsPasswordValidator : IPasswordValidator<ApplicationUser>  
{
    public Task<IdentityResult> ValidateAsync(UserManager<ApplicationUser> manager, ApplicationUser user, string password)
    {
        // as before
    }
}
And then our Identity configuration becomes:
public void ConfigureServices(IServiceCollection services)  
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders()
        .AddPasswordValidator<UsernameAsPasswordValidator>();

    // EF Core, MVC service config etc
}
Now when you try and use your username as a password to register a new user you'll get a nice friendly warning to tell you to stop being stupid!

Summary

The default password validation in ASP.NET Core Identity includes a variety of password rules that you configure, such as password length, and required character types.
You can write your own password validators by implementing IPasswordValidator and calling .AddPasswordValidator when configuring Identity.
I have created a small NuGet package containing the validator from this blog post, a similar validator for validating the password does not equal the email, and one that looks for specific phrases (for example the URL or domain of your website - another popular choice for security-lacking users!).
You can find the package NetEscapades.AspNetCore.Identity.Validators on Nuget, with instructions on how to get started on GitHub. Hope you find it useful!