- ABP Framework version: v4.2.0
- UI type: MVC
- DB provider: EF Core
- Tiered (MVC) or Identity Server Separated (Angular): no
- Exception message and stack trace:
- Steps to reproduce the issue:
I have managed to add an property to User called ConCurrentUserId of type GUID, I have also created my own cutom Signin manager.
Can you tell me:
Which method I should use and How I can add a claim How I can get the claim to check the user property ConCurrentUserID, so that I can block login?
20 Answer(s)
-
0
Hi,
You can try:
public class MyAbpClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency { public Task ContributeAsync(AbpClaimsPrincipalContributorContext context) { var claimsIdentity = new ClaimsIdentity(); claimsIdentity.AddIfNotContains(new Claim("ConCurrentUserId", "value")); context.ClaimsPrincipal.AddIdentity(claimsIdentity); return Task.CompletedTask; } } public class MyAbpClaimsService : AbpClaimsService { public MyAbpClaimsService(IProfileService profile, ILogger<DefaultClaimsService> logger) : base(profile, logger) { } protected override IEnumerable<string> FilterRequestedClaimTypes(IEnumerable<string> claimTypes) { return base.FilterRequestedClaimTypes(claimTypes) .Union(new []{ AbpClaimTypes.TenantId, AbpClaimTypes.EditionId, "ConCurrentUserId" }); } } context.Services.Replace(ServiceDescriptor.Transient<IClaimsService, MyAbpClaimsService>());
-
0
@liangshiwei can you please let me know which project I put these Class's in
*.application
? . and where does do i put the following line in ...Configure Servcies
?context.Services.Replace(ServiceDescriptor.Transient<IClaimsService, MyAbpClaimsService>());
if I am right this just adds the Custom Claim ..... I still have to modify the
SignInManager
to check if the value of ConCurrentUserId has changed and block login .... am i correct?If so which Method in the
SignInManager
should i use? -
0
Hi,
You can put the class these Class to
.Web
project.Add the following code to the
ConfigureServices
method of.WebModule
context.Services.Replace(ServiceDescriptor.Transient<IClaimsService, MyAbpClaimsService>());
How I can get the claim to check the user property ConCurrentUserID, so that I can block login?
If you are not logged in, you cannot get the user claim, You should query the database values.
-
0
Having a GUID stored as i previously thought will not be a good solution. I am trying to update the security stamp as done in aspnetzero but it doesn't appear towork in abp.io
public override async Task SignInWithClaimsAsync(Volo.Abp.Identity.IdentityUser user, AuthenticationProperties authenticationProperties, IEnumerable<Claim> additionalClaims) { var userPrincipal = await CreateUserPrincipalAsync(user); foreach (var claim in additionalClaims) { userPrincipal.Identities.First().AddClaim(claim); } await Context.SignInAsync(IdentityConstants.ApplicationScheme, userPrincipal, authenticationProperties ?? new AuthenticationProperties()); await UserManager.UpdateSecurityStampAsync(user); }
can you please let me know why the above code doesn't work, when ii test my presios sessions in other browsers are not loged out and invalidated
-
0
Hi,
Try:
Configure<SecurityStampValidatorOptions>(options => { options.ValidationInterval = TimeSpan.FromSeconds(5); });
-
0
Okay The Validate methods are now hitting in my cutom SiginManager but i can't login as a current user seems i get a user is null
what is the best place to call the below so that i can make all previous sessions in all browsers invalid and just let the current user login?
if (user != null) { await UserManager.UpdateSecurityStampAsync(user); }
-
0
Hi,
You can override the
CheckPasswordSignInAsync
method ofSignInManager
classpublic override async Task<SignInResult> CheckPasswordSignInAsync( IdentityUser user, string password, bool lockoutOnFailure) { if (user == null) { throw new ArgumentNullException(nameof(user)); } var error = await PreSignInCheck(user); if (error != null) { return error; } if (await UserManager.CheckPasswordAsync(user, password)) { var alwaysLockout = AppContext .TryGetSwitch("Microsoft.AspNetCore.Identity.CheckPasswordSignInAlwaysResetLockoutOnSuccess", out var enabled) && enabled; // Only reset the lockout when not in quirks mode if either TFA is not enabled or the client is remembered for TFA. if (alwaysLockout || !await IsTfaEnabled(user) || await IsTwoFactorClientRememberedAsync(user)) { await ResetLockout(user); } await UserManager.UpdateSecurityStampAsync(user); return SignInResult.Success; } Logger.LogWarning(2, "User failed to provide the correct password."); if (UserManager.SupportsUserLockout && lockoutOnFailure) { // If lockout is requested, increment access failed count which might lock out the user await UserManager.AccessFailedAsync(user); if (await UserManager.IsLockedOutAsync(user)) { return await LockedOut(user); } } return SignInResult.Failed; }
-
0
It works for me.
public class MySignInManager : AbpSignInManager { public MySignInManager( IdentityUserManager userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<IdentityUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<IdentityUser>> logger, IAuthenticationSchemeProvider schemes, IUserConfirmation<IdentityUser> confirmation, IOptions<AbpIdentityOptions> options) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation, options) { } private async Task<bool> IsTfaEnabled(IdentityUser user) => UserManager.SupportsUserTwoFactor && await UserManager.GetTwoFactorEnabledAsync(user) && (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0; public override async Task<SignInResult> CheckPasswordSignInAsync( IdentityUser user, string password, bool lockoutOnFailure) { if (user == null) { throw new ArgumentNullException(nameof(user)); } var error = await PreSignInCheck(user); if (error != null) { return error; } if (await UserManager.CheckPasswordAsync(user, password)) { var alwaysLockout = AppContext .TryGetSwitch("Microsoft.AspNetCore.Identity.CheckPasswordSignInAlwaysResetLockoutOnSuccess", out var enabled) && enabled; // Only reset the lockout when not in quirks mode if either TFA is not enabled or the client is remembered for TFA. if (alwaysLockout || !await IsTfaEnabled(user) || await IsTwoFactorClientRememberedAsync(user)) { await ResetLockout(user); } await UserManager.UpdateSecurityStampAsync(user); return SignInResult.Success; } Logger.LogWarning(2, "User failed to provide the correct password."); if (UserManager.SupportsUserLockout && lockoutOnFailure) { // If lockout is requested, increment access failed count which might lock out the user await UserManager.AccessFailedAsync(user); if (await UserManager.IsLockedOutAsync(user)) { return await LockedOut(user); } } return SignInResult.Failed; } } public override void PreConfigureServices(ServiceConfigurationContext context) { .................. PreConfigure<IdentityBuilder>(builder => { builder .AddSignInManager<MySignInManager>(); }); } public override void ConfigureServices(ServiceConfigurationContext context) { ............ Configure<SecurityStampValidatorOptions>(options => { options.ValidationInterval = TimeSpan.FromSeconds(5); }); ........... }
-
0
I have done exactly what you have done but it doesn't work i am logged out after 5 seconds
using IdentityServer4.Events; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Guids; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Security.Claims; using IdentityUser = Volo.Abp.Identity.IdentityUser; namespace DesertFire.Ppm.Web { public class PpmSignInManager : AbpSignInManager { public PpmSignInManager( IdentityUserManager userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<IdentityUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<IdentityUser>> logger, IAuthenticationSchemeProvider schemes, IUserConfirmation<IdentityUser> confirmation, IOptions<AbpIdentityOptions> options) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation, options) { } private async Task<bool> IsTfaEnabled(IdentityUser user) => UserManager.SupportsUserTwoFactor && await UserManager.GetTwoFactorEnabledAsync(user) && (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0; public override async Task<SignInResult> CheckPasswordSignInAsync(IdentityUser user, string password, bool lockoutOnFailure) { if (user == null) { throw new ArgumentNullException(nameof(user)); } var error = await PreSignInCheck(user); if (error != null) { return error; } if (await UserManager.CheckPasswordAsync(user, password)) { var alwaysLockout = AppContext.TryGetSwitch("Microsoft.AspNetCore.Identity.CheckPasswordSignInAlwaysResetLockoutOnSuccess", out var enabled) && enabled; // Only reset the lockout when TFA is not enabled when not in quirks mode if (alwaysLockout || !await IsTfaEnabled(user) || await IsTwoFactorClientRememberedAsync(user)) { await ResetLockout(user); } await UserManager.UpdateSecurityStampAsync(user); return SignInResult.Success; } Logger.LogWarning(2, "User {userId} failed to provide the correct password.", await UserManager.GetUserIdAsync(user)); if (UserManager.SupportsUserLockout && lockoutOnFailure) { // If lockout is requested, increment access failed count which might lock out the user await UserManager.AccessFailedAsync(user); if (await UserManager.IsLockedOutAsync(user)) { return await LockedOut(user); } } return SignInResult.Failed; } } } public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => { options.AddAssemblyResource( typeof(PpmResource), typeof(PpmDomainModule).Assembly, typeof(PpmDomainSharedModule).Assembly, typeof(PpmApplicationModule).Assembly, typeof(PpmApplicationContractsModule).Assembly, typeof(PpmWebModule).Assembly ); }); PreConfigure<IdentityBuilder>(identityBuilder => { identityBuilder.AddSignInManager<PpmSignInManager>(); }); } public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); var configuration = context.Services.GetConfiguration(); ConfigureBundles(); ConfigureUrls(configuration); ConfigurePages(configuration); ConfigureCache(configuration); ConfigureAuthentication(context, configuration); ConfigureAutoMapper(); ConfigureVirtualFileSystem(hostingEnvironment); ConfigureNavigationServices(); ConfigureAutoApiControllers(); ConfigureSwaggerServices(context.Services); ConfigureCors(context, configuration); ConfigureExternalProviders(context); //context.Services.Replace(ServiceDescriptor.Transient<IClaimsService, PpmAbpClaimsService>()); Configure<SecurityStampValidatorOptions>(options => { options.ValidationInterval = TimeSpan.FromSeconds(5); }); }
-
0
Can I check it remotely? shiwei.liang@volosoft.com
-
0
Sure how would you like to connect ?
Teams
-
0
-
0
i cant see the brach dud you publish to origin ?
-
0
-
0
Hi,
I find the problem, see : https://github.com/abpframework/abp/pull/7814/files
For now , you need to :
public class MyAbpSecurityStampValidator : AbpSecurityStampValidator { protected ITenantConfigurationProvider TenantConfigurationProvider { get; } protected ICurrentTenant CurrentTenant { get; } public MyAbpSecurityStampValidator( IOptions<SecurityStampValidatorOptions> options, SignInManager<IdentityUser> signInManager, ISystemClock systemClock, ILoggerFactory loggerFactory, ITenantConfigurationProvider tenantConfigurationProvider, ICurrentTenant currentTenant) : base(options, signInManager, systemClock, loggerFactory) { TenantConfigurationProvider = tenantConfigurationProvider; CurrentTenant = currentTenant; } [UnitOfWork] public override async Task ValidateAsync(CookieValidatePrincipalContext context) { var tenant = await TenantConfigurationProvider.GetAsync(saveResolveResult: false); using (CurrentTenant.Change(tenant?.Id, tenant?.Name)) { await base.ValidateAsync(context); } } } context.Services.AddScoped<MyAbpSecurityStampValidator>(); context.Services.AddScoped(typeof(SecurityStampValidator<IdentityUser>), provider => provider.GetService(typeof(MyAbpSecurityStampValidator))); context.Services.AddScoped(typeof(ISecurityStampValidator), provider => provider.GetService(typeof(MyAbpSecurityStampValidator)));
-
0
Should i put the following in ConfigureServices or PreConfigureServices in the
*WebModule
*.web
projectcontext.Services.AddScoped<MyAbpSecurityStampValidator>(); context.Services.AddScoped(typeof(SecurityStampValidator<IdentityUser>), provider => provider.GetService(typeof(MyAbpSecurityStampValidator))); context.Services.AddScoped(typeof(ISecurityStampValidator), provider => provider.GetService(typeof(MyAbpSecurityStampValidator)));
-
0
-
0
I have discoverd a side effect when implementing this in our application to deal with concurrent user.
It seems when we updated the user via his profile or in the users table under Identity the security stamp is updated and the user is logged out.
can you please suggest how we can over come this, if the user is updating his profile or via the user table the user shouldn't update its seucirty stamp.
-
0
Hi,
You can override these methods and not update security token
-
0
In our use case the users are not allowed to change their username so i have override the UpdatAsync to be as follows
(await UserManager.SetUserNameAsync(user, input.UserName)).CheckErrors();
The above line is what changes the SecurityStamp which forces the user out because we are using
AbpSecurityStampValidator
to ensure that the same user cant be signed in concurrently using the same username.public override async Task<IdentityUserDto> UpdateAsync(Guid id, IdentityUserUpdateDto input) { //ADDED BY VB: To check if all Roles avalible as per license have been consumed. if (CurrentUser.UserName != "dfo.admin") { await CheckCurrentTenantsLicenseConsumed(id, input); } await IdentityOptions.SetAsync(); var user = await UserManager.GetByIdAsync(id); user.ConcurrencyStamp = input.ConcurrencyStamp; if (!string.Equals(user.UserName, input.UserName, StringComparison.InvariantCultureIgnoreCase)) { if (await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsUserNameUpdateEnabled)) { (await UserManager.SetUserNameAsync(user, input.UserName)).CheckErrors(); } } await UpdateUserByInput(user, input); input.MapExtraPropertiesTo(user); (await UserManager.UpdateAsync(user)).CheckErrors(); await CurrentUnitOfWork.SaveChangesAsync(); var userDto = ObjectMapper.Map<Volo.Abp.Identity.IdentityUser, IdentityUserDto>(user); //ADDED BY VB: To send an email to the user notifying their prifile has changed var ppmUser = await GetUserByEmail(input.Email); await _subscriptionEmailer.SendPpmUserUpdatedEmailAsync(ppmUser, "MVC"); return userDto; }
Thanks for you help @liangshiwei