Windows Authentication in Orchard

Topics: Customizing Orchard, Writing modules
Jun 1, 2011 at 10:51 PM

Hi,

I'm using Orchard in the corporate network. All users in our network have windows accounts with login like FirstName.LastName1. I need to integrate windows authentication in Orchard and use this accounts as user names, but I just need to parse and display user names as 'FirstName LastName' without dots and numbers (we may to have more than two accounts with same name and password).

I can write module that registers a new content path with user DisplayName and use my custom theme to display this (may be I'm wrong). And I can use custom controller and add new users automatically in my module using membership service (like open authentication module does), but I don't know how write this module/controller for windows authentication.

Have I any chance to integrate windows authentication and 'DisplayName' (as I can see UserName is used in other modules like Orchard.Comments, but I need to show in comment Display name too) in Orchard without any change in Orchard.Core/Orchard core modules?

Thanks!

Coordinator
Jun 1, 2011 at 11:05 PM

You'll want to start by looking at the code for those modules: http://orchardproject.net/gallery/List/Search?packageType=Modules&searchTerm=authentication

Jun 2, 2011 at 2:06 PM
Edited Jun 2, 2011 at 2:10 PM
bertrandleroy wrote:

You'll want to start by looking at the code for those modules: http://orchardproject.net/gallery/List/Search?packageType=Modules&searchTerm=authentication

Thank you for the link, I didn't find solution in this modules (all of this uses forms authentication). But I find that I can write my own authentication service that will replace built-in.

I add my own implementation of IAuthenticationService in my custom module:

public class CustomAuthenticationService : IAuthenticationService
	{
		private readonly IAuthenticationService systemAuthenticationService;
		private readonly IMembershipService membershipService;
		private readonly IHttpContextAccessor httpContextAccessor;

		public CustomAuthenticationService(IClock clock, IContentManager contentManager, IMembershipService membershipService, IHttpContextAccessor httpContextAccessor)
		{
			this.membershipService = membershipService;
			this.httpContextAccessor = httpContextAccessor;
			systemAuthenticationService = new FormsAuthenticationService(clock, contentManager, httpContextAccessor);
		}

		#region Implementation of IAuthenticationService

		public void SignIn(IUser user, bool createPersistentCookie)
		{
			systemAuthenticationService.SignIn(user, createPersistentCookie);
		}

		public void SignOut()
		{
			systemAuthenticationService.SignOut();
		}

		public void SetAuthenticatedUserForRequest(IUser user)
		{
			systemAuthenticationService.SetAuthenticatedUserForRequest(user);
		}

		public IUser GetAuthenticatedUser()
		{
			var user = systemAuthenticationService.GetAuthenticatedUser();
			if (user == null)
			{
				var httpContext = httpContextAccessor.Current();
				if (httpContext != null && httpContext.Request.IsAuthenticated && !(httpContext.User.Identity is WindowsIdentity))
				{
					var windowsIdentity = (WindowsIdentity) httpContext.User.Identity;
					user = membershipService.GetUser(windowsIdentity.Name) ?? membershipService.CreateUser(new CreateUserParams(
					                                                                                       	windowsIdentity.Name,
					                                                                                       	Guid.NewGuid().ToString(),
					                                                                                       	windowsIdentity.Name +
					                                                                                       	"@someprefixe.com",
					                                                                                       	Guid.NewGuid().ToString(),
					                                                                                       	Guid.NewGuid().ToString(),
					                                                                                       	true));
				}
			}

			return user;
		}

Orchard did not replace built-in module with my one. May be I need to add some attribute to the my class or it impossible to override built-in authentication?

PS: I can provide modified orchard web.config.

Thanks!

Jun 2, 2011 at 4:07 PM
Edited Jun 2, 2011 at 6:11 PM

I found solution by myself (Adds new atrtribute [OrchardSuppressDependency("Orchard.Security.Providers.FormsAuthenticationService")]):

 

[OrchardSuppressDependency("Orchard.Security.Providers.FormsAuthenticationService")]
	public class CustomAuthenticationService : IAuthenticationService
	{
		private readonly IAuthenticationService systemAuthenticationService;
		private readonly IMembershipService membershipService;
		private readonly IHttpContextAccessor httpContextAccessor;
		private readonly IRoleService roleService;
		private readonly IRepository<UserRolesPartRecord> userRolesRepository;

		public CustomAuthenticationService(
			IClock clock,
			IContentManager contentManager,
			IMembershipService membershipService,
			IRoleService roleService,
			IHttpContextAccessor httpContextAccessor,
			IRepository<UserRolesPartRecord> userRolesRepository)
		{
			this.membershipService = membershipService;
			this.roleService = roleService;
			this.httpContextAccessor = httpContextAccessor;
			this.userRolesRepository = userRolesRepository;
			systemAuthenticationService = new FormsAuthenticationService(clock, contentManager, httpContextAccessor);
		}

		#region Implementation of IAuthenticationService

		public void SignIn(IUser user, bool createPersistentCookie)
		{
			systemAuthenticationService.SignIn(user, createPersistentCookie);
		}

		public void SignOut()
		{
			systemAuthenticationService.SignOut();
		}

		public void SetAuthenticatedUserForRequest(IUser user)
		{
			systemAuthenticationService.SetAuthenticatedUserForRequest(user);
		}

		private static string UserNameFromWindowsIdentity(WindowsIdentity identity)
		{
			string name = identity.Name;
			if (string.IsNullOrEmpty(name))
				return name;
			int slashIndex = name.IndexOf('\\');
			if (slashIndex < 1 && slashIndex > (name.Length - 1))
				return name;
			return name.Substring(slashIndex + 1);
		}

		public IUser GetAuthenticatedUser()
		{
			var user = systemAuthenticationService.GetAuthenticatedUser();
			if (user == null)
			{
				var httpContext = httpContextAccessor.Current();
				if (httpContext != null && httpContext.Request.IsAuthenticated && (httpContext.User.Identity is WindowsIdentity))
				{
					var windowsIdentity = (WindowsIdentity) httpContext.User.Identity;
					string userName = UserNameFromWindowsIdentity(windowsIdentity);
					user = membershipService.GetUser(userName);
					if (user == null)
					{
						user = membershipService.CreateUser(new CreateUserParams(
						                                    	userName,
						                                    	Guid.NewGuid().ToString(),
						                                    	userName +
						                                    	"@semedomain.com",
						                                    	Guid.NewGuid().ToString(),
						                                    	Guid.NewGuid().ToString(),
						                                    	true));
						if (user != null)
						{
							var role = roleService.GetRoleByName("Author");
							userRolesRepository.Create(new UserRolesPartRecord {Role = role, UserId = user.Id});
						}
					}
					if (user != null)
						SetAuthenticatedUserForRequest(user);
				}
			}

			return user;
		}

		#endregion
	}
Coordinator
Jun 2, 2011 at 4:38 PM

You can provide it as a module then,

Jun 2, 2011 at 6:02 PM
Edited Jun 2, 2011 at 6:21 PM

Of course, when I finish it.

Jun 2, 2011 at 6:09 PM

One more question. How can I add Redirect to another page from this service? Is it possible?

Thanks!

Jun 2, 2011 at 8:08 PM

I think it would be best to let the controller do the redirection, not your service.
You can do something like:
return RedirectToAction("RegistrationPending", "Account", new { area = "Orchard.Users" }); 

Jun 2, 2011 at 9:36 PM
santiagoIT wrote:

I think it would be best to let the controller do the redirection, not your service.
You can do something like:
return RedirectToAction("RegistrationPending", "Account", new { area = "Orchard.Users" }); 

Thank you santiagoIT! I'll try to use controller.

Jun 7, 2011 at 6:58 PM

Hi AlexZh...I'm very interested in your module when you complete it. I would love to get our intranet off of Sitefinity and on to Orchard, but the one requirement is Single Sign On. I would love to see your module in the Gallery when completed. Thanks for the great effort!

Jun 14, 2011 at 3:13 PM
Edited Jun 14, 2011 at 3:14 PM

Module with simple windows authentication:

http://orchardproject.net/gallery/List/Modules/Orchard.Module.AlexZh.WindowsAuthentication

http://orchardwinauth.codeplex.com/