getting started with Overriding orchard authentication

Topics: Customizing Orchard
Mar 9, 2011 at 2:01 PM
Edited Mar 9, 2011 at 2:34 PM

hi,

So I just took a look at a few orchard modules, I have the OpenAuth module open right now, but its a lot of code to go through so I still haven't understood one crucial thing here.

How do I really override IAuthenticationSerivice? Do I just implement it in a class with IDependency and AutoFac takes care of everything else, or is there a config or some code I have to write? 
My question : Where/how do I tell orchard.. .use my class instead of the inbuilt one.

(Detail: What I intend to do in the logic is call a REST service with the username and password and authenticate there)
Thanks!
Mar 9, 2011 at 2:22 PM

I'm interested in this issue, too.

Mar 9, 2011 at 2:38 PM

Great to hear, if someone can give me a starting point.

As a more general problem, when it comes to overriding any orchard service, do we place our class within our own module and inherit from IDependency or what? Newbies like me haven't got all that much experience with DI or AutoFac (I've dabbled with MEF)

 

This article here explains how many extensibility points orchard has but no technical detail on how they could be overriden, is there any module I've missed looking at, that does this sort of thing? (The OpenID module is just too complex!)

http://www.szmyd.com.pl/blog/most-useful-orchard-extension-points

Mar 9, 2011 at 4:22 PM
Have you seen this?
hit: - http://live.visitmix.com/Archive#VideoList
search for: Deep Dive into Orchard Extensibility for CMS Developers



Mar 14, 2011 at 4:13 PM

Thanks, that video is a year old but pretty useful.

I'm just like to know very simply though that if I have a class that implements IAuthenticationService defined in my module does it automatically override orchards inbuilt one?

May 18, 2011 at 7:52 PM

I'm looking to figure out what the best practice for this is as well.  I have the same question as giddy in that I have a web service that I need to communicate with and I would need to authenticate with that differently than Orchard.  At the same time, I don't want to (or at least I don't think I should) override Orchard's authentication because then I would have to take responsibility for managing all Orchard type accounts with my authentication.

I really just need to be able to authenticate against my web service in certain parts of the site.  Looking at the OpenAuthentication module I see how it has created it's own IOpenAuthenticationService which can be injected independently of the Orchard IAuthorizationService.  That seems like the right way to go because I can inject my authentication service when I need it.

Thanks,
Vinnie

Aug 8, 2012 at 8:15 PM

I would love to see an answer for this as well.  Just a simple example would be very appreciated.  I know this post is pretty old but I still couldn't find any documentation on implementing custom authorization in Orchard.

Developer
Aug 8, 2012 at 9:32 PM

If all you're looking for is custom authentication, it's really quite simple: In your controller, inject IAuthenticationService and create a ticket at any moment you like. For example, invoke a web service, and if it says: "go", you invoke _authenticationService.SignIn(); Now a session ticket has been created (by default, this will be a forms ticket).


Aug 20, 2012 at 2:50 PM
Edited Aug 20, 2012 at 2:53 PM

sfjskywalker, thanks, but I think you're missing the point that me and everyone above me is bringing up.  We want a simple example of how to "inject IAuthenticationService" or use any extensibility point.  Do I implement this interface and then pass it as a parameter into the constructor of my controller?  This doesn't seem to work.  Does Orchard somehow search all assemblies for any class that overrides their interfaces or do we need to put these classes in particular locations?

On another note, I was asking about authorization, not authentication, but not a big deal, any example will be very helpful.

Aug 20, 2012 at 7:49 PM

 

1) Implement interface in your module

2) suppress the normal dependency using "OrchardSuppressDependency" (use as an attribute on the class)

[OrchardSuppressDependency("namespace.class")]
public myclass : ISomeOrchardInterface
{
}

Of course, if you are playing with authentication, its only a while until you discover the fun of using a different implementation of membership....

Aug 20, 2012 at 8:17 PM
Edited Aug 23, 2012 at 7:55 PM

Thanks LordSaul!  I take it there's no actual manual "injection" happening, Orchard just searches the assemblies for anything with this attribute?

What I ended up doing was just changing my routes.cs to point to my own controller rather than Orchard's, and I handle Authentication there like I normally would:

Edit.  This doesn't work.  The admin dashboard will now redirect to this as well, and from there it's not clear how to handle the different requests, if it's even possible.

new RouteDescriptor {
                    Priority = 100, //use high priority here so Orchard's default is ignored
                    Route = new Route(
                        "Users/Account/Logon",
                        new RouteValueDictionary {
                            {"area", "SPI.OwnerSite"},
                            {"controller", "Account"},
                            {"action", "Logon"}
                        },
                        new RouteValueDictionary(),
                        new RouteValueDictionary {
                            {"area", "SPI.OwnerSite"}
                        },
                        new MvcRouteHandler())
                },

 

For Authorization, I used a regular old attribute filter:

 public class OwnerSiteAuthorization : AuthorizeAttribute
    {
        protected override bool  AuthorizeCore(HttpContextBase httpContext)
        {
            OwnerModel owner = SessionObjects.Owner;

            return owner != null;
        }
        
    }

Works perfectly for me so far, no need to over-complicate things with something as simple as authentication.  Hope I'm not missing some corner case with Orchard that will cause this not to work under certain situations, let me know if so.

Aug 21, 2012 at 10:37 AM

Yes - orchard will find that your class matches the interface, and because you told it to suppress the one already registered, your will be used. That way you can override quite a lot inside a module, without interfering with the original code (and you can just turn your code on/off very easily).

The fun comes when you try to replace the Implementation of IMembership service, then realise that not all the administration screens use it (or are inconsistent in its use - sometimes they just go straight to getting UserParts from the database).....and that IUser references ContentItem directly, tying it rather badly to Orchard. I've been looking at a patch for this, but it is deeply rooted.

Aug 23, 2012 at 7:54 PM

As it turns out, my method of routing Users/Account/Logon to my custom authentication page had the unintended consequences of causing the admin page to redirect to this as well, so now I'm stuck trying to figure out how to send people who wanted to login as admins to the normal Orchard controller without causing a circular redirect mess.

I take it Orchard is not really setup to handle custom authentication without a very complicated override of IAuthenticationService, and even then their are issues as LordSaul has stated.  If anyone else has any ideas on how to authenticate certain parts of our site, please let me know.  It's a shame this is so complicated compared to how trivial it is in most frameworks.

Aug 24, 2012 at 3:22 AM

I've done some highly customized authentication with Orchard.  Usually I've needed only to implement my own membership provider (Orchard.Security.IMembershipService)

What are you actually trying to authenticate with?  

Changing how you authenticate a user would require an override of IMembershipService 

Changing how you maintain who is authenticated is an override of IAuthenticationService

The IMembershipService is a more typical customization. IAuthentcationService defaults to using the FormsAuthentication stuff built into System.Web, and it works fine more most things.  

What I've also done in the past to get around the problem that LordSaul mentioned is that when a user is authenticated with my custom membership service, I make sure there is a user record created for them using the ContentManager.  This allows you to still rely on Orchard's built in methods for authorization while customizing entirely the process for authentication.  Hope that makes sense.  Let me know if you still have questions.

Aug 24, 2012 at 2:29 PM

You don't actually need to create a user, just return the correct contentpart items

 

public class CoolUser :IUser
    {
        public string UserName { get; set; }

        public string Email { get; set; }
       
        public ContentItem ContentItem {get;set;}

        public int Id { get; set; }

        public bool GenerateOrchardUserPart()
        {
            if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Email))
            {
                return false;
            }

            this.ContentItem = new ContentItem();
            UserPart part = new UserPart();
            part.Record = new UserPartRecord();
            part.RegistrationStatus = UserStatus.Approved;
            part.EmailStatus = UserStatus.Approved;
            part.UserName = UserName;
            part.Email = Email;
            this.ContentItem.Weld(part);

            return true;
        }
        
    }
I'm using this currently. Be advised, it is not fully tested, your mileage may vary, and there may be parts of the back end interface that do not understand this. Also, I would hazard that any module assuming it can extend the user part to provide profile properties is not going to work. My preference would be to patch orchard so that content part /item does not leak through the IUser interface, but I've not had too much time to look at it just yet.

Oct 24, 2012 at 10:43 AM

@LordSaul - Your implementation works, but with the new 1.6 relase you also need to set the ContentManager. If not the line listed below will throw an exception (fromContentItemExtensions.cs) :

var metadata = content.ContentItem.ContentManager.GetItemMetadata(content); 

This is how I implemented my "CoolUser". Although it works, it feels very hacky. Do you know of a better implementation? (The Icontentmanager I get injected into my custom implementation of IMemberShipService)

public class CoolUser : IUser
    {
        public ContentItem ContentItem { get; set; }
        public int Id { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }

        public CoolUser(IContentManager contentManager,int id,string username,string email) {
            Id = id;
            UserName = username;
            Email = email;

            ContentItem = new ContentItem();
            ContentItem.ContentManager = contentManager;
            var part = new UserPart();
            part.Record = new UserPartRecord();
            part.RegistrationStatus = UserStatus.Approved;
            part.EmailStatus = UserStatus.Approved;
            part.UserName = UserName;
            part.Email = Email;
            ContentItem.Weld(part);
        }
    }

Oct 24, 2012 at 6:35 PM

I'm currently asking the Orchard team for their thoughts on some changes so that IUser does not depend on ContentItem (which to my mind is a leaky interface). I think the "correct" solution would be to update IUser, and also update back end admin screens to use the IMembershipService, rather than directly accessing the UserPart and RolePart items (see http://orchard.codeplex.com/discussions/400650 for my discussion).

Coordinator
Oct 25, 2012 at 7:26 AM

Anything that can help make Orchard work with external user stores is good in my book. I don't remember enough of the current design details to offer a more informed opinion however.

Nov 18, 2013 at 1:38 PM
Edited Nov 18, 2013 at 1:41 PM
bertrandleroy wrote:
Anything that can help make Orchard work with external user stores is good in my book. I don't remember enough of the current design details to offer a more informed opinion however.
I know this is old but this is how you do it:
[UsedImplicitly]
[OrchardSuppressDependency("Orchard.Users.Services.MembershipService")]
public class CustomMembershipService : IMembershipService
    {
        private readonly IEncryptionService _encryptionService;
        private readonly IEnumerable<IUserEventHandler> _userEventHandlers;
        private readonly IYOUR_CUSTOM_AUTHENTICATIONWorkerService _workerService;
        private readonly IOrchardServices _orchardServices;
        private readonly IMessageManager _messageManager;

        public CustomMembershipService(IYOUR_CUSTOM_AUTHENTICATIONWorkerService  workerService, IOrchardServices orchardServices, IMessageManager messageManager, IEnumerable<IUserEventHandler> userEventHandlers, IClock clock, IEncryptionService encryptionService) {
            _workerService = workerService;
            _orchardServices = orchardServices;
            _messageManager = messageManager;
            _userEventHandlers = userEventHandlers;
            _encryptionService = encryptionService;
            Logger = NullLogger.Instance;
            T = NullLocalizer.Instance;
        }

        public IUser ValidateUser(string userNameOrEmail, string password) {
             // Do what you need to here, if user is valid return the user.As<UserPart>() or return null
        }