ServiceClient Session access

1,130 views
Skip to first unread message

quaffapint

unread,
Mar 29, 2012, 11:05:14 PM3/29/12
to servic...@googlegroups.com
I have an asp.net mvc3 servicestack project and I'm trying to authenticate against a servicestack rest webservice.

I created a custom asp.net membership provider, and was planning to use a service stack serviceclient to authenticate against the service.  The question I have though is how do I access the session in this case?

I'm using this...

            JsvServiceClient restClient = new JsvServiceClient(AppHost.Config.AccountServiceURL);

                AuthResponse authResponse = restClient.Send<AuthResponse>(new Auth {
                    provider = CredentialsAuthProvider.Name,
                    UserName = "username",
                    Password = "password",
                    RememberMe = true,
                });
               
...Using that authResponse has a sessionId, but I'm not sure the best way to access the session object to lookup all the user attributes throughout my mvc3 project.

Thanks.
matt

Demis Bellot

unread,
Mar 29, 2012, 11:45:24 PM3/29/12
to servic...@googlegroups.com
The Session Object should only accessible on the server. It's just am IAuthSession POCO that's stored in the ICacheClient which is referenced by your ss-pid (permanent sessionId) or ss-id (temporary sessionId) depending Auth.RememberMe is set or not. Here are some integration tests that uses the AuthService to Authenticate:

The format of the session key that's ICacheClient that the Session POCO is stored against is generated by:

return IdUtils.CreateUrn<IAuthSession>(sessionId);
Where sessionId is a string created by:

var sessionId = Convert.ToBase64String(PermanentOrTemporarySessionGuid.ToByteArray());


The SocialBootstrapApi project shows you can can access the same CustomUserSession via a base ServiceStack service or a base MVC Controller:

MVC:

Note: ServiceStack's ICacheClient, ISession/IAuthSession classes are completely independent and decoupled from MVC/ASP.NET's Session/Caching providers.

As it's releated, here's some more info on accessing ASP.NET's Session from within ServiceStack:

quaffapint

unread,
Mar 30, 2012, 9:13:58 AM3/30/12
to servic...@googlegroups.com
Thanks, Demi.

I can use a base controller to access the customusersession, but it's not populated and I'm still being thick on how to populate it on the mvc site.  I'm not sure how to access it inside my custom membership provider.  I can call the webservice to validate the user, but when I call GetUser for example, I want to read the username and email from the custom user session.  When I try to access the cached usersession it's empty, so I'm obviously not tying the call to the webservice to validate the user to the mvc site's customusersession, and not sure how to inside this membership provider.

Thanks again for any suggestions.
matt


On Thursday, March 29, 2012 11:45:24 PM UTC-4, mythz wrote:
The Session Object should only accessible on the server. It's just am IAuthSession POCO that's stored in the ICacheClient which is referenced by your ss-pid (permanent sessionId) or ss-id (temporary sessionId) depending Auth.RememberMe is set or not. Here are some integration tests that uses the AuthService to Authenticate:

The format of the session key that's ICacheClient that the Session POCO is stored against is generated by:

return IdUtils.CreateUrn<IAuthSession>(sessionId);
Where sessionId is a string created by:

var sessionId = Convert.ToBase64String(PermanentOrTemporarySessionGuid.ToByteArray());


The SocialBootstrapApi project shows you can can access the same CustomUserSession via a base ServiceStack service or a base MVC Controller:

MVC:

Note: ServiceStack's ICacheClient, ISession/IAuthSession classes are completely independent and decoupled from MVC/ASP.NET's Session/Caching providers.

As it's releated, here's some more info on accessing ASP.NET's Session from within ServiceStack:

Demis Bellot

unread,
Mar 30, 2012, 10:29:25 AM3/30/12
to servic...@googlegroups.com
Basically ServiceStack's Authentication and ASP.NET membership provider is not related in any way.

I just mentioned how to access the users session using the sessionId, you will still need to call the Auth service to authenticate with the service - if you don't call the Auth service the custom session wont be populated.

Once authenticated (with the Auth service), you can access the Users Custom UserSession using this base class, the source code shows how it works (i.e. by accessing the cache using the ss-pid/ss-id cookie):

I think it's probably best you forget about ServiceStack's authentication provider if you're going down the ASP.NET membership provider route as they're not in any way related. i.e. Authenticating via one doesn't authenticate with the other.

The ServiceStack AuthProvider is entirely optional (i.e. not req'd in the fx) and is just built on cookies + request filters, you can do something similar in your custom ASP.NET membership provider.

Cheers,

quaffapint

unread,
Apr 1, 2012, 11:35:20 AM4/1/12
to servic...@googlegroups.com
So, if I use the asp.net provider, I can override the ValidateUser, and make the Authresponse call with my serviceclient, it authenticates and I know it's a valid user.  BUT, how do I make subsequent calls to the webservice?  How does it know I already called it with a service client and authenticated?

Thanks.

Demis Bellot

unread,
Apr 1, 2012, 11:54:42 AM4/1/12
to servic...@googlegroups.com
The Session is linked to the user via the ss-id/ss-pid cookies which contain a Base64 encoded SessionId Guid.
This SessionId is used as the key to fetch the users session from the CacheClient and an authenticated user is one where the sessionId points to an authenticated session stored in the CacheClient.

If you make the Authresponse call with your serviceclient, inspect its cookie container to find the populated ss-id/ss-pid cookies and use them to set them on the users HttpResponse so subsequent calls the user/browser makes to the website will pass the cookies as well.

If you don't want to extract the cookies on the service, you can use the SessionId returned from the AuthService and set it manually like:

var authResponse = client.Send<AuthResponse>(new Auth { ... });
var sessionId = Convert.ToBase64String( authResponse.SessionId.ToByteArray());
httpResponse.Cookies.AddPermanentCookie(SessionFeature.PermanentSessionId, sessionId);

I have Cookie helper classes I use here:

Cheers,

quaffapint

unread,
Apr 1, 2012, 3:19:24 PM4/1/12
to servic...@googlegroups.com
Thanks, but where does httpResponse come from on the client side?  If I try to use it through an IHttpResponse interface on my AccountController, I get an error of null reference.  I don't know how to initialize that object so I can use it to set the cookie for subsequent uses of the service client.  Figure it's gotta be something simple, but I just can't find an example of it on the client side that I can figure it out with.

Thanks again.

quaffapint

unread,
Apr 1, 2012, 6:26:09 PM4/1/12
to servic...@googlegroups.com
Ok, I used base.Response.Cookies.Add(cookie) to access the base servicestack controller.  My browser now has the ss-id cookies set.  But, when I create a new service client instance in another controller to call the webservice, I get unauthorized.  I'm not setting any options in the client, just calling the same webservice in another controller. Any ideas?

Man, I feel like I'm making this 100 times harder than it needs to be.  I'm using servicestack on the webservice and for the client.  I figure I should be able to call the webservice with the client, have the auth cookies set, and be good for any subsequent calls. I tried following your examples, but the twitter example confuses me, because you're showing in the AccountController that it's using the builtin asp.net authentication against nothing.  I think if I saw an independent webservice project and front end mvc project authenticating against that independent webservice, it would make it clearer.

Thanks again for any help.

Demis Bellot

unread,
Apr 1, 2012, 8:41:20 PM4/1/12
to servic...@googlegroups.com
I think you're problems stem from trying to merge 2 completely independent authentication providers. i.e. ServiceStack Auth doesn't rely-on/touch ASP.NET Authentication and vice-versa.

The AccountController in SocialBootstrap API is a red-herring it was automatically created with the MVC VS.NET template and was just left as-is, i.e. it's not used in any way in the SocialBootstrapApi demo.

The documentation for ServiceStack's Authentication is at:

In order to Authenticate you need to call the ServiceStack AuthService which by default is made available at: /auth/{provider}
So to Authenticate with a UserName/Password you would send a HTTP Request like:

POST localhost:60339/auth/credentials
Content-Type: application/json 
{
     "UserName": "admin",
     "Password": "test"
     "RememberMe": true
}

Note: The RememberMe flag is what tells ServiceStack to store the users session against the the 'ss-pid' (PermanentSessionId).

When you Authenticate the service all ServiceStack does is loads the Users Session and stores it against the SessionId which is also returned in the AuthReponse DTO.
The AuthService automatically (if its not done already done so) tells the HTTP client to store the ss-id and ss-pid cookies which contains the Users SessionId.

The source code for the AuthService which includes the Auth and AuthReponse DTOs is available at:

There are 2 primary hooks where you can add custom AuthLogic. If you're implementing your own CustomAuthProvider you can override the AuthProvider.OnAuthenticated() method:
Which will be invoked when a user has Authenticated with that particular AuthProvider.

Otherwise if you implement a CustomUserSession you can implement its AuthUserSession.OnAuthenticated() method which will get called whenever a User is Authenticated with any of the registered AuthProviders.
The example from SocialBootstrapApi uses this to execute Post Auth tasks like extract the UserSession data, set the users Gravatar Url and extract any Twitter or Facebook AuthInfo into its own custom User db table (separate from SS):
There are 2 ways SocialBootstrapApi authenticates with ServiceStack, via the Registration service:

It uses the url from the HTML form which is at /api/register 
This calls the built-in Registration service here:
Which if AutoLogin is set to true, will auto authenticate the user (in the same request) after a successful registration:

SocialBootstrapApi also allows the user to sign-in manually using a UserName/Password against the CredentialsAuthProvider which it does at:
https://github.com/ServiceStack/SocialBootstrapApi/blob/master/src/SocialBootstrapApi/Content/js/app/login.js#L23 
Again the login uses the url from the HTML form which is at /api/auth/credentials - i.e. the CredentialsAuthProvider:
https://github.com/ServiceStack/SocialBootstrapApi/blob/master/src/SocialBootstrapApi/Views/Shared/Index.cshtml#L318 

Finally Twitter and Facebook is also supported which starts off as a simple GET request at:
         <a href="@Url.Content("~/api/auth/facebook")"><img src="@Url.Content("~/Content/img/sign-in-with-facebook.png")" alt="Sign-in with Facebook" /></a>
</div>
<div id="twitter-signin">
         <a href="@Url.Content("~/api/auth/twitter")"><img src="@Url.Content("~/Content/img/sign-in-with-twitter-l.png")" alt="Sign-in with Twitter" /></a>     
</div>

Which either starts off at the FacebookAuthProvider /api/auth/facebook or the TwitterAuthProvider at /api/auth/twitter

Hope this explains things clearer.

Cheers,

quaffapint

unread,
Apr 1, 2012, 9:47:39 PM4/1/12
to servic...@googlegroups.com
Thanks, Demis.  Here's what I noticed and what I believe has been messing me up - The client is neither storing nor sending the ss-id/pid cookie.

You mention...

"The AuthService automatically (if its not done already done so) tells the HTTP client to store the ss-id and ss-pid cookies which contains the Users SessionId."

...But, I have to walk through the CookieContainer of the initial authentication client (I'm using the JsvServiceClient), grab the ss-id and set the cookie in the user's browser myself.  Then with every new JsvServiceClient object I need to lookup the ss-id from the browser cookie and add it to the JsvServiceClient's CookieContainer.  Then it works.

If that's just how it works, that's fine - I just want to make sure I'm not doing something wrong that's going to cause issues.

Thanks.

Demis Bellot

unread,
Apr 1, 2012, 10:23:38 PM4/1/12
to servic...@googlegroups.com
Yeah that's fine, whether you use the existing ss-id/ss-pid cookies and pass them thru to the AuthService with the ServiceClient or you authenticate without any Cookies and retrieve them from the AuthResponse (overriding their existing ss-id/ss-pid cookies) either should work.

The ServiceClient has a HttpWebResponseFilter and LocalHttpWebRequestFilter you can use to inspect the returned HttpWebResponse:

I don't want to complicate/confuse this anymore (esp. if you've got something working) but if its in the same website you should be able to resolve the AuthService from ServiceStack's IOC and execute the authLogic directly (i.e. without using a ServiceClient + HTTP).

var authService = EndpointHost.AppHost.TryResolve<AuthService>();

This returns an autowired instance of the AuthService, normally you would be able to call this as-is (since most services don't use the IHttpRequest/IHttpResponse) but the AuthService does.
So we need to create a IRequestContext and inject instances of the IHttpRequest, IHttpResponse objects which are just wrappers around ASP.NET's 

//Using Ext method in ServiceStack.ServiceInterface.SessionExtensions
var req = HttpContext.Current.Request.ToRequest(); 
var res = HttpContext.Current.Response.ToResponse();

//You can also fake adding a cookie with: 
req.Items[SessionFeature.PermanentSessionId] = sessionId;

authService.RequestContext = new HttpRequestContext(req, res, null);
var response = authService.Post(new Auth.Auth {
    provider = BasicAuthProvider.Name,
    UserName = userPass.Value.Key,
    Password = userPass.Value.Value,
    RememberMe = true //to use ss-pid (aka PermanentSessionId)
});

And the response is the response DTO without any serialization + HTTP overhead :)

The above snippet was taken from the [AuthenticateAttribute] which calls the AuthService to validate using BasicAuth user/pass:
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/AuthenticateAttribute.cs#L84 

Note: Retrieve any ServiceStack IOC dependency with EndpointHost.AppHost.TryResolve<AuthService>(); 
This is basically how base.ResolveService<T>(); works under the hood :)

Cheers,
Reply all
Reply to author
Forward
0 new messages