MVC3 application authentication to a remote service stack service best practices.

873 views
Skip to first unread message

Maxus

unread,
Jun 21, 2012, 4:03:58 AM6/21/12
to servic...@googlegroups.com

Hi Service Stack People,

I have a question about what is the best practise for authentication a MVC 3.net application to a remote service stack application.

Currently we use the JsonClient to access to service stack on a remote machine and have the authentication system working at the service stack level. When a user logs into the MVC3 web application we would like to check if they are a valid user and then continue on each request to see if session is still active valid, If not log them out of the MVC application.

Basically I would like to delegate the MVC authentication to the remote service stack authentication system.

Is this the right thing to do? any suggestions on pointers or where I should start looking?

Thanks Heaps!
-M
 

Demis Bellot

unread,
Jun 21, 2012, 4:26:00 AM6/21/12
to servic...@googlegroups.com
Hi Maxus,

Please see the https://github.com/ServiceStack/SocialBootstrapApi for a demo example of an MVC3 + ServiceStack application working together, it uses ServiceStack's Authentication feature as described in: 

Note the above demo hosts ServiceStack + MVC3 in the same web application, if you go this route Sessions are shared by having SS + MVC accessing the same Caching Provider with the cookie key as described in:

Likewise when SS + MVC are on the same host than code/logic sharing is available at the IOC layer as described in:

Otherwise if SS + MVC must be run in separate hosts and connected remotely, you can communicate using the JsonServiceClient or if you're using a distributed cache (like redis or memcached) you can access the same data using a shared key and the ICacheClient API.

Cheers,

Maxus

unread,
Jun 21, 2012, 7:49:33 PM6/21/12
to servic...@googlegroups.com
Hi Dennis,
 
Thanks very much for your reply, i would love to use service stack inside mvc3, but my requirements are being dictated :(
 
Currently my code uses the JSON client, but every request tot he web site appears to come through as the same user.
 
for eaxmple, this is my client I use in my MVC application to talk to the remote service stack application:
 
 public class UserServiceClient
{
        private readonly JsonServiceClient _Client;
        public UserServiceClient()
        {
            _Client = new JsonServiceClient("http://localhost:59532");
        }
        public UserDtoResponse Get(Guid id)
        {
            return _Client.Get<UserDtoResponse>(string.Format("/user/?id={0}", id));
        }
        public bool Authenticate(string userName, string password, bool rememberMe)
        {
  _Client.Send<AuthResponse>(new Auth {UserName = userName, Password = password, RememberMe = rememberMe});
        }
}
 
After I log in and call the user get, the session is the logged in user, if i then open another browser (IE for example and call the get user without logging in, I still see the session from when I logged in as the other user the first time?) It appears the user sessions are global when using the json client, any ideas what I have done wrong? or is that by design?
 
The UserServiceClient is being created fresh on every request.
 
Thanks!
M

Demis Bellot

unread,
Jun 22, 2012, 5:33:34 AM6/22/12
to servic...@googlegroups.com
The session id is kept in the cookies, I'd read up on Sessions to see how it works:

So you'll need to proxy the Cookies from the browser request into the CookieContainer of the service clients to properly impersonate the original request/user.

Cheers,

Maxus

unread,
Jun 25, 2012, 3:10:33 AM6/25/12
to servic...@googlegroups.com

Hi Demis,
 
I'm having no luck what so ever, service stack always seems to allow access to services that are marked: [authenticate], and I cant seem log users out. Is there anything obviously wrong with what I have below, sorry for the hassle, but I think I'm missing something really obvious :)
 
 
Created my own custom: CredentialsAuthProvider:
 
    /// <summary>
    /// Handles authentication to the service.
    /// </summary>
    /// <remarks>
    /// overview of the authentication system: https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization
    /// </remarks>
    public class CustomCredentialsAuthProvider : CredentialsAuthProvider
    {
        private readonly IUnitOfWorkFactory _UnitOfWorkFactory;
        private readonly IUserDao _UserDao;
        private readonly IUtilities _Utils;
        public CustomCredentialsAuthProvider(IUtilities utils, IUnitOfWorkFactory uowFactory, IUserDao userDao)
        {
            _UnitOfWorkFactory = uowFactory;
            _UserDao = userDao;
            _Utils = utils;
        }
        /// <summary>
        /// Try to authenticate the current user.
        /// </summary>
        /// <param name="authService">The auth service.</param>
        /// <param name="userName">The username.</param>
        /// <param name="password">The users password.</param>
        /// <returns>A <see cref="bool"/> indicating if the user authenticated or not.</returns>
        public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
        {
            using (_UnitOfWorkFactory.StartUOW())
            {
                //Return true if credentials are valid, otherwise false
                var user = _UserDao.GetByUserName(userName);
                // Check the user isn't null and the hashed password is valid.
                return user != null && _Utils.VerifySha1Hash(user.Salt, password, user.Password);
            }
        }
        /// <summary>
        /// Occours after the user is authenticated.
        /// </summary>
        /// <param name="authService">The auth service.</param>
        /// <param name="session">The auth session.</param>
        /// <param name="tokens">Any OAuth tokens</param>
        /// <param name="authInfo">The auth infomation.</param>
        public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
        {
            using (_UnitOfWorkFactory.StartUOW())
            {
                // Get the currently logged in user.
                var user = _UserDao.GetByUserName(session.UserAuthName);
                // Fill the IAuthSession with data which you want to retrieve in the app eg:
                session.FirstName = user.FirstName;
                session.LastName = user.LastName;
                session.Email = user.Email;
                session.DisplayName = user.Username;
                session.CreatedAt = user.CreatedAt;
                session.LastModified = user.UpdatedAt;
                session.Sequence = user.Id.ToString();
                session.UserName = user.Username;
                session.IsAuthenticated = true;
                // Set the role the user has.
                if (user.Role != null)
                {
                    session.Roles = new List<string>(new[] { user.Role.Name });
                }
                // Important: You need to save the session!
                authService.SaveSession(session, new TimeSpan(0, 30, 0));
            }
            base.OnAuthenticated(authService, session, tokens, authInfo);
        }
    }
 
Added it to the apphost like so:
 
            // Set logging factory.
            LogManager.LogFactory = new NLogFactory();
            // Setup our user auth session.
            container.Register<IAuthSession>(c => new AuthUserSession());
            // Setup our infrastructure.
            container.Register<IUnitOfWorkFactory>(c => new UnitOfWorkFactory());
            container.Register<IAuthTokenService>(c => new ServiceStackAuthTokenService(c.Resolve<IAuthSession>()));
            container.Register<IUtilities>(c => new Utilities());
            // Daos:
            container.Register<IUserDao>(c => new UserDao(c.Resolve<IUnitOfWorkFactory>(), c.Resolve<IAuthTokenService>(), c.Resolve<IUtilities>()));
            // Validators:
            container.RegisterValidators(typeof(UserDtoValidator).Assembly);
            // Add the auth plug-in.
            Plugins.Add(new AuthFeature(container.Resolve<IAuthSession>, new IAuthProvider[] { new CustomCredentialsAuthProvider(container.Resolve<IUtilities>(), container.Resolve<IUnitOfWorkFactory>(), container.Resolve<IUserDao>()) }));
            Plugins.Add(new ValidationFeature());
            container.Register<ICacheClient>(new MemoryCacheClient());
            //var userRep = new InMemoryAuthRepository();
            //container.Register<IUserAuthRepository>(userRep);
 
I then whipped up a simple client wrapping the json client. please ignore the code quality here :)
 
    public class UserServiceClient
    {
        private readonly JsonServiceClient _Client;
        public UserServiceClient()
        {
            _Client = new JsonServiceClient("http://localhost:59532");
            if (HttpContext.Current.Session["AuthCookies"] != null)
            {
                _Client.CookieContainer.Add((CookieCollection)HttpContext.Current.Session["AuthCookies"]);
            }
        }
        public UserDtoResponse Get(Guid id)
        {
            return _Client.Get<UserDtoResponse>(string.Format("/user/?id={0}", id));
        }
        public UserDtoResponse GetByUserName(string userName)
        {
            return _Client.Get<UserDtoResponse>(string.Format("/user/?username={0}", userName));
        }
        public AuthResponse Authenticate(string userName, string password, bool rememberMe)
        {
            var result = _Client.Send<AuthResponse>(new Auth{ UserName = userName, Password = password, RememberMe = rememberMe });
            HttpContext.Current.Session["AuthCookies"] = _Client.CookieContainer.GetCookies(new Uri("http://localhost:59532"));
            return result;
        }
        public void LogOut()
        {
     // None of these appear to log out.
     //_Client.HttpMethod = HttpMethod.Delete;
     //var result = _Client.Send<AuthResponse>(new Auth());
            _Client.Send<AuthResponse>(HttpMethod.Delete, "/auth/logout", new Auth());
            // Clear the cookies, removing the auth details provided by ss.
            HttpContext.Current.Session["AuthCookies"] = null;
        }
    }
 
Thanks!
Maxus
 
 
On Friday, June 22, 2012 5:33:34 PM UTC+8, mythz wrote:
The session id is kept in the cookies, I'd read up on Sessions to see how it works:

So you'll need to proxy the Cookies from the browser request into the CookieContainer of the service clients to properly impersonate the original request/user.

Cheers,

Demis Bellot

unread,
Jun 25, 2012, 7:21:14 PM6/25/12
to servic...@googlegroups.com
Hi Maxus,

You can't use client.Send<T> with another verb (e.g. HttpMethod.Delete) - it does a strict POST.

Have you tried just: client.Get<AuthResponse>("/auth/logout"); ?
Or if you prefer: client.Send<AuthResponse>(new Auth { provider = "logout"}) ?

I see you're also using HttpContext.Current.Session["AuthCookies"] - not sure if you're aware that they aren't the same as ServiceStack's Session:

Cheers,

Maxus

unread,
Jun 28, 2012, 3:19:27 AM6/28/12
to servic...@googlegroups.com
 
Hi Demis,
 
I finally figured it out, the issue was this line:
 
// Setup our user auth session.
container.Register<IAuthSession>(c => new AuthUserSession());
What that was doing was effectivly creating the AuthUserSession as a singleton, and that was always being returned when the session handling logic went to create a new authsession effectivly poluting the session varibles.
 
Thanks for you help!
 
Cheers,
Maxus
 

On Tuesday, June 26, 2012 7:21:14 AM UTC+8, mythz wrote:
Hi Maxus,

You can't use client.Send<T> with another verb (e.g. HttpMethod.Delete) - it does a strict POST.

Have you tried just: client.Get<AuthResponse>("/auth/logout"); ?
Or if you prefer: client.Send<AuthResponse>(new Auth { provider = "logout"}) ?

I see you're also using HttpContext.Current.Session["AuthCookies"] - not sure if you're aware that they aren't the same as ServiceStack's Session:

Cheers,
Reply all
Reply to author
Forward
0 new messages