1. Home
  2. Blog
  3. 2019
  4. Two-factor authentication for Umbraco - part 2

Two-factor authentication for Umbraco part 2, securing the admin area

If you missed it, start with Part 1 of this series covering the requirements and design.  Here in part 2, we share the gritty details on how we built the service to secure the admin area.

Data layer

In order to use Authy (and probably any external security provider) we need to extend the User data object with properties to store the extra data that comes from Authy. In this case the custom Umbraco table looks a little like this:

MFA data access layer

Here, AuthyId is the user's id returned from the provider after a successful registration, and we use AuthyStatus to keep track of whether a user is pending registration, already registered or locked out - this allows the rest of the code to know what views and processes the user should be experiencing when they try to login.

Pro-tip: you want to attach the database table creation to an application event handler so that it creates the table on application start-up if it doesn’t already exist. This will ensure that releases through different environments will lay the correct groundwork for the data model.

Custom back office user store

The next bit of groundwork is the UserStore, which casts the runes necessary for later magic ...

As you can see, the custom configuration of the userstore extends both the BackOfficeUserStore and the IUserPhoneNumberStore<>, so then you’ll also need to provide getters and setters for the following methods:

SetTwoFactorEnabledAsync(BackOfficeIdentityUser user, bool enabled)
GetTwoFactorEnabledAsync(BackOfficeIdentityUser user)
SetPhoneNumberAsync(BackOfficeIdentityUser user, string phoneNumber)
GetPhoneNumberAsync(BackOfficeIdentityUser user)
GetPhoneNumberConfirmedAsync(BackOfficeIdentityUser user)
SetPhoneNumberConfirmedAsync(BackOfficeIdentityUser user, bool confirmed)

Custom sign-in and user managers

Next-up are some custom managers that we’ll need to add.

The BackOfficeMfaUserManager is really the key here.  This instantiates the Authy provider, as well as enabling the two-factor auth and pointing Umbraco to where the views for this are. Your BackOfficeMfaUserManager (or whatever you decide to call it) should look something like this:

The GetTwoFactorView is opened by the Umbraco backoffice black box of magic, but handing it views that are declared in an App_Plugin folder works just fine. The AngularJs is pretty simple, just need an option to put in a code and an option to re-send codes. Likewise, the register device view just needs a number input in order to link their account with a mobile number.

If you’ve never tried to extend Umbraco using a custom section or property editor before then it may be a bit more challenging. You can read about the general structure of a custom property editor here, but really all you need for this part is a folder with html views in it that you said would be there, and some controller functionality to pass what the user is inputting back to the server (and then on to Authy). (If you’ve never written AngularJs before, bear in mind that Umbraco is still on Angular v1.x before you go learn).

The BackOfficeMfaSignInManager needs to instantiate a Create(IdentityFactoryOptions<BackOfficeMfaSignInManager> options, IOwinContext context, ILogger logger) function and a method to override the PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout) method.

Once you’ve hooked up the AngularJs code, then there’s really only one major bit left…

OWIN start-up or “glueing it all together”

A classic OwinStartup class is structured in the following way:

So, in ConfigureServices(IAppBuilder app) we need something like this:

And then we need to declare the cookie configuration in the ConfigureMiddleware(IAppBuilder app) section:

And that should be everything that you need in order for Umbraco users to require another layer of authentication when they login.

 

Let's work together

We’d love to hear from you. Make our day.
All ideas welcome. We’ll soon let you know if we’re able to help.

Contact us