One user login at a time

Hi, I would like to know if it’s possible to implement a single login per user at a time.
I explain: user1 logins with his/her credentials… (I set GenerateNewSessionCookiesOnAuthentication = false so there aren’t cookies returned but just the SessionId and I am fine with this), if user2 logins with the same credentials of user1 I would invalidate user1.

I would implement it this way: on each succesfull login I would save the SessionId in my db user row and then, on each request, I would compare the SessionId from the request with the one stored in the db and act consequently…

I know that ServiceStack can’t handle this logic by default so I will try to implement it on my own… I’m just asking an advice about any pitfalls with my solution.

Thank you!

To invalidate the existing Session you would need to remove it from the ICacheClient, e.g:

var sessionKey = SessionFeature.GetSessionKey(sessionId);
cache.Remove(sessionKey);

Thank you mythz as always!

Where should I put the code to store the SessionId as soon as the user is authenticated?

On each request then… where should I check the SessionId? Should I use a filter or there is an Auth function that I can override?

Please see the OnAuthenticated() Session and Auth Events.

Hi mythz I’m trying to save data from OnAuthenticated to the db using the SocialBootstrapApi example as reference but I got a strange error, this is my code:

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

            var user = session.ConvertTo<UserAuth>();
            user.Id = int.Parse(session.UserAuthId);
            user.FirstName = "some test data to save";
            user.LastName = "foobar";

            using (var db = authService.TryResolve<IDbConnectionFactory>().Open())
            {
                db.Save(user);
            }
        }
    }

I got this exception and no data saved:

An exception of type 'System.Data.SqlTypes.SqlTypeException' occurred in System.Data.dll but was not handled in user code

Additional information: SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM. 

On this line:

db.Save(user);

It seems there are some problems with DateTime type vars… they are all like: ModifiedDate {01/01/0001 00:00:00} could you help me to find the mistake, please?

Thank you!

You can’t save a empty DateTime value type in SQL Server because it tries to save it as the minimum date value which SQL Server doesn’t support. Normally you would use DateTime?, but since the schema is fixed you should make sure all the DateTime fields have a value, e.g. DateTime.UtcNow.

mm… I changed it like this:

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

        var user = session.ConvertTo<UserAuth>();
        user.Id = int.Parse(session.UserAuthId);
        user.CreatedDate = session.CreatedAt;
        user.ModifiedDate = session.LastModified;
        user.FirstName = "some test data to save";
        user.LastName = "foobar";

        using (var db = authService.TryResolve<IDbConnectionFactory>().Open())
        {
            db.Save(user);
        }
    }
}

but it seems that I have a most important problem now… it overwrites every field in my user row keeping only the above specified ones… why it can’t just update them without touching everything else?

Save/Update has always inserted or updated the entire record, you need to use another API like UpdateNonDefaults() if you only want to update fields with non default values otherwise you can use UpdateOnly() and specify exactly which fields you want to update.

1 Like

I’ve just implemented this feature. Instead of storing SessionIds against users, I’m just removing all existing sessions for the user. It seems to be working for me YMMV :smile:

I’ll leave my implementation here, maybe it will help someone.

public override void OnAuthenticated(IRequest httpReq,
                                         IAuthSession session,
                                         IServiceBase authService,
                                         IAuthTokens tokens,
                                         Dictionary<string, string> authInfo)
    {
        using (var cache = authService.TryResolve<ICacheClient>())
        {
            var sessionPattern = IdUtils.CreateUrn<IAuthSession>("");
            var sessionKeys = cache.GetKeysStartingWith(sessionPattern).ToList();
            var allSessions = cache.GetAll<IAuthSession>(sessionKeys);
            var existingSessions = allSessions.Where(s => s.Value.UserName == session.UserName);
            cache.RemoveAll(existingSessions.Select(s => s.Key));
        }
    }
3 Likes