Skip to content

Commit 3d28552

Browse files
authored
Add settings to bypass 2fa for external logins (#11959)
* Added settings for bypassing 2fa for external logins * Fixed issue with saving roles using member ID before the member had an ID. * Added missing extension method * Removed test classes from git * rollback csproj
1 parent 927ee44 commit 3d28552

File tree

6 files changed

+88
-4
lines changed

6 files changed

+88
-4
lines changed

src/Umbraco.Core/Configuration/Models/SecuritySettings.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace Umbraco.Cms.Core.Configuration.Models
1111
[UmbracoOptions(Constants.Configuration.ConfigSecurity)]
1212
public class SecuritySettings
1313
{
14+
internal const bool StaticMemberBypassTwoFactorForExternalLogins = true;
15+
internal const bool StaticUserBypassTwoFactorForExternalLogins = true;
1416
internal const bool StaticKeepUserLoggedIn = false;
1517
internal const bool StaticHideDisabledUsersInBackOffice = false;
1618
internal const bool StaticAllowPasswordReset = true;
@@ -66,5 +68,17 @@ public class SecuritySettings
6668
/// Gets or sets a value for the member password settings.
6769
/// </summary>
6870
public MemberPasswordConfigurationSettings MemberPassword { get; set; }
71+
72+
/// <summary>
73+
/// 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.
74+
/// </summary>
75+
[DefaultValue(StaticMemberBypassTwoFactorForExternalLogins)]
76+
public bool MemberBypassTwoFactorForExternalLogins { get; set; } = StaticMemberBypassTwoFactorForExternalLogins;
77+
78+
/// <summary>
79+
/// 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.
80+
/// </summary>
81+
[DefaultValue(StaticUserBypassTwoFactorForExternalLogins)]
82+
public bool UserBypassTwoFactorForExternalLogins { get; set; } = StaticUserBypassTwoFactorForExternalLogins;
6983
}
7084
}

src/Umbraco.Infrastructure/Security/MemberUserStore.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ public override Task<IdentityResult> CreateAsync(MemberIdentityUser user, Cancel
112112
// create the member
113113
_memberService.Save(memberEntity);
114114

115+
//We need to add roles now that the member has an Id. It do not work implicit in UpdateMemberProperties
116+
_memberService.AssignRoles(new[] { memberEntity.Id }, user.Roles.Select(x => x.RoleId).ToArray());
117+
115118
if (!memberEntity.HasIdentity)
116119
{
117120
throw new DataException("Could not create the member, check logs for details");

src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Microsoft.AspNetCore.Http;
1111
using Microsoft.AspNetCore.Identity;
1212
using Microsoft.AspNetCore.Mvc;
13+
using Microsoft.Extensions.DependencyInjection;
1314
using Microsoft.Extensions.Logging;
1415
using Microsoft.Extensions.Options;
1516
using Umbraco.Cms.Core;
@@ -31,6 +32,7 @@
3132
using Umbraco.Cms.Web.Common.Attributes;
3233
using Umbraco.Cms.Web.Common.Authorization;
3334
using Umbraco.Cms.Web.Common.Controllers;
35+
using Umbraco.Cms.Web.Common.DependencyInjection;
3436
using Umbraco.Cms.Web.Common.Filters;
3537
using Umbraco.Extensions;
3638
using Constants = Umbraco.Cms.Core.Constants;
@@ -68,7 +70,10 @@ public class BackOfficeController : UmbracoController
6870
private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions;
6971
private readonly IManifestParser _manifestParser;
7072
private readonly ServerVariablesParser _serverVariables;
73+
private readonly IOptions<SecuritySettings> _securitySettings;
7174

75+
76+
[ActivatorUtilitiesConstructor]
7277
public BackOfficeController(
7378
IBackOfficeUserManager userManager,
7479
IRuntimeState runtimeState,
@@ -87,7 +92,8 @@ public BackOfficeController(
8792
IHttpContextAccessor httpContextAccessor,
8893
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
8994
IManifestParser manifestParser,
90-
ServerVariablesParser serverVariables)
95+
ServerVariablesParser serverVariables,
96+
IOptions<SecuritySettings> securitySettings)
9197
{
9298
_userManager = userManager;
9399
_runtimeState = runtimeState;
@@ -107,6 +113,51 @@ public BackOfficeController(
107113
_backOfficeTwoFactorOptions = backOfficeTwoFactorOptions;
108114
_manifestParser = manifestParser;
109115
_serverVariables = serverVariables;
116+
_securitySettings = securitySettings;
117+
}
118+
119+
[Obsolete("Use ctor with all params. This overload will be removed in Umbraco 10.")]
120+
public BackOfficeController(
121+
IBackOfficeUserManager userManager,
122+
IRuntimeState runtimeState,
123+
IRuntimeMinifier runtimeMinifier,
124+
IOptions<GlobalSettings> globalSettings,
125+
IHostingEnvironment hostingEnvironment,
126+
ILocalizedTextService textService,
127+
IGridConfig gridConfig,
128+
BackOfficeServerVariables backOfficeServerVariables,
129+
AppCaches appCaches,
130+
IBackOfficeSignInManager signInManager,
131+
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
132+
ILogger<BackOfficeController> logger,
133+
IJsonSerializer jsonSerializer,
134+
IBackOfficeExternalLoginProviders externalLogins,
135+
IHttpContextAccessor httpContextAccessor,
136+
IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions,
137+
IManifestParser manifestParser,
138+
ServerVariablesParser serverVariables)
139+
: this(userManager,
140+
runtimeState,
141+
runtimeMinifier,
142+
globalSettings,
143+
hostingEnvironment,
144+
textService,
145+
gridConfig,
146+
backOfficeServerVariables,
147+
appCaches,
148+
signInManager,
149+
backofficeSecurityAccessor,
150+
logger,
151+
jsonSerializer,
152+
externalLogins,
153+
httpContextAccessor,
154+
backOfficeTwoFactorOptions,
155+
manifestParser,
156+
serverVariables,
157+
StaticServiceProvider.Instance.GetRequiredService<IOptions<SecuritySettings>>()
158+
)
159+
{
160+
110161
}
111162

112163
[HttpGet]
@@ -458,7 +509,7 @@ private async Task<IActionResult> ExternalSignInAsync(ExternalLoginInfo loginInf
458509
if (response == null) throw new ArgumentNullException(nameof(response));
459510

460511
// Sign in the user with this external login provider (which auto links, etc...)
461-
SignInResult result = await _signInManager.ExternalLoginSignInAsync(loginInfo, isPersistent: false);
512+
SignInResult result = await _signInManager.ExternalLoginSignInAsync(loginInfo, isPersistent: false, bypassTwoFactor: _securitySettings.Value.UserBypassTwoFactorForExternalLogins);
462513

463514
var errors = new List<string>();
464515

src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Umbraco.Cms.Core.Scoping;
1212
using Umbraco.Cms.Core.Security;
1313
using Umbraco.Cms.Core.Services;
14+
using Umbraco.Cms.Infrastructure.Security;
1415
using Umbraco.Cms.Web.BackOffice.Security;
1516
using Umbraco.Cms.Web.Common.AspNetCore;
1617
using Umbraco.Cms.Web.Common.Security;
@@ -77,5 +78,14 @@ public static IUmbracoBuilder AddBackOfficeExternalLogins(this IUmbracoBuilder u
7778
return umbracoBuilder;
7879
}
7980

81+
public static BackOfficeIdentityBuilder AddTwoFactorProvider<T>(this BackOfficeIdentityBuilder identityBuilder, string providerName) where T : class, ITwoFactorProvider
82+
{
83+
identityBuilder.Services.AddSingleton<ITwoFactorProvider, T>();
84+
identityBuilder.Services.AddSingleton<T>();
85+
identityBuilder.AddTokenProvider<TwoFactorBackOfficeValidationProvider<T>>(providerName);
86+
87+
return identityBuilder;
88+
}
89+
8090
}
8191
}

src/Umbraco.Web.UI/Umbraco.Web.UI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<ProjectReference Include="../Umbraco.Web.Website/Umbraco.Web.Website.csproj" />
1818
<ProjectReference Include="../Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj" Condition="'$(OS)' == 'Windows_NT'" />
1919
</ItemGroup>
20+
2021

2122
<ItemGroup>
2223
<Folder Include="App_Plugins" />

src/Umbraco.Web.Website/Controllers/UmbExternalLoginController.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
using Microsoft.AspNetCore.Identity;
99
using Microsoft.AspNetCore.Mvc;
1010
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Options;
1112
using Umbraco.Cms.Core.Cache;
13+
using Umbraco.Cms.Core.Configuration.Models;
1214
using Umbraco.Cms.Core.Logging;
1315
using Umbraco.Cms.Core.Models;
1416
using Umbraco.Cms.Core.Routing;
@@ -29,6 +31,7 @@ public class UmbExternalLoginController : SurfaceController
2931
{
3032
private readonly IMemberManager _memberManager;
3133
private readonly ITwoFactorLoginService _twoFactorLoginService;
34+
private readonly IOptions<SecuritySettings> _securitySettings;
3235
private readonly ILogger<UmbExternalLoginController> _logger;
3336
private readonly IMemberSignInManagerExternalLogins _memberSignInManager;
3437

@@ -42,7 +45,8 @@ public UmbExternalLoginController(
4245
IPublishedUrlProvider publishedUrlProvider,
4346
IMemberSignInManagerExternalLogins memberSignInManager,
4447
IMemberManager memberManager,
45-
ITwoFactorLoginService twoFactorLoginService)
48+
ITwoFactorLoginService twoFactorLoginService,
49+
IOptions<SecuritySettings> securitySettings)
4650
: base(
4751
umbracoContextAccessor,
4852
databaseFactory,
@@ -55,6 +59,7 @@ public UmbExternalLoginController(
5559
_memberSignInManager = memberSignInManager;
5660
_memberManager = memberManager;
5761
_twoFactorLoginService = twoFactorLoginService;
62+
_securitySettings = securitySettings;
5863
}
5964

6065
/// <summary>
@@ -95,7 +100,7 @@ public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
95100
}
96101
else
97102
{
98-
SignInResult result = await _memberSignInManager.ExternalLoginSignInAsync(loginInfo, false);
103+
SignInResult result = await _memberSignInManager.ExternalLoginSignInAsync(loginInfo, false, _securitySettings.Value.MemberBypassTwoFactorForExternalLogins);
99104

100105
if (result == SignInResult.Success)
101106
{

0 commit comments

Comments
 (0)