2
votes

I am currently fighting a bit with my custom CredentialsAuthProvider implementation. First it is important to say, that I am writing a WPF client as a reference for my API.

  1. A browser stores cookies and you can configure how to deal with them, e.g. delete when the browser is closed. On windows desktop you have Environment.SpecialFolder.Cookies where Windows stores cookies. But I could not find anything from ServiceStack. So does it not store anything on a Windows Desktop app? I saw there is a client.CookieContainer where I find three cookies after login.
  2. Can I somehow add properties to this cookie during Authentication? If so how? Currently I use AuthenticationResponse.MetaDictionary to transfer additional information:

    public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
    {
        var authResponse = (AuthenticateResponse)base.Authenticate(authService, session, request);
        authResponse.Meta = new Dictionary<string, string>();
        authResponse.Meta.Add("Test", "TestValue");
        return authResponse;
    }
    
  3. And finally: Is an instance of my derived CredentialsAuthProvider class thread safe? In TryAuthenticate(...) I make a DB connection and retrieve an object which contains all information including hashed password etc. But I can only fill this information to the session object in OnAuthenticated(....) and/or overridden Authenticate(...). If possible I do not want to make another DB call to retrieve the same object again. So is it safe to declare a member user fill it in TryAuthenticate and reuse it in other overwritten methods like so:

    public class BediCredentialsAuthProvider : CredentialsAuthProvider
    {
        private AppUser user = null;
    
        public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
        {
            var authResponse = (AuthenticateResponse)base.Authenticate(authService, session, request);
            authResponse.Meta = new Dictionary<string, string>();
            authResponse.Meta.Add("ValueA", user.ValueA);
            // ... add more properties from user object
            return authResponse;
         }
    
         public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
         {
            AppUser user = null;
            using (var session = NhSessionFactories.OpenSession(TopinConstants.TopInDbFactory))
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        var appUserRepo = new AccountManagementRepository(session);
                        user = appUserRepo.GetAppUser(userName); // get user from database using NHibernate
                        transaction.Commit();
                        session.Close();
                     }
                     catch (Exception ex)
                     {
                         Log.Error($"Error retrieving user {user} to authenticate. Error: {ex}");
                         throw;
                     }
                 }
             }
             // do some logic to test passed credentials and return true or false
          }
    
         public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens,
                                                Dictionary<string, string> authInfo)
        {
            session.DisplayName = user.DisplayName;
            session.FirstName = user.Firstname;
            session.LastName = user.Lastname;
            session.Email = user.EmailAddress;
            // etc.....
            return base.OnAuthenticated(authService, session, tokens, authInfo);
        }
    }
    
1

1 Answers

2
votes

You can populate ServiceStack Service Client Cookies just like you would a browser except it only retains permanent Session Ids where you'll need to authenticate with RememberMe=true, e.g:

var response = client.Post(new Authenticate {
    provider = "credentials",
    UserName = ...,
    Password = ...,
    RememberMe = true,
});

Which will save the Authenticated User Session against the ss-pid permanent Cookie in the HttpWebRequest CookieContainer and gets sent on every subsequent request.

You can set your own Permanent Cookies in OnAuthenticated from authService with:

var httpRes = authService.Request.Response;
httpRes.SetPermanentCookie(cookieName, cookieValue);

Is an instance of my derived CredentialsAuthProvider class thread safe?

No the same AuthProvider singleton instance is used to Authenticate each request so you can't maintain any stored variables on the instance itself and will need to remove:

//private AppUser user = null; //Instance variables are not ThreadSafe

If you want to pass items and access them throughout the Request Pipeline you can store them in IRequest.Items Dictionary, e.g:

authService.Request.Items["AppUser"] = user;