hi
Please share the code of your
logout
code, Thanks
we are calling this method, first we are calling external logout URL.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(LoggedOutModel))]
public class CustomLogout: LoggedOutModel
{
public override Task<IActionResult> OnGetAsync()
{
IConfigurationRoot _config = new ConfigurationBuilder().SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
.AddJsonFile("appsettings.json", false).Build();
var externalLogout = _config.GetSection("ADFSConfig:EndSession").Get<string>();
Response.Redirect(externalLogout);
return base.OnGetAsync();
}
}
Hi
Add your code into
CurrentPrincipalAccessor
'change
scope.var userPrincipal = await SignInManager.CreateUserPrincipalAsync(user); using (CurrentPrincipalAccessor.Change(userPrincipal)) { await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = "Login" + result }); }
var userPrincipal = await SignInManager.CreateUserPrincipalAsync(user); using (CurrentPrincipalAccessor.Change(userPrincipal)) { await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() { Identity = IdentitySecurityLogIdentityConsts.IdentityExternal, Action = result.ToIdentitySecurityLogAction(), UserName = user.Name }); }
thanks, it is working, but how can we do the same in logout as on logout userId is updated but username is null.
hi
var userPrincipal = await SignInManager.CreateUserPrincipalAsync(user); using (CurrentPrincipalAccessor.Change(userPrincipal)) { await IdentitySecurityLogManager.SaveAsync() }
SaveAsysc required to pass IdentitySecurityLogContext and in IdentitySecurityLogContext object only I'm not getting userId property, how userPrincipal is useful here ?
What is the purpose for adding ConCurrentUserId property and how it will be useful
It stores the currently loggedin concurrent ID and adds it to user claims.
Check the concurrent ID in the middleware, logout if it is different.
https://github.com/abpframework/abp-samples/blob/master/ConcurrentLogin/src/ConcurrentLogin.Web/ConcurrentLoginWebModule.cs#L264
Thank you, this is working but it is not redirecting to login page for previous browser
public override async Task<IActionResult> OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null)
{
IConfigurationRoot _config = new ConfigurationBuilder().SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
.AddJsonFile("appsettings.json", false).Build();
string webBaseUri = _config.GetSection("WebUISetting:URL").Get<string>();
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");
}
var userName = loginInfo.Principal.FindFirstValue(ClaimTypes.Name);
var result = await SignInManager.ExternalLoginSignInAsync(
loginInfo.LoginProvider,
userName,
isPersistent: false,
bypassTwoFactor: true
);
if (!result.Succeeded)
{
**await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
Action = "Login" + result
});**
}
var user = await UserManager.FindByNameAsync(userName);
if (result.Succeeded && user != null)
{
var activeUserOrganizationUnitMappings = await _userOrganizationUnitExt_TRRepository.GetListAsync(x => x.UserId == user.Id && x.IsActive);
if (user.IsActive && activeUserOrganizationUnitMappings != null && activeUserOrganizationUnitMappings.Any())
{
user.SetProperty(CommonConsts.ConCurrentUserId, new Guid().ToString("N"));
await UserManager.UpdateAsync(user);
await SignInManager.SignInAsync(user, false);
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
Action = result.ToIdentitySecurityLogAction(),
UserName = user.Name
});
return RedirectSafely(returnUrl, returnUrlHash);
}
}
}
I tried this, but still, I'm able to do the login in two different browsers at the same time.
What is the purpose for adding ConCurrentUserId property and how it will be useful? Also, on every login we need to assign same value for same user or different value?
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
using Volo.Abp.SecurityLog;
using Volo.Abp.Users;
namespace Volo.Abp.Identity;
public class IdentitySecurityLogManager : ITransientDependency
{
protected ISecurityLogManager SecurityLogManager { get; }
protected IdentityUserManager UserManager { get; }
protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; }
protected IUserClaimsPrincipalFactory<IdentityUser> UserClaimsPrincipalFactory { get; }
protected ICurrentUser CurrentUser { get; }
public IdentitySecurityLogManager(
ISecurityLogManager securityLogManager,
IdentityUserManager userManager,
ICurrentPrincipalAccessor currentPrincipalAccessor,
IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory,
ICurrentUser currentUser)
{
SecurityLogManager = securityLogManager;
UserManager = userManager;
CurrentPrincipalAccessor = currentPrincipalAccessor;
UserClaimsPrincipalFactory = userClaimsPrincipalFactory;
CurrentUser = currentUser;
}
public async Task SaveAsync(IdentitySecurityLogContext context)
{
Action<SecurityLogInfo> securityLogAction = securityLog =>
{
securityLog.Identity = context.Identity;
securityLog.Action = context.Action;
if (!context.UserName.IsNullOrWhiteSpace())
{
securityLog.UserName = context.UserName;
}
if (!context.ClientId.IsNullOrWhiteSpace())
{
securityLog.ClientId = context.ClientId;
}
foreach (var property in context.ExtraProperties)
{
securityLog.ExtraProperties[property.Key] = property.Value;
}
};
if (CurrentUser.IsAuthenticated)
{
await SecurityLogManager.SaveAsync(securityLogAction);
}
else
{
if (context.UserName.IsNullOrWhiteSpace())
{
await SecurityLogManager.SaveAsync(securityLogAction);
}
else
{
var user = await UserManager.FindByNameAsync(context.UserName);
if (user != null)
{
using (CurrentPrincipalAccessor.Change(await UserClaimsPrincipalFactory.CreateAsync(user)))
{
await SecurityLogManager.SaveAsync(securityLogAction);
}
}
else
{
await SecurityLogManager.SaveAsync(securityLogAction);
}
}
}
}
}
As per the sample code, I need to add like this
user.SetProperty(ConcurrentLoginConsts.ConcurrentLoginToken, Guid.NewGuid().ToString("N")); await UserManager.UpdateAsync(user); return await base.PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure);
In sample code we are using PasswordSignInAsync, will it work with SignInAsync as well? or SignInWithClaimsAsync is required after adding this ?
If I just add this code, will it be enough?
user.SetProperty("ConCurrentUserId", Guid.NewGuid().ToString("N"));
await UserManager.UpdateAsync(user);
await SignInManager.SignInAsync(user, false);
In my application, I'm doing external login (ADFS login), once it is success on ExternalLogincallback, I'm calling ExternalLoginSignInAsync as below
var result = await SignInManager.ExternalLoginSignInAsync( loginInfo.LoginProvider, userName, isPersistent: false, bypassTwoFactor: true );
once it is also successful, I'm calling below function
await SignInManager.SignInAsync(user, false);
but using these steps I'm not able to prevent concurrent login in two browsers, I have found the existing solution https://support.abp.io/QA/Questions/1023/How-to-prevent-ConCurrent-Users-from-logging-in-using-the-same-user-credentials
By following this I can add MyAbpClaimsPrincipalContributor and MyAbpClaimsService but I'm not sure, how I can implement in my application, could you please help me where can I use this in my application when I'm using ExternalLoginSignInAsync and SignInAsync.
ABP Framework version: v5.3.2
UI Type:React
Database System: EF Core (SQL Server)
Tiered (for MVC) or Auth Server Separated (for Angular): yes
Exception message and full stack trace:NA
Steps to reproduce the issue: Call ExternalLoginSignInAsync and SignInAsync
Could you please help with an example, how can I change the current Principal to override the ICurrentUser ?