Activities of "Spospisil"

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";
        });
}

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;

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.

I get the following message in my blazor webassembly app when I go to any abp page that has a blazorise component.

I am using ABP 9.1.0 and Blazorise 1.7.5

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Method not found: string Blazorise.IClassProvider.TableRowHoverCursor() System.MissingMethodException: Method not found: string Blazorise.IClassProvider.TableRowHoverCursor() at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[<HandleVirtualize>d__47](<HandleVirtualize>d__47& stateMachine) at Blazorise.DataGrid.DataGrid1[[Volo.Saas.Host.Dtos.SaasTenantDto, Volo.Saas.Host.Application.Contracts, Version=9.1.0.0, Culture=neutral, PublicKeyToken=null]].HandleVirtualize() at Blazorise.DataGrid.DataGrid1.<OnAfterRenderAsync>d__42[[Volo.Saas.Host.Dtos.SaasTenantDto, Volo.Saas.Host.Application.Contracts, Version=9.1.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()

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;
    }

}

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

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.

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

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.

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

Showing 51 to 60 of 375 entries
Learn More, Pay Less
33% OFF
All Trainings!
Get Your Deal
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on September 12, 2025, 10:20