Skip to content

Instantly share code, notes, and snippets.

@Shazwazza Shazwazza/2fa-login.html Secret
Last active Jun 19, 2017

Embed
What would you like to do?
Umbraco 2FA integration demo
<div ng-controller="My2FA.LoginController">
<div id="twoFactorlogin" class="umb-modalcolumn umb-dialog" ng-cloak>
<div class="form">
<h1>2FA</h1>
<div>
<p>Enter 2FA Code</p>
<form method="POST" name="twoFactorSendForm" ng-submit="send(provider)" ng-if="step=='send'">
<div class="control-group">
<select ng-model="provider" ng-show="providers.length > 0">
<option ng-repeat="provider in providers" value="{{provider}}">{{provider}}</option>
</select>
</div>
<button type="submit" class="btn">Send</button>
</form>
<form method="POST" name="twoFactorCodeForm" ng-submit="validate(provider, code)" ng-if="step=='code'">
<div class="control-group">
<input type="text" name="code" class="input-xlarge" placeholder="2fa code" ng-model="code" />
</div>
<button type="submit" class="btn">Validate</button>
</form>
</div>
</div>
</div>
</div>
angular.module("umbraco").controller("My2FA.LoginController",
function ($scope, $cookies, localizationService, userService, externalLoginInfo, resetPasswordCodeInfo, $timeout, authResource, dialogService) {
$scope.code = "";
$scope.provider = "";
$scope.providers = [];
$scope.step = "send";
authResource.get2FAProviders()
.then(function (data) {
$scope.providers = data;
});
$scope.send = function (provider) {
$scope.provider = provider;
authResource.send2FACode(provider)
.then(function () {
$scope.step = "code";
});
};
$scope.validate = function (provider, code) {
$scope.code = code;
authResource.verify2FACode(provider, code)
.then(function (data) {
userService.setAuthenticationSuccessful(data);
$scope.submit(true);
});
};
});
/// <summary>
/// A custom Umbraco OWIN startup class to replace the Umbraco UserManager to enable 2FA
/// </summary>
public class CustomOwinStartup : UmbracoDefaultOwinStartup
{
/// <summary>
/// Completely override this method - do not call it's base implementation we'll wire up everything manually
/// </summary>
/// <param name="app"></param>
protected override void ConfigureServices(IAppBuilder app)
{
app.SetUmbracoLoggerFactory();
var applicationContext = ApplicationContext.Current;
//Here's where we assign a custom UserManager called MyBackOfficeUserManager
app.ConfigureUserManagerForUmbracoBackOffice<MyBackOfficeUserManager, BackOfficeIdentityUser>(
applicationContext,
(options, context) =>
{
var membershipProvider = Umbraco.Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider();
//Create the custom MyBackOfficeUserManager
var userManager = MyBackOfficeUserManager.Create(options,
applicationContext.Services.UserService,
applicationContext.Services.ExternalLoginService,
membershipProvider);
return userManager;
});
}
/// <summary>
/// Override this method to enable the 2FA cookie middleware with Umbraco's 2FA cookie auth type
/// </summary>
/// <param name="app"></param>
/// <remarks>
/// This is required in order for 2FA to work since the validated username from validating credentials is stored
/// in a temporary cookie which is then fetched once the 2FA validation is done.
/// </remarks>
protected override void ConfigureMiddleware(IAppBuilder app)
{
app.UseTwoFactorSignInCookie(global::Umbraco.Core.Constants.Security.BackOfficeTwoFactorAuthenticationType, TimeSpan.FromMinutes(5));
base.ConfigureMiddleware(app);
}
}
/// <summary>
/// Subclass the default BackOfficeUserManager and extend it to support 2FA
/// </summary>
public class MyBackOfficeUserStore : BackOfficeUserStore
{
public MyBackOfficeUserStore(IUserService userService, IExternalLoginService externalLoginService, MembershipProviderBase usersMembershipProvider)
: base(userService, externalLoginService, usersMembershipProvider)
{
}
/// <summary>
/// Override to support setting whether two factor authentication is enabled for the user
/// </summary>
/// <param name="user"/><param name="enabled"/>
/// <returns/>
/// <remarks>
/// This Demo does not persist any data, so this method doesn't really have any effect, if you wish
/// to have 2FA setup per user, you'll need to persist that somewhere and to do that you'd need to override
/// the IUserStore.UpdateAsync method by explicitly implementing that interface's method, calling the base
/// class logic and then updating your 2FA storage for the user. Similarly you'd have to do that for
/// IUserStore.DeleteAsync and IUserStore.CreateAsync.
///
/// This method is NOT designed to persist data! It's just meant to assign it, just like this
/// </remarks>
public override Task SetTwoFactorEnabledAsync(BackOfficeIdentityUser user, bool enabled)
{
user.TwoFactorEnabled = enabled;
return Task.FromResult(0);
}
/// <summary>
/// Returns whether two factor authentication is enabled for the user
/// </summary>
/// <param name="user"/>
/// <returns/>
/// <remarks>
/// This Demo does not persist any data, so this method for this Demo always returns true.
/// If you want to have 2FA configured per user, you will need to store that information somewhere.
/// See the notes above in the SetTwoFactorEnabledAsync method.
/// </remarks>
public override Task<bool> GetTwoFactorEnabledAsync(BackOfficeIdentityUser user)
{
return Task.FromResult(true);
//If you persisted this data somewhere then you could either look it up now, or you could
//explicitly implement all IUserStore "Find*" methods, call their base implementation and then lookup
//your persisted value and assign to the TwoFactorEnabled property of the resulting BackOfficeIdentityUser user.
//return Task.FromResult(user.TwoFactorEnabled);
}
}
/// <summary>
/// Subclass the default BackOfficeUserManager and extend it to support 2FA
/// </summary>
public class MyBackOfficeUserManager : BackOfficeUserManager, IUmbracoBackOfficeTwoFactorOptions
{
public MyBackOfficeUserManager(IUserStore<BackOfficeIdentityUser, int> store)
: base(store)
{
}
/// <summary>
/// Creates a BackOfficeUserManager instance with all default options and the default BackOfficeUserManager
/// </summary>
/// <param name="options"></param>
/// <param name="userService"></param>
/// <param name="externalLoginService"></param>
/// <param name="membershipProvider"></param>
/// <returns></returns>
public static MyBackOfficeUserManager Create(
IdentityFactoryOptions<MyBackOfficeUserManager> options,
IUserService userService,
IExternalLoginService externalLoginService,
MembershipProviderBase membershipProvider)
{
if (options == null) throw new ArgumentNullException("options");
if (userService == null) throw new ArgumentNullException("userService");
if (externalLoginService == null) throw new ArgumentNullException("externalLoginService");
var manager = new MyBackOfficeUserManager(new MyBackOfficeUserStore(userService, externalLoginService, membershipProvider));
manager.InitUserManager(manager, membershipProvider, options.DataProtectionProvider);
//Here you can specify the 2FA providers that you want to implement,
//in this demo we are using the custom AcceptAnyCodeProvider - which literally accepts any code - do not actually use this!
var dataProtectionProvider = options.DataProtectionProvider;
manager.RegisterTwoFactorProvider("test", new AcceptAnyCodeProvider(dataProtectionProvider.Create("Testing")));
return manager;
}
/// <summary>
/// Override to return true
/// </summary>
public override bool SupportsUserTwoFactor
{
get { return true; }
}
/// <summary>
/// Return the view for the 2FA screen
/// </summary>
/// <param name="owinContext"></param>
/// <param name="umbracoContext"></param>
/// <param name="username"></param>
/// <returns></returns>
public string GetTwoFactorView(IOwinContext owinContext, UmbracoContext umbracoContext, string username)
{
return "../App_Plugins/2FA/2fa-login.html";
}
}
/// <summary>
/// Silly IUserTokenProvider for this Demo to be used for the 2FA provider, this will generate a code but not send it anywhere
/// (which is what the base class does), and then we override the ValidateAsync method to validate any code given - do not actually use this!
/// </summary>
public class AcceptAnyCodeProvider : DataProtectorTokenProvider<BackOfficeIdentityUser, int>, IUserTokenProvider<BackOfficeIdentityUser, int>
{
public AcceptAnyCodeProvider(IDataProtector protector)
: base(protector)
{
}
/// <summary>
/// Explicitly implement this interface method - which overrides the base class's implementation
/// </summary>
/// <param name="purpose"></param>
/// <param name="token"></param>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
Task<bool> IUserTokenProvider<BackOfficeIdentityUser, int>.ValidateAsync(string purpose, string token, UserManager<BackOfficeIdentityUser, int> manager, BackOfficeIdentityUser user)
{
return Task.FromResult(true);
}
}
@biapar

This comment has been minimized.

Copy link

commented Jun 19, 2017

Very long code but simple to read...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.