Open Closed

Custom ConnectionStringResolver Issue #9350


User avatar
0
Spospisil created

Hi,

I have implemented a custom ConnectionStringResolver class but the ICurrentTenant implementation does not resolve to the current logged in tenant. However I can't even get ABP's solutions to run even if I exclude this resolver class. At this point I have grown very tired of getting any sample projects to work to show support when I generate them using ABP tools, so I'll just give you the generated solution and you can figure out how to get it to run first and then we can have a discussion about the CurrentTenant issue I am having.

Can't waste yet another day trying to get a stock generated ABP solution to run

Steps to Reproduce:

  1. Get stock ABP generated solution to compile/run (Good luck as I've spend 2 days attempting to get it generated and to even work)
  2. Login in as host admin and create a Tenant of 'KENCO' with a non-shared DB (tenant has their own db)
  3. Login in as the KENCO tenant admin
  4. Monitor the debug messages I've added to the CFDMultiTenantConnectionStringResolver.cs class
  5. Observe the CurrentTenant variable still being set to null
  6. I've not been able to verify this test project will reproduce the issue I am experiencing as I've stated above I can't even get the ABP tool generated solution to compile/run

https://github.com/spospisil/ABPSampleTenant.git


57 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please make your GitHub repos PRIVATE.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    I can build your project via dotnet build command

     % dotnet build
    Restore complete (0.2s)
      Test.Domain.Shared succeeded (0.1s) → ABPSampleTenant/src/Test.Domain.Shared/bin/Debug/net9.0/Test.Domain.Shared.dll
      Test.Domain succeeded (0.1s) → ABPSampleTenant/src/Test.Domain/bin/Debug/net9.0/Test.Domain.dll
      Test.EntityFrameworkCore succeeded (0.1s) → ABPSampleTenant/src/Test.EntityFrameworkCore/bin/Debug/net9.0/Test.EntityFrameworkCore.dll
      Test.AuthServer succeeded (2.6s) → bin/Debug/net9.0/Test.AuthServer.dll
    
    Build succeeded in 3.7s
    maliming@malim Test.AuthServer % 
    
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    If your Entity has no IMultiTenant, abp will switch to host(tenant will null) If your DbContext has IgnoreMultiTenancyAttribute, abp will switch to host(tenant will null)

  • User Avatar
    0
    Spospisil created

    So, neither one of those things is true as you can see in the demo project!

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I create and log in to a tenant, it works

  • User Avatar
    0
    Spospisil created

    Ok, thanks for getting the base project solution working...funny how myself and a coworker were both unable to get it to compile and run in Visual Studio.

    Let me now take the working base project and try to reproduce the issue I am experiencing in my actual project.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok. I just open it in vscode and run it

    dotnet run

  • User Avatar
    0
    Spospisil created

    Well, since you can see that I specified Visual Studio 2022 vs vscode in the initial creation of the ticket, I'm not sure why you would use vscode. What's the point of asking out environment tools if you're just going to use something else to attempt to reproduce the issue. Doesn't really make sense does it

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    End your VS studio. and run dotnet build /graphbuild to build your project. then open it in VS Studio again.

  • User Avatar
    0
    Spospisil created

    What are the other possibilities why ICurrentTenant would not resolve to the current tenant for my abpmodule? I can't reproduce the issue and sharing the code where it's happening is not an option. I've spent over a week trying to figure this out with no success.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    I don't have many ideas.

    I ran your project, and it works.

    https://abp.io/support/questions/9350/Custom-ConnectionStringResolver-Issue#answer-3a1a07c1-789d-d7e0-e1c2-2bab8465974e

  • User Avatar
    0
    Spospisil created

    The __tenant value is not present in the cookies. Where does this value get set?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    if the current user has logged in. This should always be the first contributor for the security.

    https://abp.io/docs/latest/framework/architecture/multi-tenancy#default-tenant-resolvers

  • User Avatar
    0
    Spospisil created

    Hi,

    See my below code for the login. I have verified at least with my custom login page that the resolver exists as it should. Is there something I am doing wrong with replacing the login logic?

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Http.Extensions;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using Owl.reCAPTCHA;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Diagnostics;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    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.Security.Recaptcha;
    using Volo.Abp.Account.Settings;
    using Volo.Abp.Auditing;
    using Volo.Abp.Data;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.Identity;
    using Volo.Abp.Identity.AspNetCore;
    using Volo.Abp.Identity.Settings;
    using Volo.Abp.MultiTenancy;
    using Volo.Abp.Reflection;
    using Volo.Abp.Security.Claims;
    using Volo.Abp.Settings;
    using Volo.Abp.Uow;
    using Volo.Abp.Validation;
    using Volo.Saas.Tenants;
    using IdentityUser = Volo.Abp.Identity.IdentityUser;
    
    namespace CFData.Structure.Web.Pages.Account;
    
    [DisableAuditing]
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(MyLoginModel), typeof(LoginModel))]
    public class MyLoginModel : LoginModel
    {
        [Inject]
        public ICurrentTenantAccessor CurrentTenantAccessor { get; set; }
    
        [Inject]
        public ITenantStore TenantStore { get; set; }
    
        private readonly ITenantRepository _tenantRepository;
        protected IDataFilter DataFilter { get; }
    
        public MyLoginModel(
            IAuthenticationSchemeProvider schemeProvider, 
            IOptions<AbpAccountOptions> accountOptions, 
            IAbpRecaptchaValidatorFactory recaptchaValidatorFactory, 
            IAccountExternalProviderAppService accountExternalProviderAppService, 
            ICurrentPrincipalAccessor currentPrincipalAccessor, 
            IOptions<IdentityOptions> identityOptions, 
            IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions,
            ITenantRepository tenantRepository,
            DataFilter dataFilter) : base(
                schemeProvider, 
                accountOptions, 
                recaptchaValidatorFactory, 
                accountExternalProviderAppService, 
                currentPrincipalAccessor, 
                identityOptions, 
                reCaptchaOptions)
        {
            //ReCaptchaOptions = reCaptchaOptions;
            _tenantRepository = tenantRepository;
            DataFilter = dataFilter;
        }
    
        public override async Task<IActionResult> OnGetAsync()
        {
            return await base.OnGetAsync();
        }
    
        [UnitOfWork] //TODO: Will be removed when we implement action filter
        public override async Task<IActionResult> OnPostAsync(string action)
        {
            try
            {
                await ReCaptchaVerification();
            }
            catch (UserFriendlyException e)
            {
                if (e is ScoreBelowThresholdException)
                {
                    var onScoreBelowThresholdResult = OnRecaptchaScoreBelowThreshold();
                    if (onScoreBelowThresholdResult != null)
                    {
                        return await onScoreBelowThresholdResult;
                    }
                }
    
                Alerts.Danger(GetLocalizeExceptionMessage(e));
                return Page();
            }
    
            //ValidateModel();
    
            await IdentityOptions.SetAsync();
    
            var localLoginResult = await CheckLocalLoginAsync();
            if (localLoginResult != null)
            {
                return localLoginResult;
            }
    
            IsSelfRegistrationEnabled = false; //await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled);
    
    
            await ReplaceEmailToUsernameOfInputIfNeeds();
    
    
            var getUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
            if (getUser == null)
            {
                Alerts.Danger("User does not exist");
                return Page();
            }
    
            TenantConfiguration tenant = null;
            if (getUser.TenantId is not null)
            {
                tenant = await TenantStore.FindAsync((Guid)getUser.TenantId);
                CurrentTenantAccessor.Current = new BasicTenantInfo(
                    tenant?.Id,
                    tenant?.Name);
            }
    
            IsLinkLogin = await VerifyLinkTokenAsync();
    
            var result = new Microsoft.AspNetCore.Identity.SignInResult();
    
            using (DataFilter.Disable<IMultiTenant>())
            {
                result = await SignInManager.PasswordSignInAsync(
                LoginInput.UserNameOrEmailAddress,
                LoginInput.Password,
                LoginInput.RememberMe,
                true);
    
                await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                {
                    Identity = IdentitySecurityLogIdentityConsts.Identity,
                    Action = result.ToIdentitySecurityLogAction(),
                    UserName = LoginInput.UserNameOrEmailAddress
                });
            }
    
            if (result.RequiresTwoFactor)
            {
                return RedirectToPage("./SendSecurityCode", new
                {
                    returnUrl = ReturnUrl,
                    returnUrlHash = ReturnUrlHash,
                    rememberMe = LoginInput.RememberMe,
                    linkUserId = LinkUserId,
                    linkTenantId = LinkTenantId,
                    linkToken = LinkToken
                });
            }
    
    
            if (result.IsLockedOut)
            {
                var lockedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
                await StoreLockedUser(lockedUser);
                return RedirectToPage("./LockedOut", new
                {
                    returnUrl = ReturnUrl,
                    returnUrlHash = ReturnUrlHash
                });
            }
    
            if (result.IsNotAllowed)
            {
                var notAllowedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
    
                if (!await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password))
                {
                    Alerts.Danger(L["InvalidUserNameOrPassword"]);
                    return Page();
                }
    
                if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser))
                {
                    await StoreChangePasswordUser(notAllowedUser);
                    return RedirectToPage("./ChangePassword", new
                    {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash,
                        RememberMe = LoginInput.RememberMe
                    });
                }
    
                if (notAllowedUser.IsActive)
                {
                    await StoreConfirmUser(notAllowedUser);
                    return RedirectToPage("./ConfirmUser", new
                    {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash
                    });
                }
    
                Alerts.Danger(L["LoginIsNotAllowed"]);
                return Page();
            }
    
            if (!result.Succeeded)
            {
                Alerts.Danger(L["InvalidUserNameOrPassword"]);
                return Page();
            }
    
            var user = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
    
    
    
    
            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 }
                                    }
                            });
                        }
                    }
    
                    return RedirectToPage("./LinkLogged", new
                    {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash,
                        targetLinkUserId = LinkUserId,
                        targetLinkTenantId = LinkTenantId
                    });
                }
            }
    
            // Clear the dynamic claims cache.
            await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
    
            return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
        }
    
        protected override async Task ReplaceEmailToUsernameOfInputIfNeeds()
        {
            if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress))
            {
                return;
            }
    
            var userByUsername = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
            if (userByUsername != null)
            {
                return;
            }
    
            var userByEmail = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
            if (userByEmail == null)
            {
                return;
            }
    
            LoginInput.UserNameOrEmailAddress = userByEmail.UserName;
        }
    
        protected override async Task<IdentityUser> GetIdentityUser(string userNameOrEmailAddress)
        {
            IdentityUser identityUser = null;
            using (CurrentTenant.Change(null))
            {
                identityUser = await base.UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress);
                if (identityUser == null)
                {
                    identityUser = await base.UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
                }
            }
    
            IdentityUser user = identityUser;
            Debug.Assert(user != null, "user != null");
            return user;
        }
    
        protected virtual async Task<IdentityUser> FindUserAsync(string uniqueUserNameOrEmailAddress)
        {
            IdentityUser user = null;
            using (CurrentTenant.Change(null))
            {
                user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
                       await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
    
                if (user != null)
                {
                    return user;
                }
            }
    
            foreach (var tenant in await _tenantRepository.GetListAsync())
            {
                using (CurrentTenant.Change(tenant.Id))
                {
                    user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
                           await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
    
                    if (user != null)
                    {
                        return user;
                    }
                }
            }
    
            return null;
        }
    
    }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Do you get any errors/exceptions with your custom login page?

    Thanks.

  • User Avatar
    0
    Spospisil created

    Can you tell me the things I need to check? Again the issue seems to be in any abp layered modules that I create the currenttenant is always null/blank even though I'm logged in as the tenant? Since I can't reproduce this issue I need guidance from ABP in order to check what might be wrong.

    It's been 3 weeks of solely focused on this and I still have no resolution.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I create the currenttenant is always null/blank even though I'm logged in as the tenant?

    What are the claims of your current user?

    You can inject the ICurrentUser and call the GetAllClaims method.

  • User Avatar
    0
    Spospisil created

    Hi,

    Here are the claims associated with the current logged in tenant user

    [09:55:10 FTL] Claim: email_verified Value: True [09:55:10 FTL] Claim: phone_number_verified Value: False [09:55:10 FTL] Claim: editionid Value: 3a1a4870-79bf-c38d-1e33-3c5a09fffd1e [09:55:10 FTL] Claim: client_id Value: Structure_Blazor [09:55:10 FTL] Claim: oi_tkn_id Value: 3a1a4c89-cfb7-9302-f739-76a2df5e99ea

    The following shows the current user, current user TenantId (which is correct), but then the CurrentTenantId is null and the IsAvailable is False.

    [09:55:10 FTL] Current User: KENCO_admin CurrentUserTenantId: 3a1a4978-6b4a-2eaa-01c7-d5abd68df27f [09:55:10 FTL] Current ConnectionStringName: Company CurrentTenantId: IsAvailable: False [09:55:10 FTL] Connection String Resolved: Server=postgresql;Database=KENCO;Port=5432;User Id=sc_kenco_dba_3a1a49786b3b044130998ef1dd29e3a7;Password=a334!1Sz[-yJ;Ssl Mode=allow;

  • User Avatar
    0
    Spospisil created

    Hi,

    After realizing that I was missing some code in our AuthServer Module code I now get this but as you can see the Current TenantId is still not getting set.

    [10:44:13 FTL] Claim: iss Value: https://localhost.structurecloud.com/Auth⁠
    [10:44:13 FTL] Claim: exp Value: 1749037452
    [10:44:13 FTL] Claim: iat Value: 1749033852
    [10:44:13 FTL] Claim: aud Value: Structure
    [10:44:13 FTL] Claim: scope Value: openid profile Structure roles email phone
    [10:44:13 FTL] Claim: jti Value: 5c8a9744-9af6-4387-a1d5-deb62c48269b
    [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier⁠ Value: 3a1a4978-8cfc-22db-b223-931eeb528231
    [10:44:13 FTL] Claim: preferred_username Value: KENCO_admin
    [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress⁠ Value: spospisil109@gmail.com
    [10:44:13 FTL] Claim: tenantid Value: 3a1a4978-6b4a-2eaa-01c7-d5abd68df27f
    [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname⁠ Value: Kenco Admin
    [10:44:13 FTL] Claim: phone_number_verified Value: False
    [10:44:13 FTL] Claim: email_verified Value: True
    [10:44:13 FTL] Claim: session_id Value: 673ea7fd-60d4-4e55-8451-84247357b6a9
    [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name⁠ Value: KENCO_admin
    [10:44:13 FTL] Claim: editionid Value: 3a1a4870-79bf-c38d-1e33-3c5a09fffd1e
    [10:44:13 FTL] Claim: BrowserInfo Value: 13926292-bf7f-4e71-9684-c6fc7e28a316
    [10:44:13 FTL] Claim: oi_prst Value: Structure_Blazor
    [10:44:13 FTL] Claim: oi_au_id Value: 3a1a497a-9a71-380a-0d63-c9be9944c3cf
    [10:44:13 FTL] Claim: client_id Value: Structure_Blazor
    [10:44:13 FTL] Claim: oi_tkn_id Value: 3a1a4cb7-747a-af97-ba5f-7c1531707b55
    
    [10:44:13 FTL] Current User: KENCO_admin CurrentUserTenantId: 3a1a4978-6b4a-2eaa-01c7-d5abd68df27f
    [10:44:13 FTL] Current ConnectionStringName: Company CurrentTenantId: IsAvailable: False
    [10:44:13 FTL] Connection String Resolved: Server=postgresql;Database=KENCO;Port=5432;User Id=sc_kenco_dba_3a1a49786b3b044130998ef1dd29e3a7;Password=a334!1Sz[-yJ;Ssl Mode=allow;
    

    Here is the code in my connection string resolver class.

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IConnectionStringResolver), typeof(MultiTenantConnectionStringResolver))]
    public class SWMultiTenantConnectionStringResolver : MultiTenantConnectionStringResolver
    {
        private readonly ICurrentTenant _currentTenant;
        private readonly ICurrentUser _currentUser;
        private readonly IServiceProvider _serviceProvider;
        private readonly ILogger<SWMultiTenantConnectionStringResolver> _logger;
    
        public SWMultiTenantConnectionStringResolver(
            IOptionsMonitor<AbpDbConnectionOptions> options,
            ICurrentTenant currentTenant,
            IServiceProvider serviceProvider,
            ICurrentUser currentUser,
            ILogger<SWMultiTenantConnectionStringResolver> logger) : base(options, currentTenant, serviceProvider)
        {
            _currentTenant = currentTenant;
            _currentUser = currentUser;
            _serviceProvider = serviceProvider;
            _logger = logger;
        }
    
        public override async Task<string> ResolveAsync(string? connectionStringName = null)
        {
            string connectionstring = "";
    
            var claims = _currentUser.GetAllClaims().ToList();
            foreach (var claim in claims)
            {
                _logger.LogCritical($"Claim: {claim.Type} Value: {claim.Value}");
            }
    
            _logger.LogCritical($"Current User: {_currentUser.UserName} CurrentUserTenantId: {_currentUser.TenantId}");
            _logger.LogCritical($"Current ConnectionStringName: {connectionStringName} CurrentTenantId: {_currentTenant.Id} IsAvailable: {_currentTenant.IsAvailable}");
    
            //Force Current Tenant to write to Host DB for the below functions
            switch (connectionStringName)
            {
                case "Default":
                case LanguageManagementDbProperties.ConnectionStringName:
                case AbpFeatureManagementDbProperties.ConnectionStringName:
                case AbpPermissionManagementDbProperties.ConnectionStringName:
                case AbpOpenIddictDbProperties.ConnectionStringName:
                case AbpBackgroundJobsDbProperties.ConnectionStringName:
                case AbpBlobStoringDatabaseDbProperties.ConnectionStringName:
                case AbpIdentityDbProperties.ConnectionStringName:
                case AbpCmsKitDbProperties.ConnectionStringName:
                case AbpSettingManagementDbProperties.ConnectionStringName:
                case SaasDbProperties.ConnectionStringName:
                case ChatDbProperties.ConnectionStringName:
                case TextTemplateManagementDbProperties.ConnectionStringName:
                case PaymentDbProperties.ConnectionStringName:
                case TemplateDbProperties.ConnectionStringName:
                case SecurityDbProperties.ConnectionStringName:
                case LexiconDbProperties.ConnectionStringName:
                case LicensingDbProperties.ConnectionStringName:
                    using (_currentTenant.Change(null))
                    {
                        connectionstring = await base.ResolveAsync(connectionStringName);
                        _logger.LogCritical($"Connection String Resolved: {connectionstring}");
                        return await Task.FromResult(connectionstring);
                    }
    
                default:
                    if (_currentTenant.Id is null || _currentUser.TenantId is not null)
                    {
                        using (_currentTenant.Change(_currentUser.TenantId))
                        {
                            connectionstring = await base.ResolveAsync(connectionStringName);
                        }
                    }
                    else
                    { 
                        connectionstring = await base.ResolveAsync(connectionStringName);
                    }
    
                    _logger.LogCritical($"Connection String Resolved: {connectionstring}");
                    return await Task.FromResult(connectionstring);
            }
        }
    

    Here is my code in my auth server module

    private static void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddAuthentication()
            .AddGoogle(GoogleDefaults.AuthenticationScheme, _ => { })
            .WithDynamicOptions<GoogleOptions, GoogleHandler>(
                GoogleDefaults.AuthenticationScheme,
                options =>
                {
                    options.WithProperty(x => x.ClientId);
                    options.WithProperty(x => x.ClientSecret, isSecret: true);
                }
            )
            .AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
            {
                //Personal Microsoft accounts as an example.
                options.AuthorizationEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize";
                options.TokenEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
            })
            .WithDynamicOptions<MicrosoftAccountOptions, MicrosoftAccountHandler>(
                MicrosoftAccountDefaults.AuthenticationScheme,
                options =>
                {
                    options.WithProperty(x => x.ClientId);
                    options.WithProperty(x => x.ClientSecret, isSecret: true);
                }
            )
            .AddTwitter(TwitterDefaults.AuthenticationScheme, options => options.RetrieveUserDetails = true)
            .WithDynamicOptions<TwitterOptions, TwitterHandler>(
                TwitterDefaults.AuthenticationScheme,
                options =>
                {
                    options.WithProperty(x => x.ConsumerKey);
                    options.WithProperty(x => x.ConsumerSecret, isSecret: true);
                }
            )
            .AddJwtBearer(options =>
            {
                options.Authority = configuration["AuthServer:Authority"];
                options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                options.Audience = "Structure";
            });
    }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Try using Change method instead of set CurrentTenantAccessor.Current

    TenantConfiguration tenant = null;
    if (getUser.TenantId is not null)
    {
        tenant = await TenantStore.FindAsync((Guid)getUser.TenantId);
        CurrentTenantAccessor.Current = new BasicTenantInfo(
            tenant?.Id,
            tenant?.Name);
    }
    
    using (CurrentTenant.Change(tenant?.Id))
    {
      // Add all the code following this to this scope
    }
    
  • User Avatar
    0
    Spospisil created

    Exactly where do I make your suggested change and will this set the current tenant correctly so all queries I’ll to the db will be filtered by that tenant automatically?

  • User Avatar
    0
    Spospisil created

    Exactly where do I make your suggested change and will this set the current tenant correctly so all queries I’ll to the db will be filtered by that tenant automatically?

    I have found that everywhere in my blazor wasm app the current tenant is not being set correctly. No idea why.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Tenant will work as expected when tenant claim is set correctly.

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Http.Extensions;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using Owl.reCAPTCHA;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Diagnostics;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    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.Security.Recaptcha;
    using Volo.Abp.Account.Settings;
    using Volo.Abp.Auditing;
    using Volo.Abp.Data;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.Identity;
    using Volo.Abp.Identity.AspNetCore;
    using Volo.Abp.Identity.Settings;
    using Volo.Abp.MultiTenancy;
    using Volo.Abp.Reflection;
    using Volo.Abp.Security.Claims;
    using Volo.Abp.Settings;
    using Volo.Abp.Uow;
    using Volo.Abp.Validation;
    using Volo.Saas.Tenants;
    using IdentityUser = Volo.Abp.Identity.IdentityUser;
    
    namespace CFData.Structure.Web.Pages.Account;
    
    [DisableAuditing]
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(MyLoginModel), typeof(LoginModel))]
    public class MyLoginModel : LoginModel
    {
        [Inject]
        public ICurrentTenantAccessor CurrentTenantAccessor { get; set; }
    
        [Inject]
        public ITenantStore TenantStore { get; set; }
    
        private readonly ITenantRepository _tenantRepository;
        protected IDataFilter DataFilter { get; }
    
        public MyLoginModel(
            IAuthenticationSchemeProvider schemeProvider, 
            IOptions accountOptions, 
            IAbpRecaptchaValidatorFactory recaptchaValidatorFactory, 
            IAccountExternalProviderAppService accountExternalProviderAppService, 
            ICurrentPrincipalAccessor currentPrincipalAccessor, 
            IOptions identityOptions, 
            IOptionsSnapshot reCaptchaOptions,
            ITenantRepository tenantRepository,
            DataFilter dataFilter) : base(
                schemeProvider, 
                accountOptions, 
                recaptchaValidatorFactory, 
                accountExternalProviderAppService, 
                currentPrincipalAccessor, 
                identityOptions, 
                reCaptchaOptions)
        {
            //ReCaptchaOptions = reCaptchaOptions;
            _tenantRepository = tenantRepository;
            DataFilter = dataFilter;
        }
    
        public override async Task OnGetAsync()
        {
            return await base.OnGetAsync();
        }
    
        [UnitOfWork] //TODO: Will be removed when we implement action filter
        public override async Task OnPostAsync(string action)
        {
            try
            {
                await ReCaptchaVerification();
            }
            catch (UserFriendlyException e)
            {
                if (e is ScoreBelowThresholdException)
                {
                    var onScoreBelowThresholdResult = OnRecaptchaScoreBelowThreshold();
                    if (onScoreBelowThresholdResult != null)
                    {
                        return await onScoreBelowThresholdResult;
                    }
                }
    
                Alerts.Danger(GetLocalizeExceptionMessage(e));
                return Page();
            }
    
            //ValidateModel();
    
            await IdentityOptions.SetAsync();
    
            var localLoginResult = await CheckLocalLoginAsync();
            if (localLoginResult != null)
            {
                return localLoginResult;
            }
    
            IsSelfRegistrationEnabled = false; //await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled);
    
    
            await ReplaceEmailToUsernameOfInputIfNeeds();
    
    
            var getUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
            if (getUser == null)
            {
                Alerts.Danger("User does not exist");
                return Page();
            }
    
            TenantConfiguration tenant = null;
            if (getUser.TenantId is not null)
            {
                tenant = await TenantStore.FindAsync((Guid)getUser.TenantId);
            }
    
            using (CurrentTenant.Change(tenant?.Id))
            {
                IsLinkLogin = await VerifyLinkTokenAsync();
    
                var result = new Microsoft.AspNetCore.Identity.SignInResult();
    
                using (DataFilter.Disable())
                {
                    result = await SignInManager.PasswordSignInAsync(
                    LoginInput.UserNameOrEmailAddress,
                    LoginInput.Password,
                    LoginInput.RememberMe,
                    true);
    
                    await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                    {
                        Identity = IdentitySecurityLogIdentityConsts.Identity,
                        Action = result.ToIdentitySecurityLogAction(),
                        UserName = LoginInput.UserNameOrEmailAddress
                    });
                }
    
                if (result.RequiresTwoFactor)
                {
                    return RedirectToPage("./SendSecurityCode", new
                    {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash,
                        rememberMe = LoginInput.RememberMe,
                        linkUserId = LinkUserId,
                        linkTenantId = LinkTenantId,
                        linkToken = LinkToken
                    });
                }
    
    
                if (result.IsLockedOut)
                {
                    var lockedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
                    await StoreLockedUser(lockedUser);
                    return RedirectToPage("./LockedOut", new
                    {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash
                    });
                }
    
                if (result.IsNotAllowed)
                {
                    var notAllowedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
    
                    if (!await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password))
                    {
                        Alerts.Danger(L["InvalidUserNameOrPassword"]);
                        return Page();
                    }
    
                    if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser))
                    {
                        await StoreChangePasswordUser(notAllowedUser);
                        return RedirectToPage("./ChangePassword", new
                        {
                            returnUrl = ReturnUrl,
                            returnUrlHash = ReturnUrlHash,
                            RememberMe = LoginInput.RememberMe
                        });
                    }
    
                    if (notAllowedUser.IsActive)
                    {
                        await StoreConfirmUser(notAllowedUser);
                        return RedirectToPage("./ConfirmUser", new
                        {
                            returnUrl = ReturnUrl,
                            returnUrlHash = ReturnUrlHash
                        });
                    }
    
                    Alerts.Danger(L["LoginIsNotAllowed"]);
                    return Page();
                }
    
                if (!result.Succeeded)
                {
                    Alerts.Danger(L["InvalidUserNameOrPassword"]);
                    return Page();
                }
    
                var user = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
    
    
                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 }
                                        }
                                });
                            }
                        }
    
                        return RedirectToPage("./LinkLogged", new
                        {
                            returnUrl = ReturnUrl,
                            returnUrlHash = ReturnUrlHash,
                            targetLinkUserId = LinkUserId,
                            targetLinkTenantId = LinkTenantId
                        });
                    }
                }
    
                // Clear the dynamic claims cache.
                await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
    
                return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
    
            }
        }
    
        protected override async Task ReplaceEmailToUsernameOfInputIfNeeds()
        {
            if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress))
            {
                return;
            }
    
            var userByUsername = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
            if (userByUsername != null)
            {
                return;
            }
    
            var userByEmail = await FindUserAsync(LoginInput.UserNameOrEmailAddress);
            if (userByEmail == null)
            {
                return;
            }
    
            LoginInput.UserNameOrEmailAddress = userByEmail.UserName;
        }
    
        protected override async Task GetIdentityUser(string userNameOrEmailAddress)
        {
            IdentityUser identityUser = null;
            using (CurrentTenant.Change(null))
            {
                identityUser = await base.UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress);
                if (identityUser == null)
                {
                    identityUser = await base.UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
                }
            }
    
            IdentityUser user = identityUser;
            Debug.Assert(user != null, "user != null");
            return user;
        }
    
        protected virtual async Task FindUserAsync(string uniqueUserNameOrEmailAddress)
        {
            IdentityUser user = null;
            using (CurrentTenant.Change(null))
            {
                user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
                       await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
    
                if (user != null)
                {
                    return user;
                }
            }
    
            foreach (var tenant in await _tenantRepository.GetListAsync())
            {
                using (CurrentTenant.Change(tenant.Id))
                {
                    user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
                           await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
    
                    if (user != null)
                    {
                        return user;
                    }
                }
            }
    
            return null;
        }
    
    }
    
  • User Avatar
    0
    Spospisil created

    This still has not worked. I've given you access to our forked source code where this an issue. The branch name 'Integration-BugFixes' in this forked report is the branch that has the code you want to look at.

    Thanks.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share the steps to reproduce your problem with the Integration-BugFixes code?

    Thanks.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on June 13, 2025, 11:37