CustomAuthUserSession doesn't populate custom fields when calling back into API

I’ve created a CustomAuthUserSession with a custom field:

public class CustomAuthUserSession : AuthUserSession
{
    public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
    {
        base.OnAuthenticated(authService, session, tokens, authInfo);

        var customSession = session as CustomAuthUserSession; // AgencyId == 1 right here, yay!

        if (customSession == null)
            return;
    }

    public int AgencyId { get; set; }
}

In my CustomAuthProvider I set the agency Id in the OnAuthenticated method:

(session as CustomAuthUserSession).AgencyId = user.AgencyId; // AgencyId = 1 (set AgencyId)

In my service (MeService), AgencyId is 0:

public class MeService : Service
{
    public GetMeResponse Get(GetMe request)
    {
        var session = this.GetSession() as CustomAuthUserSession;

        return new GetMeResponse()
        {
            Email = session.Email,
            FirstName = session.FirstName,
            LastName = session.LastName,
            Roles = session.Roles,
            Permissions = session.Permissions,
            AgencyId = session.AgencyId // This is 0?!
        };
    }
}

Why is AgencyId 0 when I call the MeService to pull the users information?

I can even see in the CacheEntry table in the DB that AgencyId is 1.

Since AuthUserSession is a DataContract, your additional properties need to be annotated with:

[DataMember]
public int AgencyId { get; set; }

You need to register your CustomAuthUserSession in your AuthFeature plugin:

Plugins.Add(new AuthFeature(() => new CustomAuthUserSession(), ....));

Then you should use the Typed API to resolve your session, i.e:

var session = this.SessionAs<CustomAuthUserSession>();

I’ve made the requested changes but it still comes back as 0.

new AuthFeature(() => new CustomAuthUserSession(), new IAuthProvider[]

// Startup.cs

[DataMember]
public int AgencyId { get; set; }

// CustomAuthUserSession.cs

  var session = this.SessionAs<CustomAuthUserSession>();

// MeService.cs

The Session is saved at the end of OnAuthenticated(), so your code that updates the session needs to be called before calling base.OnAuthenticated():

((CustomAuthUserSession)session).AgencyId = 1;
return base.OnAuthenticated(authService,session,tokens,authInfo);

Hrmmm, yeah that is in the right spot:

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 roles = LoadRoles(db, user);
                var permissions = LoadPermissions(db, user);

                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;
            }
            return base.OnAuthenticated(authService, session, tokens, authInfo);
        }

If I change the call to

var session = this.GetSession(true) as CustomAuthUserSession;

With reload session as true, it seems to populate.

Ok than it should work, are you sure user.AgencyId has the correct value? Can you check that inside your ICacheClient the value for the key urn:iauthsession:{session.Id} after OnAuthenticated() is called to see if AgencyId is saved with the right value?

Yes, I can debug in the immediate window:

cacheClient.Get<CustomAuthUserSession>("urn:iauthsession:phR7iDkyGVzxrMvnyHhB")
{MyApp.Logic.Services.CustomAuthUserSession}
    Address: null
    Address2: null
    AgencyId: 1
    AuthProvider: "credentials"
    BirthDate: null
    BirthDateRaw: null
    City: null
    Company: null
    Country: null
    CreatedAt: {3/8/2018 8:34:31 AM}
    Culture: null
    DisplayName: "Admin istrator"
    Email: "....."
    FacebookUserId: null
    FacebookUserName: null
    FirstName: "Admin"
    FromToken: false
    FullName: null
    Gender: null
    Id: "phR7iDkyGVzxrMvnyHhB"
    IsAuthenticated: true
    Language: null
    LastModified: {3/8/2018 9:14:47 AM}
    LastName: "istrator"
    MailAddress: null
    Meta: Count = 0
    Nickname: null
    Permissions: Count = 5
    PhoneNumber: null
    PostalCode: null
    PrimaryEmail: null
    ProfileUrl: null
    ProviderOAuthAccess: Count = 0
    ReferrerUrl: null
    RequestTokenSecret: null
    Roles: Count = 1
    Sequence: null
    State: null
    Tag: 0
    TimeZone: null
    TwitterScreenName: null
    TwitterUserId: null
    UserAuthId: "1"
    UserAuthName: "...."
    UserName: "...."

It looks like in the SessionFeature.GetOrCreateSession calls httpReq.GetSession(false), then just returns session if session is T

 public static T GetOrCreateSession<T>(ICacheClient cache = null, IRequest httpReq = null, IResponse httpRes = null)
        {
          if (httpReq == null)
            httpReq = HostContext.GetCurrentRequest();
          IAuthSession session = httpReq.GetSession(false);
          if (session is T)
            return (T) session; /// returns here, AgencyId is 0
          string sessionId = httpReq.GetSessionId();
          string sessionKey = SessionFeature.GetSessionKey(sessionId);
          if (sessionKey != null)
          {
            T obj = (cache ?? httpReq.GetCacheClient()).Get<T>(sessionKey);
            if (!object.Equals((object) obj, (object) default (T)))
              return (T) HostContext.AppHost.OnSessionFilter((IAuthSession) (object) obj, sessionId);
          }
          return (T) SessionFeature.CreateNewSession(httpReq, sessionId);
        }

Any idea why I must force a refresh to make it work?

Do you have any custom JsConfig?

Yes, at the bottom of AppHost.Configure:

JsConfig.DateHandler = DateHandler.ISO8601;

Yeah that’s not going to affect it, can you print out the JSON returned from:

var sessionKey = SessionFeature.GetSessionKey(session.Id);
var json = httpReq.GetCacheClient().Get<string>(sessionKey);

Although I’d prefer to see the raw JSON value of sessionKey from your CacheClient. BTW which cache client are you using?

We are using OrmLiteCacheClient for now

        container.Register<ICacheClient>(new OrmLiteCacheClient()
        {
            DbFactory = container.Resolve<IDbConnectionFactory>()
        });

I’ve modified my CustomCredentialsProvider.OnAuthenticated with the following:

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 roles = LoadRoles(db, user);
                var permissions = LoadPermissions(db, user);

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

            var b = base.OnAuthenticated(authService, session, tokens, authInfo);

            var sessionKey = SessionFeature.GetSessionKey(session.Id);
            var json = authService.Request.GetCacheClient().Get<string>(sessionKey);

            return b;
        }

The results of json are:

"{__type:\"MyApp.Logic.Services.CustomAuthUserSession, MyApp.Logic\",agencyId:1,id:izT1lw7oJcMjVndV2l73,userAuthId:1,userAuthName:me@me.com,userName:admin@me.com,firstName:Admin,lastName:istrator,displayName:Admin istrator,email:me@me.com,createdAt:2018-03-08T09:22:56.6411306-06:00,lastModified:2018-03-08T19:12:49.2408675Z,roles:[Admin],permissions:[View Dashboard,View Dashboard Packets,View Dashboard Intel,View Dashboard Seizures,View Dashboard Investigations],isAuthenticated:True,fromToken:False,tag:0,authProvider:credentials,providerOAuthAccess:[]}"

Pretty:

{
  __type: \"MyApp.Logic.Services.CustomAuthUserSession,
   MyApp.Logic\",
  agencyId: 1,
  id: izT1lw7oJcMjVndV2l73,
  userAuthId: 1,
  userAuthName: me@me.com,
  userName: me@me.com,
  firstName: Admin,
  lastName: istrator,
  displayName: Admin istrator,
  email: me@me.com,
  createdAt: 2018-03-08T09: 22: 56.6411306-06: 00,
  lastModified: 2018-03-08T19: 12: 49.2408675Z,
  roles: [
    Admin
  ],
  permissions: [
    View Dashboard
  ],
  isAuthenticated: True,
  fromToken: False,
  tag: 0,
  authProvider: credentials,
  providerOAuthAccess: [
    
  ]
}

ok everything looks right there, so I don’t see how the Session wouldn’t contain all the info serialized and why you you’d need to do a refresh. Which just means to force reload the ICacheClient instead of using the session that’s already cached in the IRequest for that request.

The first time the Session is retrieved for the request it needs to fetch from the ICacheClient, but later on for the same request other requests for the Users Session will return the cached session for that request unless you call httpReq.GetSession(false) which forces a refresh.

The place to look is to inspect how the session is first deserialized for the request. Firstly do you have any custom code that’s populating httpReq.Items[Keywords.Session]? And are you using any other AuthProviders (i.e. in addition to your Custom AuthProvider like JWT), if so the session that’s attached to the ss-tok Cookie will be used for that request. I’d start with clearing all your existing Cookies to make sure you don’t have anything else interfering with it.

If you still can’t find the issue, try overriding OnSessionFilter() in your AppHost which gets called after the Session is retrieved from the ICacheClient or for new Sessions. If a Session doesn’t contain the correct values can you print out the Environment.StackTrace property so we can see where the code to retrieve the session is first being called.

That was it. I have a custom CredentialsAuthProvider, but registered a base JwtAuthProvider and even a BasicAuthProvider. It looks like the JwtAuthProvider was getting a hold of the session and doing something (clearing out my AgencyId). I guess I will need to customize the JwtAuthProvider to populate the same information?

The JWT token includes a minimal self-encapsulated User Session, if you need more info embedded in the JWT you need to use CreatePayloadFilter to embed the info into the JWT payload and PopulateSessionFilter to populate the UserSession with the custom properties embedded in the JWT.

1 Like