Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure Token provider from the corresponding provider #15627

Merged
merged 33 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
20a097a
Configure Token provider from the corresponding provider
MikeAlhayek Mar 29, 2024
a0e10f8
rename
MikeAlhayek Mar 29, 2024
574acbd
Update Startup.cs
MikeAlhayek Apr 7, 2024
1876438
Update Startup.cs
MikeAlhayek Apr 7, 2024
067d7fa
Merge branch 'main' into ma/configure-token-providers
hishamco Apr 7, 2024
d04b1a6
Add a way to configure the email-confirmation, password-reset, change…
MikeAlhayek Apr 7, 2024
b44ec21
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 7, 2024
73a9631
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 8, 2024
462def1
Use 15 mins for email change
MikeAlhayek Apr 8, 2024
70b53cf
Fix the TwoFactor provider
MikeAlhayek Apr 8, 2024
d1b3242
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 8, 2024
aecad0f
update comments
MikeAlhayek Apr 8, 2024
2b09441
Rename
MikeAlhayek Apr 8, 2024
fbcb3e8
Update 1.9.0.md
MikeAlhayek Apr 9, 2024
8c7b73c
Update 1.9.0.md
MikeAlhayek Apr 9, 2024
16a4cae
Update TwoFactorEmailTokenProvider.cs
MikeAlhayek Apr 9, 2024
eadcc90
Update TwoFactorEmailTokenProvider.cs
MikeAlhayek Apr 9, 2024
a2782e1
Update TwoFactorEmailTokenProvider.cs
MikeAlhayek Apr 9, 2024
119befd
Fix build
MikeAlhayek Apr 9, 2024
9941023
Use 3 mins by default for TOTP
MikeAlhayek Apr 9, 2024
9f66cda
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 9, 2024
0c76454
Fix build
MikeAlhayek Apr 9, 2024
6ffca4a
Use IClock
MikeAlhayek Apr 9, 2024
812d56d
cleanup
MikeAlhayek Apr 9, 2024
e284ca0
Merge branch 'main' into ma/configure-token-providers
hishamco Apr 9, 2024
f5f20f5
Add a test
MikeAlhayek Apr 9, 2024
cfd9c53
cleanup
MikeAlhayek Apr 9, 2024
77a7b0b
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 9, 2024
45b8c30
Use the default RFC6238 implementation
MikeAlhayek Apr 10, 2024
ffcda7e
Fix services. The issue is generating numeric tokens
MikeAlhayek Apr 10, 2024
1a8a0b9
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 11, 2024
eebfa80
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 12, 2024
5e51c0e
Merge branch 'main' into ma/configure-token-providers
MikeAlhayek Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/OrchardCore.Modules/OrchardCore.Users/AdminMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public Task BuildNavigationAsync(string name, NavigationBuilder builder)
}
}

[Feature("OrchardCore.Users.Registration")]
[Feature(UserConstants.Features.UserRegistration)]
public class RegistrationAdminMenu : INavigationProvider
{
private static readonly RouteValueDictionary _routeValues = new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace OrchardCore.Users.AuditTrail.Registration
{
[RequireFeatures("OrchardCore.Users.AuditTrail", "OrchardCore.Users.Registration")]
[RequireFeatures("OrchardCore.Users.AuditTrail", UserConstants.Features.UserRegistration)]
public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using OrchardCore.DisplayManagement;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.Environment.Shell;
using OrchardCore.Modules;
using OrchardCore.Mvc.Core.Utilities;
using OrchardCore.Settings;
Expand All @@ -39,6 +40,7 @@ public class AccountController : AccountBaseController
private readonly ISiteService _siteService;
private readonly IEnumerable<ILoginFormEvent> _accountEvents;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly IShellFeaturesManager _shellFeaturesManager;
private readonly IDisplayManager<LoginForm> _loginFormDisplayManager;
private readonly IUpdateModelAccessor _updateModelAccessor;
private readonly INotifier _notifier;
Expand All @@ -62,6 +64,7 @@ public AccountController(
IClock clock,
IDistributedCache distributedCache,
IDataProtectionProvider dataProtectionProvider,
IShellFeaturesManager shellFeaturesManager,
IDisplayManager<LoginForm> loginFormDisplayManager,
IUpdateModelAccessor updateModelAccessor,
IEnumerable<IExternalLoginEventHandler> externalLoginHandlers)
Expand All @@ -76,6 +79,7 @@ public AccountController(
_clock = clock;
_distributedCache = distributedCache;
_dataProtectionProvider = dataProtectionProvider;
_shellFeaturesManager = shellFeaturesManager;
_loginFormDisplayManager = loginFormDisplayManager;
_updateModelAccessor = updateModelAccessor;
_externalLoginHandlers = externalLoginHandlers;
Expand Down Expand Up @@ -859,6 +863,14 @@ private bool AddUserEnabledError(IUser user)

private async Task<bool> AddConfirmEmailErrorAsync(IUser user)
{
var registrationFeatureIsAvailable = (await _shellFeaturesManager.GetAvailableFeaturesAsync())
.Any(feature => feature.Id == UserConstants.Features.UserRegistration);

if (!registrationFeatureIsAvailable)
{
return false;
}

var registrationSettings = (await _siteService.GetSiteSettingsAsync()).As<RegistrationSettings>();
if (registrationSettings.UsersMustValidateEmail)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class AuthenticatorAppController : TwoFactorAuthenticationBaseController
{
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&digits={3}&issuer={0}";

private readonly IdentityOptions _identityOptions;
private readonly UrlEncoder _urlEncoder;
private readonly ShellSettings _shellSettings;

Expand All @@ -36,6 +37,7 @@ public AuthenticatorAppController(
IHtmlLocalizer<AccountController> htmlLocalizer,
IStringLocalizer<AccountController> stringLocalizer,
IOptions<TwoFactorOptions> twoFactorOptions,
IOptions<IdentityOptions> identityOptions,
INotifier notifier,
IDistributedCache distributedCache,
UrlEncoder urlEncoder,
Expand All @@ -52,6 +54,7 @@ public AuthenticatorAppController(
stringLocalizer,
twoFactorOptions)
{
_identityOptions = identityOptions.Value;
_urlEncoder = urlEncoder;
_shellSettings = shellSettings;
}
Expand Down Expand Up @@ -88,7 +91,7 @@ public async Task<IActionResult> Index(EnableAuthenticatorViewModel model)
return View(model);
}

var isValid = await UserManager.VerifyTwoFactorTokenAsync(user, TokenOptions.DefaultAuthenticatorProvider, StripToken(model.Code));
var isValid = await UserManager.VerifyTwoFactorTokenAsync(user, _identityOptions.Tokens.AuthenticatorTokenProvider, StripToken(model.Code));

if (!isValid)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace OrchardCore.Users.Controllers
{
[Feature("OrchardCore.Users.Registration")]
[Feature(UserConstants.Features.UserRegistration)]
public class RegistrationController : Controller
{
private readonly UserManager<IUser> _userManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace OrchardCore.Users.Controllers;
[Authorize, Feature(UserConstants.Features.SmsAuthenticator)]
public class SmsAuthenticatorController : TwoFactorAuthenticationBaseController
{
private readonly IdentityOptions _identityOptions;
private readonly IUserService _userService;
private readonly ISmsService _smsService;
private readonly ILiquidTemplateManager _liquidTemplateManager;
Expand All @@ -41,6 +42,7 @@ public SmsAuthenticatorController(
IHtmlLocalizer<AccountController> htmlLocalizer,
IStringLocalizer<AccountController> stringLocalizer,
IOptions<TwoFactorOptions> twoFactorOptions,
IOptions<IdentityOptions> identityOptions,
INotifier notifier,
IDistributedCache distributedCache,
IUserService userService,
Expand All @@ -60,6 +62,7 @@ public SmsAuthenticatorController(
stringLocalizer,
twoFactorOptions)
{
_identityOptions = identityOptions.Value;
_userService = userService;
_smsService = smsService;
_liquidTemplateManager = liquidTemplateManager;
Expand Down Expand Up @@ -220,7 +223,7 @@ public async Task<IActionResult> SendCode()
}

var settings = (await SiteService.GetSiteSettingsAsync()).As<SmsAuthenticatorLoginSettings>();
var code = await UserManager.GenerateTwoFactorTokenAsync(user, TokenOptions.DefaultPhoneProvider);
var code = await UserManager.GenerateTwoFactorTokenAsync(user, _identityOptions.Tokens.ChangePhoneNumberTokenProvider);

var message = new SmsMessage()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace OrchardCore.Users.Drivers
{
[Feature("OrchardCore.Users.Registration")]
[Feature(UserConstants.Features.UserRegistration)]
public class RegistrationSettingsDisplayDriver : SectionDisplayDriver<ISite, RegistrationSettings>
{
public const string GroupId = "userRegistration";
Expand Down
31 changes: 26 additions & 5 deletions src/OrchardCore.Modules/OrchardCore.Users/Manifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,41 @@
Id = UserConstants.Features.Users,
Name = "Users",
Description = "The users module enables authentication UI and user management.",
Dependencies = ["OrchardCore.Roles.Core"],
Dependencies =
[
"OrchardCore.Roles.Core"
],
Category = "Security"
)]

[assembly: Feature(
Id = UserConstants.Features.UserEmailConfirmation,
Name = "Users Email Confirmation",
Description = "Provides services to handler user email confirmation.",
Category = "Security",
EnabledByDependencyOnly = true
)]

[assembly: Feature(
Id = "OrchardCore.Users.ChangeEmail",
Name = "Users Change Email",
Description = "The Change email feature allows users to change their email address.",
Dependencies = [UserConstants.Features.Users],
Dependencies =
[
UserConstants.Features.Users,
UserConstants.Features.UserEmailConfirmation,
],
Category = "Security"
)]

[assembly: Feature(
Id = "OrchardCore.Users.Registration",
Id = UserConstants.Features.UserRegistration,
Name = "Users Registration",
Description = "The registration feature allows external users to sign up to the site and ask to confirm their email.",
Dependencies =
[
UserConstants.Features.Users,
UserConstants.Features.UserEmailConfirmation,
"OrchardCore.Email",
],
Category = "Security"
Expand Down Expand Up @@ -60,7 +76,11 @@
Id = "OrchardCore.Users.Localization",
Name = "User Localization",
Description = "Provides a way to set the culture per user.",
Dependencies = new[] { "OrchardCore.Users", "OrchardCore.Localization" },
Dependencies =
[
"OrchardCore.Users",
"OrchardCore.Localization"
],
Category = "Settings",
Priority = "-1" // Added to avoid changing the order in the localization module.
)]
Expand Down Expand Up @@ -100,7 +120,7 @@
[assembly: Feature(
Id = UserConstants.Features.TwoFactorAuthentication,
Name = "Two-Factor Authentication Services",
Description = "Provices Two-factor core services.",
Description = "Provides Two-factor core services.",
Dependencies = [UserConstants.Features.Users],
EnabledByDependencyOnly = true,
Category = "Security"
Expand All @@ -126,6 +146,7 @@
[
UserConstants.Features.Users,
UserConstants.Features.TwoFactorAuthentication,
UserConstants.Features.UserEmailConfirmation,
"OrchardCore.Liquid",
"OrchardCore.Email",
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OrchardCore.Users.Models;

namespace OrchardCore.Users.Services;

public sealed class AuthenticatorAppProviderTwoFactorOptionsConfiguration : IConfigureOptions<TwoFactorOptions>
{
private readonly IdentityOptions _identityOptions;

public AuthenticatorAppProviderTwoFactorOptionsConfiguration(IOptions<IdentityOptions> identityOptions)
{
_identityOptions = identityOptions.Value;
}

public void Configure(TwoFactorOptions options)
{
if (string.IsNullOrEmpty(_identityOptions.Tokens.AuthenticatorTokenProvider) ||
options.Providers.Contains(_identityOptions.Tokens.AuthenticatorTokenProvider))
{
return;
}

options.Providers.Add(_identityOptions.Tokens.AuthenticatorTokenProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OrchardCore.Users.Models;

namespace OrchardCore.Users.Services;

public sealed class PhoneProviderTwoFactorOptionsConfiguration : IConfigureOptions<TwoFactorOptions>
{
private readonly IdentityOptions _identityOptions;

public PhoneProviderTwoFactorOptionsConfiguration(IOptions<IdentityOptions> identityOptions)
{
_identityOptions = identityOptions.Value;
}

public void Configure(TwoFactorOptions options)
{
if (string.IsNullOrEmpty(_identityOptions.Tokens.ChangePhoneNumberTokenProvider) ||
options.Providers.Contains(_identityOptions.Tokens.ChangePhoneNumberTokenProvider))
{
return;
}

options.Providers.Add(_identityOptions.Tokens.ChangePhoneNumberTokenProvider);
}
}
40 changes: 25 additions & 15 deletions src/OrchardCore.Modules/OrchardCore.Users/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilde
pattern: _userOptions.LoginPath,
defaults: new { controller = _accountControllerName, action = nameof(AccountController.Login) }
);

routes.MapAreaControllerRoute(
name: "ChangePassword",
areaName: UserConstants.Features.Users,
Expand Down Expand Up @@ -119,7 +120,7 @@ public override void ConfigureServices(IServiceCollection services)

// Add the default token providers used to generate tokens for reset passwords, change email,
// and for two-factor authentication token generation.
var identityBuilder = services.AddIdentity<IUser, IRole>(options =>
services.AddIdentity<IUser, IRole>(options =>
{
// Specify OrchardCore User requirements.
// A user name cannot include an @ symbol, i.e. be an email address
Expand All @@ -128,17 +129,6 @@ public override void ConfigureServices(IServiceCollection services)
options.User.RequireUniqueEmail = true;
});

var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(identityBuilder.UserType);
identityBuilder.AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType);
var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(identityBuilder.UserType);
identityBuilder.AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType);
services.Configure<IdentityOptions>(options =>
{
options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
options.Tokens.ChangeEmailTokenProvider = TokenOptions.DefaultEmailProvider;
options.Tokens.ChangePhoneNumberTokenProvider = TokenOptions.DefaultPhoneProvider;
});
services.AddPhoneFormatValidator();
// Configure the authentication options to use the application cookie scheme as the default sign-out handler.
// This is required for security modules like the OpenID module (that uses SignOutAsync()) to work correctly.
Expand All @@ -153,7 +143,7 @@ public override void ConfigureServices(IServiceCollection services)
options.Cookie.Name = "orchauth_" + HttpUtility.UrlEncode(_tenantName);

// Don't set the cookie builder 'Path' so that it uses the 'IAuthenticationFeature' value
// set by the pipeline and comming from the request 'PathBase' which already ends with the
// set by the pipeline and coming from the request 'PathBase' which already ends with the
// tenant prefix but may also start by a path related e.g to a virtual folder.

options.LoginPath = "/" + userOptions.Value.LoginPath;
Expand Down Expand Up @@ -292,6 +282,17 @@ public override void ConfigureServices(IServiceCollection services)
}
}

[Feature(UserConstants.Features.UserEmailConfirmation)]
public class EmailConfirmationStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IConfigureOptions<IdentityOptions>, EmailConfirmationIdentityOptionsConfigurations>()
.AddTransient<EmailConfirmationTokenProvider>()
.AddOptions<EmailConfirmationTokenProviderOptions>();
}
}

[Feature("OrchardCore.Users.ChangeEmail")]
public class ChangeEmailStartup : StartupBase
{
Expand All @@ -318,6 +319,10 @@ public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder ro

public override void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IConfigureOptions<IdentityOptions>, ChangeEmailIdentityOptionsConfigurations>()
.AddTransient<ChangeEmailTokenProvider>()
.AddOptions<ChangeEmailTokenProviderOptions>();

services.Configure<TemplateOptions>(o =>
{
o.MemberAccessStrategy.Register<ChangeEmailViewModel>();
Expand All @@ -339,7 +344,7 @@ public override void ConfigureServices(IServiceCollection services)
}
}

[Feature("OrchardCore.Users.Registration")]
[Feature(UserConstants.Features.UserRegistration)]
public class RegistrationStartup : StartupBase
{
private const string RegisterPath = nameof(RegistrationController.Register);
Expand Down Expand Up @@ -384,7 +389,7 @@ public override void ConfigureServices(IServiceCollection services)
}
}

[Feature("OrchardCore.Users.Registration")]
[Feature(UserConstants.Features.UserRegistration)]
[RequireFeatures("OrchardCore.Deployment")]
public class RegistrationDeploymentStartup : StartupBase
{
Expand Down Expand Up @@ -433,6 +438,10 @@ public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder ro

public override void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IConfigureOptions<IdentityOptions>, PasswordResetIdentityOptionsConfigurations>()
.AddTransient<PasswordResetTokenProvider>()
.AddOptions<PasswordResetTokenProviderOptions>();

services.Configure<TemplateOptions>(o =>
{
o.MemberAccessStrategy.Register<LostPasswordViewModel>();
Expand Down Expand Up @@ -476,6 +485,7 @@ public override void ConfigureServices(IServiceCollection services)
}
}

[RequireFeatures("OrchardCore.Deployment")]
public class UserDeploymentStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
Expand Down
Loading