Understanding Authentication

I’m having lots of problems with my custom authentication providers, and I think it’s due to my lack of understanding of how they work. Our app is a React application, with a ServiceStack backend.

In my CustomCredentialsAuthProvider, we load additional data into our CustomAuthUserSession. It’s there in the db. I see it in the CacheEntry table in the database. When calling services that use SessionAs() - sometimes the data is populated and sometimes it’s not. Sometimes the provider on CustomAuthUserSession says credentials, sometimes it says jwt. Right now I can look in the database and see a CacheEntry with my roles, permissions, and two custom fields (agencyId and stateAbbreviation), with a provider of credentials.

Yet when I hit my endpoint and call SessionAs(), it says my provider is jwt. It has the roles, permissions, firstName, lastName populated - but not the agencyId or stateAbbreviation. I’ve resorted to using GetSession(true) which works sometimes, but not always. What gives? Am I just misunderstanding how the bearerToken / refreshToken should be used?

Here is our auth process:

User enters email / password, we call this code to POST the new Authenticate() TS DTO:

const request = new Authenticate();
  request.provider = 'credentials';
  request.userName = email;
  request.password = password;
  const data = await client.post(request);
  setToken(data.bearerToken, data.refreshToken); // this sets the token on our client for all future calls
  return data;

You see that we set the token afterwards for all future calls.

Here is my OnAuthenticated method of CustomCredentialsAuthProvider:

public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
        {
            var dbFactory = authService.ResolveService<IDbConnectionFactory>();

            using (var db = dbFactory.Open())
            {
                var user = db.LoadSelect<User>(x => x.Email.ToLower() == session.UserAuthName.ToLower()).FirstOrDefault();

                var state = StateEnum.FromValue(user.Agency.PrimaryStateId.Value);

                var roles = LoadPermissionGroups(db, user) ?? new List<string>();
                var permissions = LoadPermissions(db, user) ?? new List<string>();

                session.UserAuthId = user.Id.ToString();
                session.FirstName = user.FirstName;
                session.LastName = user.LastName;
                session.DisplayName = user.FirstName + " " + user.LastName;
                session.Email = user.Email;
                session.UserName = user.Email;
                session.Roles = roles;
                session.Permissions = permissions;

				(session as CustomAuthUserSession).AgencyId = user.AgencyId;
				(session as CustomAuthUserSession).StateAbbreviation = state.Abbreviation;
            }

            return base.OnAuthenticated(authService, session, tokens, authInfo);
        }

All of this is duplicated in a CustomBasicAuthProvider and CustomJwtAuthProvider. The BasicAuthProvider is currently only used for swagger and will be removed. I added CustomJwtAuthProvider in an attempt to get these values to populate to no avail.

Is there some conversion between credentials and jwt that I’m missing? Why do I intermittently get values for stateAbbreviation and agencyId?

JWT by default only embeds minimal info into the JWT Cookie which when JWT GetSession() will only return the session in the JWT Token instead of the UserSession saved in the cache. See the JWT docs below on using CreatePayloadFilter to save additional info into the JWT and using PopulateSessionFilter to populate the same fields from the UserSession from the JWT.

http://docs.servicestack.net/jwt-authprovider#modifying-the-payload

When you authenticate you can use UseTokenCookie=true to also return the JWT in the ss-tok Cookie and remove the existing UserSession so it will only be using the UserSession from the JWT:

const request = new Authenticate();
//...
request.useTokenCookie = true;

const data = await client.post(request);. //client is populated with ss-tok cookie
1 Like

Yep, that did it. Thank you!

So I should be able to remove all of my explicit GetSession(true) as CustomAuthUserSession calls, and just use SessionAs() now aye?

Yeah you only need to get the session once and SessionAs<T>() returns the typed session.

1 Like