Base solution for your next web application
Open Closed

Tenant Id null when refreshtoken is requested. #8565


User avatar
0
cangunaydin created

Hello, I have a problem while refresh token is requested. This is happening in mobile but i believe it is independent from mobile app. Basically the problem is in the backend. If the user is logged in, data storage of the mobile app is asking directly to get the configuration from the server. Then the request is coming to

 public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
 

here principal is generated. So there is no problem over here. It has useridentifier key in the claims. And if you look at the settingManager.abpSession it has tenantId. you can see it in the snapshot.

everything works fine until now. Then when it tries to call ValidateSecurityStampAsync() it says that There is no user with the id 3. Cause it is trying to validate the principal in ValidateSecurityStampAsync()

 if (!await securityStampHandler.Object.Validate(principal))

and when it tries to parse the identifier it gets the tenantid as null. you can look at the snapshot.

and afterwards it tries to call ValidateSecurityStampFromDb() in JwtSecurityStampHandler Class here is the method.

private async Task<bool> ValidateSecurityStampFromDb(UserIdentifier userIdentifier, string securityStamp)
        {
            using (var uow = _unitOfWorkManager.Begin())
            {
                using (_unitOfWorkManager.Current.SetTenantId(userIdentifier.TenantId))
                {
                    var user = _userManager.GetUser(userIdentifier);
                    uow.Complete();

                    //cache last requested value
                    await SetSecurityStampCacheItem(userIdentifier.TenantId, userIdentifier.UserId, user.SecurityStamp);

                    return await _signInManager.ValidateSecurityStampAsync(user, securityStamp);
                }
            }
        }

Now when the tenantid is null it won't find the user for sure. Cause the user is related to Tenant Id=2 not to null. I think this is a kind of bug or somehow i am missing sth. that should be done before UserIdentifier.parse() method. Can you help me out pls?


8 Answer(s)
  • User Avatar
    0
    musa.demir created

    Hello @cangunaydin

    Can you please check if your securityToken contains tenant id. You can use https://jwt.io/ to decode your token.

  • User Avatar
    0
    musa.demir created

    By the way I removed setting manager because it is unused. Commit: https://github.com/aspnetzero/aspnet-zero-core/commit/84b485491570e149c5b075ae15a7579baffb3385

  • User Avatar
    0
    cangunaydin created

    Hello @demirmusa it contains, "http://www.aspnetboilerplate.com/identity/claims/tenantId": "2",

    but not tenantid particularly i don't know how should it be.

    here is the data.

    {
      "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "3",
      "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "admin",
      "AspNet.Identity.SecurityStamp": "HJKZNO7WXIZIS7YACAQFM7XJVIJ5FDJH",
      "http://www.aspnetboilerplate.com/identity/claims/tenantId": "2",
      "sub": "3",
      "jti": "4d75b067-beff-4df3-a647-15e48d9a8c99",
      "iat": 1582989348,
      "token_validity_key": "1471862d-c31a-4f39-88f0-4bf51d0346f2",
      "user_identifier": "3",
      "token_type": "0",
      "nbf": 1582989348,
      "exp": 1583075748,
      "iss": "BookAndAd",
      "aud": "BookAndAd"
    }
    
  • User Avatar
    0
    musa.demir created

    Here is my token

    {
      "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "2",
      "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "admin",
      "AspNet.Identity.SecurityStamp": "4e84f937-ceaf-d829-6bad-39f36f3eacfe",
      "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin",
      "http://www.aspnetboilerplate.com/identity/claims/tenantId": "1",
      "sub": "2",
      "jti": "846b530b-aa40-4388-97e3-5c187dfbc33d",
      "iat": 1583127952,
      "token_validity_key": "5f248bd4-dff0-492b-9b91-1f6efd60b205",
      "user_identifier": "2@1",//it also has tenant id
      "token_type": "0",
      "nbf": 1583127952,
      "exp": 1583214352,
      "iss": "AbpZeroTemplate",
      "aud": "AbpZeroTemplate"
    }
    

    Can you please check that lines, https://github.com/aspnetzero/aspnet-zero-core/blob/78674ac37d4ff8f19a860a1146f9ca96a5989406/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Controllers/TokenAuthController.cs#L681 https://github.com/aspnetzero/aspnet-zero-core/blob/78674ac37d4ff8f19a860a1146f9ca96a5989406/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Web.Core/Controllers/TokenAuthController.cs#L688

  • User Avatar
    0
    cangunaydin created

    Hello again, There is nothing wrong with those lines. They are identical yes. What i have found out is when i first login to the mobile app. it sends the useridentifier with userid@tenantid. But after some time. Probably when the token is expired in the mobile project first it goes to UserConfigurationManager.cs

     public static async Task GetAsync(Func<Task> successCallback = null)
            {
                var userConfigurationService = DependencyResolver.IocManager.Resolve<UserConfigurationService>();
                userConfigurationService.OnAccessTokenRefresh = App.OnAccessTokenRefresh;
                userConfigurationService.OnSessionTimeOut = App.OnSessionTimeout;
    
                await WebRequestExecuter.Execute(
                    async () => await userConfigurationService.GetAsync(AccessTokenManager.IsUserLoggedIn),
                    async result =>
                    {
                        AppContext.Value.Configuration = result;
                        SetCurrentCulture();
                        if (!result.MultiTenancy.IsEnabled)
                        {
                            AppContext.Value.SetAsTenant(TenantConsts.DefaultTenantName, TenantConsts.DefaultTenantId);
                        }
    
                        AppContext.Value.CurrentLanguage = result.Localization.CurrentLanguage;
                        WarnIfUserHasNoPermission();
                        if (successCallback != null)
                        {
                            await successCallback();
                        }
                    },
                    _ =>
                    {
                        App.ExitApplication();
                        return Task.CompletedTask;
                    },
                    () => DependencyResolver
                        .IocManager
                        .Release(userConfigurationService)
                );
            }
    

    method. and since it seems like AccessTokenManager.IsUserLoggedIn is true. it is calling the method below.

    public async Task<AbpUserConfigurationDto> GetAsync(bool isUserLoggedIn)
            {
                return isUserLoggedIn
                    ? await GetAuthenticatedUserConfig()
                    : await _apiClient.GetAnonymousAsync<AbpUserConfigurationDto>(Endpoint);
            }
    

    And it tries to get GetAuthenticatedUserConfig() and it is refreshing the token with this code i believe from AccessTokenManager.cs and afterwards in this token there is no TenantId comes with @ that's my theory i couldn't really dig deep into that i will debug it since it needs to pass time to reproduce the error, it can be tricky to recreate the same behavior. Do you have any idea or suggestion?

     public async Task<string> RefreshTokenAsync()
            {
                if (AuthenticateResult == null ||
                    string.IsNullOrWhiteSpace(AuthenticateResult.RefreshToken))
                {
                    throw new ApplicationException("No refresh token!");
                }
    
                using (var client = CreateApiClient())
                {
                    var response = await client.Request(RefreshTokenUrlSegment)
                        .PostUrlEncodedAsync(new { refreshToken = AuthenticateResult.RefreshToken })
                        .ReceiveJson<AjaxResponse<RefreshTokenResult>>();
    
                    if (!response.Success)
                    {
                        AuthenticateResult = null;
                        throw new UserFriendlyException(response.Error.Message + ": " + response.Error.Details);
                    }
    
                    AuthenticateResult.AccessToken = response.Result.AccessToken;
                    AccessTokenRetrieveTime = DateTime.Now;
    
                    return response.Result.AccessToken;
                }
            }
    
  • User Avatar
    0
    alper created
    Support Team

    I'll check it.

  • User Avatar
    0
    alper created
    Support Team

    We have reproduced the problem and fixed the issue 👍

    • Issue: https://github.com/aspnetzero/aspnet-zero-core/issues/3056
    • Fix: https://github.com/aspnetzero/aspnet-zero-core/commit/434103691c7282d2f243d2291643ae4d0901cf44
  • User Avatar
    0
    twopenguinsstudios created

    Where can we see the fix. This is the same exact issue I reported a few months ago. The github links do not work.

    https://support.aspnetzero.com/QA/Questions/8833/Multi-Tenant-Issues-after-build-agents-upgraded-to-31