- ABP Framework version: v9.0.2
- UI Type: Angular
- Database System: EF Core (PostgreSQL, etc..)
- Tiered (for MVC) or Auth Server Separated (for Angular): yes
- Exception message and full stack trace:
- Steps to reproduce the issue:
Intended flow:
- We have users login with SSO, if their email domain is whitelisted, we will create a new user
public virtual async Task AutoRegisterUserAsync(string email, CustomSaasTenantDto tenant)
{
using (_currentTenant.Change(tenant.Id))
{
var existingUser = await _userManager.FindByEmailAsync(email);
// Check if the user already exists
if (existingUser == null)
{
// Create a new user
var newUser = new IdentityUser(
_guidGenerator.Create(),
email,
email,
tenant.Id // Assign the user to the tenant
);
var result = await _userManager.CreateAsync(newUser);
if (result.Succeeded)
{
// Optionally assign roles to the user
await _userManager.AddToRoleAsync(newUser, DefaultRoleConsts.Learner);
}
}
}
}
After user is created, he should be able to login again with SSO.
User should not be able to login with password as no password was set as part of the SSO onboarding flow
Actual:
23 Answer(s)
-
0
-
0
Hi,
Actually we do NOT want to show the password reset screen what is the simplest way we can implement this change? thanks
-
0
HI,
It's changing the password screen, not resetting the password, and it's the same as the screenshot you shared
Another way is to create your own change password page and redirect.
-
0
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.
-
0
Hi,
Sory, I misunderstood your meaning. I thought you wanted to set a password for the user.
This is simple; you just need to disable the local login (This way, all users can't log in with a password)
Or you can override the Register model to only check external users
[Dependency(ReplaceServices = true)] [ExposeServices(typeof(RegisterModel))] public class MyRegisterModel : RegisterModel { public MyRegisterModel(IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IAccountExternalProviderAppService accountExternalProviderAppService, ICurrentPrincipalAccessor currentPrincipalAccessor, IHttpClientFactory httpClientFactory) : base(schemeProvider, accountOptions, accountExternalProviderAppService, currentPrincipalAccessor, httpClientFactory) { } [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(); } } }
-
0
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?
-
0
Hi,
You can try putting a breakpoint to debug.
-
0
-
0
Hi,
If you are using the
Auth Server Separated
Try putEduverseRegisterModel
in theAuthServer
project.And redirect to register page for new users only
-
0
Hi,
Nope we are using a project with combined auth server (But we are using a layered application if that is applicable)
-
0
-
0
Can you share a GIF, and your login page model code(If you override the login page model)
Is this the correct import? I noticed that the code snippet should use Pro version of the lib right?
namespace is correct.
-
0
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... } }
-
0
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?
-
0
Hi,
We have not changed the login/register code recently.
You can log in with a new external user and check if it redirects you to the register page, you can set breakpoints in the constructor to check if the override is successful
-
0
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(); } } } }
-
0
Can you share your solution structure screenshot
-
0
-
0
After investigating, i've realised that the user is not redirected to a /Register screen. I've tested overriding the ChangePassword model and breakpoints are being hit
Seems like Login > Microsoft SSO > change password screen
-
0
-
0
I've updated the login model and it stops redirecting to the change password screen for external users. Can you advise if this is okay (i've only commented out one block of code)?
using System; using System.Security.Claims; using System.Threading.Tasks; 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.Account.Security.Recaptcha; using Volo.Abp.Account.ExternalProviders; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Security.Claims; using Volo.Abp.Uow; using Volo.Abp.Account.Public.Web.Pages.Account; using Volo.Abp.Account.Public.Web; using Volo.Abp.Account; using Volo.Abp.DependencyInjection; // namespace Volo.Abp.Account.Public.Web.Pages.Account; namespace Eduverse.Pages.Account { [Dependency(ReplaceServices = true)] [ExposeServices(typeof(LoginModel))] public class EduverseLoginModel: LoginModel { public EduverseLoginModel(IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IAbpRecaptchaValidatorFactory recaptchaValidatorFactory, IAccountExternalProviderAppService accountExternalProviderAppService, ICurrentPrincipalAccessor currentPrincipalAccessor, IOptions<IdentityOptions> identityOptions, IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions) : base(schemeProvider, accountOptions, recaptchaValidatorFactory, accountExternalProviderAppService, currentPrincipalAccessor, identityOptions, reCaptchaOptions) { System.Console.WriteLine("EduverseLoginModel"); } [UnitOfWork] public override async Task<IActionResult> OnGetExternalLoginCallbackAsync(string remoteError = null) { //TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample) /* Also did not implement these: * - Logout(string logoutId) */ if (remoteError != null) { Logger.LogWarning($"External login callback error: {remoteError}"); return RedirectToPage("./Login"); } await IdentityOptions.SetAsync(); var loginInfo = await SignInManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { Logger.LogWarning("External login info is not available"); return RedirectToPage("./Login"); } IsLinkLogin = await VerifyLinkTokenAsync(); // Commented out for resolve ED-520 - Prevent prompt change pw for external users // if (await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin)) // { // var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); // if (user != null && user.PasswordHash == null) // { // await StoreChangePasswordUser(user); // return RedirectToPage("./ChangePassword", new { // returnUrl = ReturnUrl ?? "/", // returnUrlHash = ReturnUrlHash // }); // } // } var askUserPasswordResult = await ShouldAskUserPasswordAsync(loginInfo); if (askUserPasswordResult != null) { return askUserPasswordResult; } var result = await SignInManager.ExternalLoginSignInAsync( loginInfo.LoginProvider, loginInfo.ProviderKey, isPersistent: true, bypassTwoFactor: true ); if (!result.Succeeded) { await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = "Login" + result }); } if (result.IsLockedOut) { var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); await StoreLockedUser(user); Logger.LogWarning($"Cannot proceed because user is locked out!"); return RedirectToPage("./LockedOut", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } if (result.IsNotAllowed) { Logger.LogWarning($"External login callback error: User is Not Allowed!"); var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); if (user == null) { Logger.LogWarning($"External login callback error: User is Not Found!"); return RedirectToPage("./Login"); } if (user.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(user)) { await StoreChangePasswordUser(user); return RedirectToPage("./ChangePassword", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } if (user.IsActive) { await StoreConfirmUser(user); return RedirectToPage("./ConfirmUser", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } return RedirectToPage("./Login"); } if (result.Succeeded) { var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); if (user == null) { Logger.LogWarning($"External login callback error: User is Not Found!"); return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(user))) { await AccountExternalLoginAppService.SetPasswordVerifiedAsync(loginInfo.LoginProvider, loginInfo.ProviderKey); } if (IsLinkLogin) { 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 } } }); } } } } await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = result.ToIdentitySecurityLogAction(), UserName = user.UserName }); // Clear the dynamic claims cache. await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } //TODO: Handle other cases for result! var email = loginInfo.Principal.FindFirstValue(AbpClaimTypes.Email) ?? loginInfo.Principal.FindFirstValue(ClaimTypes.Email); var externalUser = email.IsNullOrWhiteSpace() ? null : await UserManager.FindByEmailAsync(email); if (externalUser == null) { return RedirectToPage("./Register", new { isExternalLogin = true, returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, linkTenantId = LinkTenantId, linkUserId = LinkUserId, linkToken = LinkToken }); } if (await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey) == null) { CheckIdentityErrors(await UserManager.AddLoginAsync(externalUser, loginInfo)); } if (await HasRequiredIdentitySettings()) { Logger.LogWarning($"New external user is created but confirmation is required!"); await StoreConfirmUser(externalUser); return RedirectToPage("./ConfirmUser", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } askUserPasswordResult = await ShouldAskUserPasswordAsync(loginInfo); if (askUserPasswordResult != null) { return askUserPasswordResult; } await SignInManager.SignInAsync(externalUser, false , loginInfo.LoginProvider); // Clear the dynamic claims cache. await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(externalUser.Id, externalUser.TenantId); if (IsLinkLogin) { using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(externalUser))) { await IdentityLinkUserAppService.LinkAsync(new LinkUserInput { UserId = LinkUserId.Value, TenantId = LinkTenantId, Token = LinkToken }); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentityProSecurityLogActionConsts.LinkUser, UserName = externalUser.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 } } }); } } } } await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = result.ToIdentitySecurityLogAction(), UserName = externalUser.Name }); return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } } }
-
0
Hi,
Yes, I think that's enough.
-
0
Helpful, think we can close this off. Thanks