Integrate Client Application Services

Topics: Customizing Orchard, General
Feb 22, 2011 at 5:18 AM

I would like to use an Orchard installation as the host for Client Application Services so that users of my sites/apps have only a single username and password and a single place to maintain that information.
http://msdn.microsoft.com/en-us/library/bb384297.aspx

Does anyone have information on where to start with this?
Basically it's just exposing the authentication/authorization in orchard to other sites and apps but it's not exactly obvious where to start plugging this in to Orchard.

Thanks in advance,
Will

Feb 24, 2011 at 4:22 AM
Edited Feb 24, 2011 at 5:29 AM

Well after some digging around I have gotten a bit farther but have hit another roadblock.

I have added an OrchardMembershipProvider class to Orchard.Web and this class implements MembershipProvider. This all works and the Client Application Service calls this class once I have everything wired up correctly -- I can show the code for how this is done if anyone is interested.

Now I have a WPF application that hits my OrchardMemvershipProvider.ValidateUser function. What I want is for ValidateUser to simply call the same ValidateUser that orchard uses from the IMembershipService. I'll do similar things for Roles, Profile, etc.

However, the problem I now have is that my IMembershipService lacks an implementation and I can't figure out how to get the one Orchard is using (the current one in the IoC container).

Can anyone help with this aspect of the problem?

Thanks,
Will

Coordinator
Feb 24, 2011 at 5:25 AM

I'm not sure what value Orchard would bring to this. There are plenty of single sign-on systems out there, which makes me wonder what the scenario is here that justifies the use of Orchard.

Feb 24, 2011 at 5:34 AM

I'm not after SSO, per se. It's not a requirement that a user be able to authenticate on one site and automatically be authenticated on another. The requirement is simply that I have multiple sites and multiple WPF apps and I only want the user to have a single username/password for all of them and a central location for roles and profiles. The way to accomplish this is to use Client Application Services, which is very easy in a normal ASP.NET application, because I have easy access to the membership provider because we wither create it or we use the one provided with Microsoft based on the aspnet_xyz tables.

The alternative would be for me to host CAS in another website using my own membership and role providers and then tailor Orchard to use my providers instead if its default. This is a less desirable option as I want the services hosted from the same site that will be responsible for account management (the current Orchard project I am working on).

TIA,
Will

Coordinator
Feb 24, 2011 at 5:43 AM

You could start by looking at the code for that module: http://orchardopenauth.codeplex.com/

Feb 24, 2011 at 2:32 PM

I've setup my membership service in a module the same way they do in the open id module (which is similar to what I was already doing). The problem I am having is that my membership service requires a parameterless constructor.
When this service is called it is not routed through orchard the way a normal request would be routes. My service, which has a dependency on IMembershipService, isn't initialized correctly because if I provide a parameterless constructor then the dependency for IMembershipService can't be injected...

What I need is a way to initialize the same MembershipObject that Orchard is using but do it in my own parameterless constructor. Not the cleanest method but I don't really see any other alternative.

This would be so much simpler is Orchard exposed interfaces for authentication that are common to Microsoft. One has to wonder why that decision was made since Orchard was written by members of the ASP.NET team right? Why not use their own authentication providers... It seems like they did use the same interfaces for role management...

Coordinator
Feb 24, 2011 at 7:47 PM

The ASP.NET provider infrastructure is fairly old and inconsistent with the current design practices that we use on Orchard. This is why we wrapped them, but we still use them under the cover. From what I hear from the dev team, it wasn't easy actually as it is quite singleton-minded in places which makes it more difficult to reconcile it with features such as multi-tenancy.

You don't have to inject references from the constructor. For example, the localizer is usually injected through properties. You should be able to do the same thing. You can also get references from the work context I believe.

Feb 24, 2011 at 8:29 PM

Do you have an example of this?
Keep in mind that what happens here is a WCF service is being called that doesn't really have anything to do with Orchard (for now my implemention of MembershipProvider exists in the Orchard.Web project but that's just a temporary thing.
It just happens that I want this service to be able to use the same IMembershipService, IRoleService etc that Orchard uses.
So, what I need is a way to inject those dependencies into my service when the service is started. Ideally, if I do it correctly, if someone updates Orchard to use a different implementation of IMembershipService in the future then my service will automatically use the same one because I want it injected with AutoFac.
I'm searching the AutoFac docs and the Orchard source for where this is happening in Orchard but I haven't found it yet. Any ideas?

Thanks,
Will

Coordinator
Feb 24, 2011 at 10:52 PM

Except for the localizer example I gave you, no I dont't have an example because we never have to do that. Why exactly can't you have the dependencies injected in the constructor? I mean, not everything has to be in the WCF service's class, you can also use a separate technical class that does get a work context accessor using for example WorkContextExtensions's extension method GetWorkContext. Once you have a work context, you can programmatically get any dependency.

Let me know how that goes.

Feb 25, 2011 at 3:30 AM

Things are just never simple. I finally got the actual service to spin up and return as expected. Still haven't figured out how to get the dependencies I need. Turns out the WorkContextExtensions.GetWorkContext only works on ControllerContext or RequestContext, neither of which I have because I am in a WCF service. I'm wondering if I am even going about this the correct way. CAS works over WCF and the services already exist in .net so I don't have any way to customize them. The only place I have for customization is my own implementation of the MembershipService. This part all works as expected up until I need the services that Orchard uses. None of the code out there is handling this situation. If I was building out a module this wouldn't be a problem because the dependencies would get auto-injected. However, this being WCF is completely bypassing seemingly everything Orchard is doing.

So, somehow I have to figure out the code needed to resolve these dependencies. I am certain this facility resides in AutoFac but I can't even find the code in the Orchard source that shows where they are even registering the implementation of IMembershipService. It has to happen somewhere, I just don't see where.

Any AutoFac experts out there know how/where this is happening?

Thanks,
Will

Coordinator
Feb 25, 2011 at 5:15 AM

Just wanted to let you know that we're looking into it.

Feb 25, 2011 at 7:05 AM

Thanks.

I'll be more than happy to share all the steps necessary to get this up and running once we solve what will hopefully be the last of the issues.
Just getting the CAS stuff to work requires a few odd tweaks to the orchard web.config but they're easy once you know what to look for.

Thanks again,
Will

Coordinator
Feb 25, 2011 at 7:17 AM

OK, so it seems like this requires a little more work and we'll need some time to get to a solution. Would you mind filing a bug so that we can track that?

Feb 25, 2011 at 4:39 PM

Created:
http://orchard.codeplex.com/workitem/17389

Once we get to a resolution I will write a how-to. Hopefully this is something we can figure out a way to wrap into a  module that is then easily installable/configurable. Though with the required web.config changes maybe this isn't possible.

Will

Feb 25, 2011 at 10:10 PM

A little further. When my service is called by my client app I can put a breakpoint in the constructor. What I have found is this
var req = HttpContext.Current.Request.RequestContext; //This is actually populated.
var wc = req.GetWorkContext(); //This returns null.

Any thoughts here?
Thanks,
Will

Coordinator
Mar 1, 2011 at 12:27 AM

It might help to step into the method and see where it fails. It needs to find the route data, which is something that could be missing here.

Mar 1, 2011 at 4:16 AM

Ok here you go.

In WorkContextExtensions.GetContext

if(!routeData.DataTokens.TryGetValue("IWorkContextAccessor", out workContextValue)){ //this is false
    workContextValue = FindWorkContextInParent(routeData); }                                      //this gets executed and returns null

FindWorkContextInParent returns null because routeData.DataTokens is an empty Dictionary.

Which seems like exactly what you said in your last post.
Any idea where to look to see why the routeData isn't getting populated correctly?

Thanks,
Will

Coordinator
Mar 1, 2011 at 5:07 AM

I don't think the request is going through routing at all on a service request. I'll ask around how we should deal with that going forward, but in the meantime, to get you going, you might want to consider replacing the WCF service with a controller action.

Coordinator
Mar 1, 2011 at 5:25 AM

This bug is tracking the issue: http://orchard.codeplex.com/workitem/17197 Feel free to vote it up.