Custom User Session and JwtAuthProvider

Is there anyway I can use a CustomUserSession as well as a JwtAuthProvider? The CustomUserSession works great by itself but doesnt when I include the JwtAuthProvider as all the values are empty.

Could you provide a code/project so we can replicate the issue?

I’ve created a new project from the vue-spa project and added the JwtAuthProvider (it already uses a CustUserSession). Authenticating with credentials and then using the Jwt bearer token and returning the session I can see the values that are populated from the UserAuth (example admin user).

Depending on your AuthRepository setup, you might need to have a look at modifying JWT payloads, but if you can post a minimal reproduction of your issue and replicate what you are seeing ourselves, we can likely help out. Cheers.

Its going to be hard to make a minimal repo as this is all running in aws in API gateway. The current auth setup is this.

Plugins.Add(new AuthFeature(() => new CustomUserSession(),
                new IAuthProvider[] {
                    new MicrosoftGraphAuthProvider(azureAdOauthSettings),
                    new JwtAuthProvider(AppSettings) {
                        UseTokenCookie = true,
                        AuthKey = secretsManager.Get("ServiceStack_JwtAuthKey").ToUtf8Bytes(),
                        IncludeJwtInConvertSessionToTokenResponse = true,
                        PersistSession = true
                    }
                }));

We are registering an IPocoDynamo and then using a DynamoDbAuthRepository and a DynamoDbCacheClient. With the JwtAuthProvider commented out the CacheEntry table gets the session written to with the custom values populated. When the jwt provider is included its not written reguardless of what PersistSession is set to and the values added to the custom session are empty.

It sounds like something is getting stripped before it gets to your ServiceStack instance or as it returns to the client. Can you successfully authenticate via JWT at all? Can you share client code/details?

Remembering that none of your code is actually hosted “in” API Gateway, it is a managed service that sits between your application (hosted somewhere like Lambda/ECS/EC2 etc) and the outside world/client. If you are using other layers like CloudFront that could also be playing a role.

Can you share how the client is authenticating/passing the JWT token? Is it trying to use a cookie every request after auth or passing using the Authorize header or? If it is passing through CloudFront, the associated cookie will have to be on the allow list. I believe API Gateway can do the same depending on how it is configured. Same with headers but Authorize I think that usually by default passed through, and would assume so if other AuthProviders are working.

If you can share any more details about your AWS setup, I might be able to try and replicate, but if you could first confirm the payload with the token is being received by your ServiceStack instance and if it is that would help narrow down possible root causes/issues. If it is running in Lambda, you should be able to get access to the raw payload, if Lambda is running a different tech as just a proxy, then you’ll probably need to use EnableBuffering to see the raw request.

So I am authenticating with a session cookie, basically after I get redirected back to our website from the oauth process I just tell my client to useTokenCookie and I make a call to ensure im authenticated. This works fine. The AWS setup is an api gateway backed by a single lambda proxy that hosts the api. All of the cookies seem to be flowing through as expected and I can auth normally. At this point I was able to use PopulateSessionFilter based on your post above to accomplish what I need. I just am curious as to why I cant get the custom session to work without doing this. Thanks for the assistance!

Glad you’ve got something working. I think this has given me enough info to try and replicate the issue, could you share the declaration of your CustomUserSession as well? I assume it is just inheriting off AuthUserSession with some custom properties? Also just to confirm what version of ServiceStack your Lambda is running? Thanks!

Using ServiceStack 5.11.0 and here is the code for my custom user session:

[DataContract]
    public class CustomUserSession : AuthUserSession
    {
        [DataMember] public Guid WorkloadId { get; set; }
        [DataMember] public string SecurityGroupId { get; set; }
        [DataMember] public string HostAddress { get; set; }
        //[DataMember] public string CustomCompany { get; set; }


        public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
        {
            var workstationDetailsRepository = authService.TryResolve<IWorkstationDetailsRepository>();
            var workstationDetails = workstationDetailsRepository.GetWorkstationDetailsByAdEmail(session.Email);

            if (workstationDetails != null)
            {
                this.WorkloadId = workstationDetails.WorkloadId;
                this.SecurityGroupId = workstationDetails.SecurityGroupId;
                this.HostAddress = workstationDetails.HostAddress;
                this.Company = workstationDetails.Company;
            }
        }
    }

Thanks for sharing those details. The UseTokenCookie = true in your JwtAuthProvider changes the way sessions are handled from being persisted in cache to being stateless. Given JWT only contains essential limited information, the rest of the data needs to be resolved from the database as required.

Your change to use the PopulateSessionFilter (or CreatePayloadFilter ) is how additional data is added to the stateless token payload, so what you’ve got is the right way to resolve your issue :+1:. Ideally only security related data will live on stateless token to keep it small, so your use of SecurityGroupId etc makes sense be a part of your PopulateSessionFilter.

I missed these details in the docs as well and didn’t put it together until I was able to reproduce what you were seeing and realized the use of UseTokenCookie was a part of your server config. To try and help this, the docs have been updated in several places to make this clearer for those who use custom user/session data since the use of JWT and UseTokenCookies changes the way sessions are handled. You can detect when the token was used to populate the session via the session.FromToken property.

Hopefully that has cleared up why the use of those filters on the JwtAuthProvider are opt-in and additional fields need to be manually mapped to be included in the stateless token as to avoid unintended bloat in the token.

Thanks for the update and the assistance, great how fast you guys respond.

1 Like