Hi there!
I’ve been meaning to ask this question for while.
I am currently developing a site purely in asp.net mv + ravendb and using service stack for text etc. I am now at a point where I need authentication. I’m also sparingly using angular js and few internal apis. Eventually, we’d be exposing more api’s and using angular throughout the site. Is it still viable to use SS’s authentication, caching features even though 90% of the site is controller/action based? My decision would be to go with service stack. What would you suggest? Anything that I need to keep in mind or watch out for?
Thanks!
Kebin
Yep, sure you can use ServiceStack Authentication with MVC, checkout the live demo at: http://mvc.servicestack.net and some supporting docs at: https://github.com/ServiceStack/ServiceStack/wiki/ServiceStack-Integration
Kebin Maharjan:
Awesome! Looking forward to getting started with this. I’m currently using Ninject and I’m aware that I can use container adapter to make this work with Funq. Do you recommend that I completely use Funq since I’ll have one less project to worry about? Definitely up to me, but I’m open to your opinion/suggestion.
Finally is it possible to use guid for userid? RavenDb allows to override the default behavior of using incremental integer as id. Will there be any issue within ServiceStack?
Thanks a lot!
I’m likely not the best person to ask as I consider heavy IOC’s are an unnecessary complexity/dependency. Given that I’ve not needed anything more than Funq and make a point of using the min moving parts/deps - I personally wouldn’t reference another IOC myself.
The RavenDbUserAuthRepository should take care of persisting UserAuth info, but it does use as int UserId. You can find the UserAuth POCO’s that are used here: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/Auth/UserAuth.cs also see this answer for different ways they can be extended: http://stackoverflow.com/a/11118747/85785
Fredrick Lackey:
+Demis Bellot Wish more felt that way. Having been on teams with all sorts of IoC (Windsor, Unity, whatever), I prefer Funq. It’s light, logical, and doesn’t overstep what IoC should do.
Kebin Maharjan:
Following up about ways to extend UserAuth. I subclassed UserAuth to add to its schema. All is good since I can do the custom registration to create new accounts. However there are several places in RavenDbUserAuthRepository where concrete UserAuth is being used instead of IUserAuth. Perhaps its unavoidable at certain places to use the interface? This creates an issue. The first one is as follows:
Lets say I have CustomUser that subclasses UserAuth.
Ravendb index currently is based on type UserAuth and therefore GetUserAuthByUserName fails for CustomUser. I resolved using polymorphic index(using convention for subclasses) as documented in http://ravendb.net/docs/article-page/2.5/Csharp/client-api/querying/polymorphism.
So all is good for that particular situation. So I guess my question is if there are any particular tips/gotchas that I should be aware of while extending UserAuth while using RavenDb? Or do you recommend that I separate User as a different entity than UserAuth? My concern with the latter approach is that I’d have bits and pieces of data lying around in these two separate entities. I could definitely use UserAuth solely for auth purposes and put anything other than authentication properties to custom class.
Extending the UserAuth/UserAuthDetails tables is only supported in select UserAuth Repo’s e.g.
The OrmLiteAuthRepository and RedisAuthRepository enable this with a specialized generic constructor:
OrmLiteAuthRepository<TUserAuth, TUserAuthDetails>
see: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Server/Auth/OrmLiteAuthRepository.cs
The RavenDbUserAuthRepository doesn’t have a specialized constructor that supports custom UserAuth/UserAuthDetails tables: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Authentication.RavenDb/RavenDbUserAuthRepository.cs
If you want I can try re-factor the RavenDb UserAuth Repo to support a similar generic constructor?
The other alternatives is to leave the concrete UserAuth/UserAuthDetails tables alone and store any additional metadata in the Meta Dictionary using the Get<T> and Set<T> API’s or in separate tables/types that you have complete control over which can be linked back using the RefId
or RefIdStr
fields.
Kebin Maharjan:
Thanks for the quick response. Ideally I think it would be nice to have a generic constructor as in OrmLiteAuthRepositor/RedisAuthRepository.But it might not be necessary if UserAuth didn’t have concrete properties like city, state, mailing address etc. Besides the reason that not all application would need those properties, some of those properties don’t apply in various scenarios. For example, there is no concept of state in some countries. Its a simple example but if UserAuth is to grow in future, there might be other additional properties that would just be there without being used. The dilemma is that if I were to use UserAuth(either by subclassing, using get, set or RefId), would in the future I will end up with lots of such unused properties.
Kebin Maharjan:
In addition to that, when I GetUserAuthByUserName, the whole document is brought back from the db. Surely this is great if I were calling this to display the entire object(and add them to session) but I probably would not want everything for authentication purposes? If authentication failed, its a waste, specially it the document was a bit bulky. I could definitely subclass GetUserAuthByUserName(if made virtual) & have it just bring back minimum properties required for authentication. Again this would just be necessary if UserAuth were to be bulky in the future.
It doesn’t apply in all scenarios, but in order to support RDBMS tables fixed schemas we’ve have to preempt and add concrete fields that can be returned by the different OAuth providers. The UserAuth tables are effectively frozen since any changes will break existing RDBMS tables and would require a manual upgrade on each update, even when they’re not being used. To mitigate this the UserAuth/UserAuthDetails POCO’s include a fairly complete list of fields related to User Authentication as well as different extensibility options so additional metadata can be added or referenced.
The large number of fields isn’t an issue in many NoSQL DB’s since null fields are not serialized by default and so unused fields doesn’t affect the payload. It may have an affect in RavenDB, but Authentication only needs to be done once so a little overhead is preferred to any additional configuration or code maintenance - which isn’t noticeable when using OAuth since the latency of the redirects and manual approval would affect performance a lot more than additional fields would.
Kebin Maharjan:
I’m totally convinced with your comments regarding rdbms & need for preempt fields. Also agree on overhead vs code maintenance! So I think making a generic repository for ravendb would definitely be helpful. And perhaps making the methods that make db calls virtual will be of greater use too. Even the index creation calls why you are at this?
Thanks a lot, I’ve to appreciate and be thankful that I’m learning a lot from ServiceStack and having discussions with you!
ok cool, I’ve just refactored RavenDB to support specialized constructors in this commit: https://github.com/ServiceStack/ServiceStack/commit/a25769b8c7c4f65201400d180a9939fcba51b60e
This change has also just finished deploying to MyGet, if you’ve already get the latest v4.0.34 of MyGet packages installed you’ll need to clear your NuGet cache and delete your /packages folder to be able to pull down the updated v3.0.34 packages, see: https://github.com/ServiceStack/ServiceStack/wiki/MyGet#redownloading-myget-packages
Kebin Maharjan:
Looks like there’s a little bug with OAuth provider after the above update:
- I create a user using generic ravendb constructor…RavenDbUserAuthRepository<MyUserType, UserAuthDetails> where MyUserType: UserAuth
- Login
- Sign up using facebook
This creates a UserAuthDetails document for facebook but everything is null except UserAuthId and Provider.
However everything is well If I do the same exact with generic constructor but with RavenDbUserAuthRepository<UserAuth, UserAuthDetails>.
Does FacebookAuthProvider need to be updated?
I doubt this is specific to the Facebook Auth provider - I suspect it’s an issue with using custom types in the RavenDb Auth Provider. Is there another doc type that’s created, i.e. MyUserType or UserAuth?
Kebin Maharjan:
With FacebookAuthProvider I get MyUserType and UserAuthDetails.
UserAuthDetails only has provider, UserAuthId, Items.access_token.
When I create a new account with facebookprovider, both UserAuthDetails and MyUserType are mostly null.
ok it looks like I missed a type in the latest refactor, fixed in this commit:
https://github.com/ServiceStack/ServiceStack/commit/1469b4c1285e518988834ac6b5cb2cd725bbe14a
Which has now just finished deploying to MyGet, you’ll need to clear NuGet pkgs again to pull latest version: https://github.com/ServiceStack/ServiceStack/wiki/MyGet#redownloading-myget-packages
Kebin Maharjan:
Awesome. Works great! Is there an attribute to negate [Authenticate] attribute. i.e. I set the Authenticate attribute on the controller and then add something like AllowAnonymous on selected actions?
You can’t reverse the effects of a filter. Just move the anonymous operations into a different Service class without the [Authenticate] attribute, or alternatively to keep everything in the same class, only apply [Authenticate] on the operations (i.e. methods) that need it.
Kebin Maharjan:
Got it! Is there a way to get hold of AuthUserSession outside of controller or service?
The Session is tied to the Request (which it needs to retrieve the session ids in the request cookies). If you have an instance of http request (e.g. in filters) you can get the session with:
var session = httpReq.GetSession();
Otherwise pass it in from the Service to your dependency.
In ASP.NET hosts you can access the current request with the static HttpContext accessor:
var httpReq = HttpContext.Current.Request.ToRequest();
var session = httpReq.GetSession();