Hi, Seems like breakpoints not being hit strangely. I've also tried following this guide https://abp.io/docs/latest/framework/ui/mvc-razor-pages/customization-user-interface#overriding-a-page-model-c
Any Gotchas that i might have missed? I've created the file in src\Eduverse.HttpApi.Host\Pages\Account\EduverseRegister.cshtml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Owl.reCAPTCHA;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.ExternalProviders;
using Volo.Abp.Account.Public.Web;
using Volo.Abp.Account.Public.Web.Pages.Account;
using Volo.Abp.Account.Public.Web.Security.Recaptcha;
using Volo.Abp.Account.Settings;
using Volo.Abp.Auditing;
using Volo.Abp.Content;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Reflection;
using Volo.Abp.Security.Claims;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
using Volo.Abp.Validation;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace Eduverse.Pages.Account
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(RegisterModel))]
public class EduverseRegisterModel : RegisterModel
{
public EduverseRegisterModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IAccountExternalProviderAppService accountExternalProviderAppService,
ICurrentPrincipalAccessor currentPrincipalAccessor,
IHttpClientFactory httpClientFactory): base(schemeProvider,
accountOptions, accountExternalProviderAppService, currentPrincipalAccessor, httpClientFactory)
{
System.Console.WriteLine("EduverseRegisterModel");
}
[UnitOfWork]
public override async Task<IActionResult> OnPostAsync()
{
try
{
ExternalProviders = await GetExternalProviders();
if (!await CheckSelfRegistrationAsync())
{
throw new UserFriendlyException(L["SelfRegistrationDisabledMessage"]);
}
await SetUseCaptchaAsync();
IdentityUser user;
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
}
if (Input.UserName.IsNullOrWhiteSpace())
{
Input.UserName = await UserManager.GetUserNameFromEmailAsync(Input.EmailAddress);
}
user = await RegisterExternalUserAsync(externalLoginInfo, Input.UserName, Input.EmailAddress);
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
}
else
{
user = await RegisterLocalUserAsync();
}
if (await SettingProvider.IsTrueAsync(IdentitySettingNames.SignIn.RequireConfirmedEmail) && !user.EmailConfirmed ||
await SettingProvider.IsTrueAsync(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber) && !user.PhoneNumberConfirmed)
{
await StoreConfirmUser(user);
return RedirectToPage("./ConfirmUser", new {
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash
});
}
if (await VerifyLinkTokenAsync())
{
using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(user)))
{
await IdentityLinkUserAppService.LinkAsync(new LinkUserInput
{
UserId = LinkUserId.Value,
TenantId = LinkTenantId,
Token = LinkToken
});
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentityProSecurityLogActionConsts.LinkUser,
UserName = user.UserName,
ExtraProperties =
{
{ IdentityProSecurityLogActionConsts.LinkTargetTenantId, LinkTenantId },
{ IdentityProSecurityLogActionConsts.LinkTargetUserId, LinkUserId }
}
});
using (CurrentTenant.Change(LinkTenantId))
{
var targetUser = await UserManager.GetByIdAsync(LinkUserId.Value);
using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(targetUser)))
{
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentityProSecurityLogActionConsts.LinkUser,
UserName = targetUser.UserName,
ExtraProperties =
{
{ IdentityProSecurityLogActionConsts.LinkTargetTenantId, targetUser.TenantId },
{ IdentityProSecurityLogActionConsts.LinkTargetUserId, targetUser.Id }
}
});
}
}
}
}
// skip password change if it is an external login
if (!IsExternalLogin)
{
await StoreChangePasswordUser(user);
return RedirectToPage("./ChangePassword", new {
returnUrl = ReturnUrl ?? "/",
returnUrlHash = ReturnUrlHash
});
}
await SignInManager.SignInAsync(user, isPersistent: true);
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return Redirect(ReturnUrl ?? "/");
}
catch (BusinessException e)
{
Alerts.Danger(GetLocalizeExceptionMessage(e));
return Page();
}
}
}
}
Actually i believe we had overridden the entire login page, is there some changes in v9.0.2 Login model that is not reflected in our current version of the Login Model?
Here is the code for login page
namespace Eduverse.Pages.Account
{
[DisableAuditing]
public class LoginModel : AccountPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid? LinkUserId { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid? LinkTenantId { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string LinkToken { get; set; }
public bool IsLinkLogin { get; set; }
[BindProperty]
public LoginInputModel LoginInput { get; set; }
public bool EnableLocalLogin { get; set; }
public bool IsSelfRegistrationEnabled { get; set; }
public bool ShowCancelButton { get; set; }
public bool UseCaptcha { get; set; }
//TODO: Why there is an ExternalProviders if only the VisibleExternalProviders is used.
public IEnumerable<ExternalProviderModel> ExternalProviders { get; set; }
public IEnumerable<ExternalProviderModel> VisibleExternalProviders => ExternalProviders.Where(x => !string.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
protected readonly IAuthenticationSchemeProvider SchemeProvider;
protected readonly AbpAccountOptions AccountOptions;
protected readonly ICurrentPrincipalAccessor CurrentPrincipalAccessor;
protected readonly IAbpRecaptchaValidatorFactory RecaptchaValidatorFactory;
protected readonly IAccountExternalProviderAppService AccountExternalProviderAppService;
public LoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IAbpRecaptchaValidatorFactory recaptchaValidatorFactory,
IAccountExternalProviderAppService accountExternalProviderAppService,
ICurrentPrincipalAccessor currentPrincipalAccessor,
IOptions<IdentityOptions> identityOptions,
IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions)
{
SchemeProvider = schemeProvider;
AccountExternalProviderAppService = accountExternalProviderAppService;
AccountOptions = accountOptions.Value;
CurrentPrincipalAccessor = currentPrincipalAccessor;
RecaptchaValidatorFactory = recaptchaValidatorFactory;
}
public virtual async Task<IActionResult> OnGetAsync()
{
LoginInput = new LoginInputModel();
var localLoginResult = await CheckLocalLoginAsync();
if (localLoginResult != null)
{
return localLoginResult;
}
IsSelfRegistrationEnabled = await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled);
UseCaptcha = await UseCaptchaOnLoginAsync();
IsLinkLogin = await VerifyLinkTokenAsync();
if (IsLinkLogin)
{
if (CurrentUser.IsAuthenticated)
{
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentitySecurityLogActionConsts.Logout
});
await SignInManager.SignOutAsync();
return Redirect(HttpContext.Request.GetDisplayUrl());
}
}
return Page();
}
etc...
}
}
Hi,
Nope we are using a project with combined auth server (But we are using a layered application if that is applicable)
I'm trying to add that file in src\Eduverse.HttpApi.Host\Pages\Account\Register.cshtml.cs
however it does not seem to work. Am I missing anything for the override?
Hi,
Okay, we are thinking of overriding the Register screen (the snippet you highlighted) and don't do the redirect to /ChangePassword
for external logins.
This also means that external users would not have any password set. Is that any risk? we want external users only to have to login using the SSO flow and never using password.
Hi,
Actually we do NOT want to show the password reset screen what is the simplest way we can implement this change? thanks