diff --git a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
index 7d4dd45fb8..982ba8c63e 100644
--- a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
@@ -11,6 +11,8 @@ namespace Umbraco.Cms.Core.Configuration.Models
[UmbracoOptions(Constants.Configuration.ConfigSecurity)]
public class SecuritySettings
{
+ internal const bool StaticMemberBypassTwoFactorForExternalLogins = true;
+ internal const bool StaticUserBypassTwoFactorForExternalLogins = true;
internal const bool StaticKeepUserLoggedIn = false;
internal const bool StaticHideDisabledUsersInBackOffice = false;
internal const bool StaticAllowPasswordReset = true;
@@ -66,5 +68,17 @@ namespace Umbraco.Cms.Core.Configuration.Models
/// Gets or sets a value for the member password settings.
///
public MemberPasswordConfigurationSettings MemberPassword { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to bypass the two factor requirement in Umbraco when using external login for members. Thereby rely on the External login and potential 2FA at that provider.
+ ///
+ [DefaultValue(StaticMemberBypassTwoFactorForExternalLogins)]
+ public bool MemberBypassTwoFactorForExternalLogins { get; set; } = StaticMemberBypassTwoFactorForExternalLogins;
+
+ ///
+ /// Gets or sets a value indicating whether to bypass the two factor requirement in Umbraco when using external login for users. Thereby rely on the External login and potential 2FA at that provider.
+ ///
+ [DefaultValue(StaticUserBypassTwoFactorForExternalLogins)]
+ public bool UserBypassTwoFactorForExternalLogins { get; set; } = StaticUserBypassTwoFactorForExternalLogins;
}
}
diff --git a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
index 4fba880e81..420d66b0b4 100644
--- a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
+++ b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
@@ -112,6 +112,9 @@ namespace Umbraco.Cms.Core.Security
// create the member
_memberService.Save(memberEntity);
+ //We need to add roles now that the member has an Id. It do not work implicit in UpdateMemberProperties
+ _memberService.AssignRoles(new[] { memberEntity.Id }, user.Roles.Select(x => x.RoleId).ToArray());
+
if (!memberEntity.HasIdentity)
{
throw new DataException("Could not create the member, check logs for details");
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
index aa5fc83e1e..e866409c17 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
@@ -31,6 +32,7 @@ using Umbraco.Cms.Web.Common.ActionsResults;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Cms.Web.Common.Controllers;
+using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Cms.Web.Common.Filters;
using Umbraco.Extensions;
using Constants = Umbraco.Cms.Core.Constants;
@@ -68,7 +70,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
private readonly IManifestParser _manifestParser;
private readonly ServerVariablesParser _serverVariables;
+ private readonly IOptions _securitySettings;
+
+ [ActivatorUtilitiesConstructor]
public BackOfficeController(
IBackOfficeUserManager userManager,
IRuntimeState runtimeState,
@@ -87,7 +92,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
IHttpContextAccessor httpContextAccessor,
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
IManifestParser manifestParser,
- ServerVariablesParser serverVariables)
+ ServerVariablesParser serverVariables,
+ IOptions securitySettings)
{
_userManager = userManager;
_runtimeState = runtimeState;
@@ -107,6 +113,51 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
_backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
_manifestParser = manifestParser;
_serverVariables = serverVariables;
+ _securitySettings = securitySettings;
+ }
+
+ [Obsolete("Use ctor with all params. This overload will be removed in Umbraco 10.")]
+ public BackOfficeController(
+ IBackOfficeUserManager userManager,
+ IRuntimeState runtimeState,
+ IRuntimeMinifier runtimeMinifier,
+ IOptions globalSettings,
+ IHostingEnvironment hostingEnvironment,
+ ILocalizedTextService textService,
+ IGridConfig gridConfig,
+ BackOfficeServerVariables backOfficeServerVariables,
+ AppCaches appCaches,
+ IBackOfficeSignInManager signInManager,
+ IBackOfficeSecurityAccessor backofficeSecurityAccessor,
+ ILogger logger,
+ IJsonSerializer jsonSerializer,
+ IBackOfficeExternalLoginProviders externalLogins,
+ IHttpContextAccessor httpContextAccessor,
+ IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
+ IManifestParser manifestParser,
+ ServerVariablesParser serverVariables)
+ : this(userManager,
+ runtimeState,
+ runtimeMinifier,
+ globalSettings,
+ hostingEnvironment,
+ textService,
+ gridConfig,
+ backOfficeServerVariables,
+ appCaches,
+ signInManager,
+ backofficeSecurityAccessor,
+ logger,
+ jsonSerializer,
+ externalLogins,
+ httpContextAccessor,
+ backOfficeTwoFactorOptions,
+ manifestParser,
+ serverVariables,
+ StaticServiceProvider.Instance.GetRequiredService>()
+ )
+ {
+
}
[HttpGet]
@@ -458,7 +509,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
if (response == null) throw new ArgumentNullException(nameof(response));
// Sign in the user with this external login provider (which auto links, etc...)
- SignInResult result = await _signInManager.ExternalLoginSignInAsync(loginInfo, isPersistent: false);
+ SignInResult result = await _signInManager.ExternalLoginSignInAsync(loginInfo, isPersistent: false, bypassTwoFactor: _securitySettings.Value.UserBypassTwoFactorForExternalLogins);
var errors = new List();
diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
index e9cc213598..1dc5bda7a9 100644
--- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
+++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs
@@ -11,6 +11,7 @@ using Umbraco.Cms.Core.Net;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Infrastructure.Security;
using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Cms.Web.Common.AspNetCore;
using Umbraco.Cms.Web.Common.Security;
@@ -77,5 +78,14 @@ namespace Umbraco.Extensions
return umbracoBuilder;
}
+ public static BackOfficeIdentityBuilder AddTwoFactorProvider(this BackOfficeIdentityBuilder identityBuilder, string providerName) where T : class, ITwoFactorProvider
+ {
+ identityBuilder.Services.AddSingleton();
+ identityBuilder.Services.AddSingleton();
+ identityBuilder.AddTokenProvider>(providerName);
+
+ return identityBuilder;
+ }
+
}
}
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 69f812b6e6..46ff558aee 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -17,6 +17,7 @@
+
diff --git a/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs b/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs
index c43754e170..cb9188f5d0 100644
--- a/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs
+++ b/src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs
@@ -8,7 +8,9 @@ using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Cache;
+using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Routing;
@@ -29,6 +31,7 @@ namespace Umbraco.Cms.Web.Website.Controllers
{
private readonly IMemberManager _memberManager;
private readonly ITwoFactorLoginService _twoFactorLoginService;
+ private readonly IOptions _securitySettings;
private readonly ILogger _logger;
private readonly IMemberSignInManagerExternalLogins _memberSignInManager;
@@ -42,7 +45,8 @@ namespace Umbraco.Cms.Web.Website.Controllers
IPublishedUrlProvider publishedUrlProvider,
IMemberSignInManagerExternalLogins memberSignInManager,
IMemberManager memberManager,
- ITwoFactorLoginService twoFactorLoginService)
+ ITwoFactorLoginService twoFactorLoginService,
+ IOptions securitySettings)
: base(
umbracoContextAccessor,
databaseFactory,
@@ -55,6 +59,7 @@ namespace Umbraco.Cms.Web.Website.Controllers
_memberSignInManager = memberSignInManager;
_memberManager = memberManager;
_twoFactorLoginService = twoFactorLoginService;
+ _securitySettings = securitySettings;
}
///
@@ -95,7 +100,7 @@ namespace Umbraco.Cms.Web.Website.Controllers
}
else
{
- SignInResult result = await _memberSignInManager.ExternalLoginSignInAsync(loginInfo, false);
+ SignInResult result = await _memberSignInManager.ExternalLoginSignInAsync(loginInfo, false, _securitySettings.Value.MemberBypassTwoFactorForExternalLogins);
if (result == SignInResult.Success)
{