We help IT Professionals succeed at work.

I would like to authenticate username and password against Active directory

r3nder
r3nder asked
on
I have username and password fields that I am using. I would like to create a class and when they authenticate activate the controls - Like tables, and buttons and keep the username in memory for use later. in the app
Comment
Watch Question

Todd GerbertSenior Engineer
Top Expert 2010

Commented:
You can use the LogonUser Win32 API to attempt to logon as the user.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
	class Program
	{
		
		static void Main(string[] args)
		{
			Console.WriteLine("Password verified: {0}",
				LogonHelper.VerifyADLogon("MyDomain", "jdoe", "SecretSquirel"));
			Console.ReadKey();
		}
	}

	static class LogonHelper
	{
		[DllImport("kernel32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool CloseHandle(IntPtr hObject);

		[DllImport("advapi32.dll", SetLastError = true)]
		private static extern bool LogonUser(
			string lpszUsername,
			string lpszDomain,
			string lpszPassword,
			LogonType dwLogonType,
			LogonProvider dwLogonProvider,
			out IntPtr phToken
			);

		private enum LogonType : int
		{
			/// <summary>
			/// This logon type is intended for users who will be interactively using the computer, such as a user being logged on  
			/// by a terminal server, remote shell, or similar process.
			/// This logon type has the additional expense of caching logon information for disconnected operations; 
			/// therefore, it is inappropriate for some client/server applications,
			/// such as a mail server.
			/// </summary>
			LOGON32_LOGON_INTERACTIVE = 2,

			/// <summary>
			/// This logon type is intended for high performance servers to authenticate plaintext passwords.

			/// The LogonUser function does not cache credentials for this logon type.
			/// </summary>
			LOGON32_LOGON_NETWORK = 3,

			/// <summary>
			/// This logon type is intended for batch servers, where processes may be executing on behalf of a user without 
			/// their direct intervention. This type is also for higher performance servers that process many plaintext
			/// authentication attempts at a time, such as mail or Web servers. 
			/// The LogonUser function does not cache credentials for this logon type.
			/// </summary>
			LOGON32_LOGON_BATCH = 4,

			/// <summary>
			/// Indicates a service-type logon. The account provided must have the service privilege enabled. 
			/// </summary>
			LOGON32_LOGON_SERVICE = 5,

			/// <summary>
			/// This logon type is for GINA DLLs that log on users who will be interactively using the computer. 
			/// This logon type can generate a unique audit record that shows when the workstation was unlocked. 
			/// </summary>
			LOGON32_LOGON_UNLOCK = 7,

			/// <summary>
			/// This logon type preserves the name and password in the authentication package, which allows the server to make 
			/// connections to other network servers while impersonating the client. A server can accept plaintext credentials 
			/// from a client, call LogonUser, verify that the user can access the system across the network, and still 
			/// communicate with other servers.
			/// NOTE: Windows NT:  This value is not supported. 
			/// </summary>
			LOGON32_LOGON_NETWORK_CLEARTEXT = 8,

			/// <summary>
			/// This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
			/// The new logon session has the same local identifier but uses different credentials for other network connections. 
			/// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
			/// NOTE: Windows NT:  This value is not supported. 
			/// </summary>
			LOGON32_LOGON_NEW_CREDENTIALS = 9,
		}

		private enum LogonProvider : int
		{
			/// <summary>
			/// Use the standard logon provider for the system. 
			/// The default security provider is negotiate, unless you pass NULL for the domain name and the user name 
			/// is not in UPN format. In this case, the default provider is NTLM. 
			/// NOTE: Windows 2000/NT:   The default security provider is NTLM.
			/// </summary>
			LOGON32_PROVIDER_DEFAULT = 0,
		}

		public static bool VerifyADLogon(string DomainName, string Username, string Password)
		{
			IntPtr hToken;

			LogonUser(Username, DomainName, Password, LogonType.LOGON32_LOGON_NETWORK, LogonProvider.LOGON32_PROVIDER_DEFAULT, out hToken);

			if (hToken != IntPtr.Zero)
			{
				CloseHandle(hToken);
				return true;
			}
			else
				return false;
		}
	}
}

Open in new window

Author

Commented:
I am sorry - that doesnt help - Also this is for C# in winforms

Author

Commented:
I have a txtbxUser and a txtBxPass and a btnLogon How do we start from here

Author

Commented:
I am using C# not asp.net
Todd GerbertSenior Engineer
Top Expert 2010

Commented:
Use the LogonHelper class from above, and handle the btnLogon's click event:
(This example has a DomainTextBox, UsernameTextBox, PasswordTextBox text boxes, and a LogonButton button)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void LogonButton_Click(object sender, EventArgs e)
		{
			if (!String.IsNullOrWhiteSpace(DomainTextBox.Text) &&
				!String.IsNullOrWhiteSpace(UsernameTextBox.Text))
			{
				if (LogonHelper.VerifyADLogon(DomainTextBox.Text, UsernameTextBox.Text, PasswordTextBox.Text))
					MessageBox.Show("Password verified.");
				else
					MessageBox.Show("Password failed.");
			}
			else
				MessageBox.Show("You must enter at least a domain name and a username.");
		}
	}
}

Open in new window

Author

Commented:
Here are the errors I am getting

Error 1 'string' does not contain a definition for 'IsNullOrWhiteSpace'
Error 2 'string' does not contain a definition for 'IsNullOrWhiteSpace'
Error 3 The name 'LogonHelper' does not exist in the current context      
for this section
if (!String.IsNullOrWhiteSpace(Domain.text) &&!String.IsNullOrWhiteSpace(txtBxUser.Text))
            {
                if (LogonHelper.VerifyADLogon(Domain.text, txtBxUser.Text, txtBxPass.Text))

I am hard coding the domain - because I dont ask for it - I just ask for username and password

Author

Commented:
I am using vs 2008 and .net version 2.5 and this is a windows form - just to let you know

Author

Commented:
3.5 not 2.5 - sorry
Todd GerbertSenior Engineer
Top Expert 2010

Commented:
You can use String.IsNullOrEmpty(someTextBox.Text.Trim()) instead of String.IsNullOrWhitespace
You need to add the LogonHelper class I posted above, http:#32993692

Author

Commented:
Here is the login for program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Text;
using System.Runtime.InteropServices;


namespace Nomad
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmMain());
            Console.WriteLine("Password verified: {0}",
                LogonHelper.VerifyADLogon("mydomain", "bubba", "mypassword"));
            Console.ReadKey();

        }
        static class LogonHelper
        {
            [DllImport("kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr hObject);

            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool LogonUser(
                string lpszUsername,
                string lpszDomain,
                string lpszPassword,
                LogonType dwLogonType,
                LogonProvider dwLogonProvider,
                out IntPtr phToken
                );

            private enum LogonType : int
            {
                /// <summary>
                /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on  
                /// by a terminal server, remote shell, or similar process.
                /// This logon type has the additional expense of caching logon information for disconnected operations;
                /// therefore, it is inappropriate for some client/server applications,
                /// such as a mail server.
                /// </summary>
                LOGON32_LOGON_INTERACTIVE = 2,

                /// <summary>
                /// This logon type is intended for high performance servers to authenticate plaintext passwords.

                /// The LogonUser function does not cache credentials for this logon type.
                /// </summary>
                LOGON32_LOGON_NETWORK = 3,

                /// <summary>
                /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without
                /// their direct intervention. This type is also for higher performance servers that process many plaintext
                /// authentication attempts at a time, such as mail or Web servers.
                /// The LogonUser function does not cache credentials for this logon type.
                /// </summary>
                LOGON32_LOGON_BATCH = 4,

                /// <summary>
                /// Indicates a service-type logon. The account provided must have the service privilege enabled.
                /// </summary>
                LOGON32_LOGON_SERVICE = 5,

                /// <summary>
                /// This logon type is for GINA DLLs that log on users who will be interactively using the computer.
                /// This logon type can generate a unique audit record that shows when the workstation was unlocked.
                /// </summary>
                LOGON32_LOGON_UNLOCK = 7,

                /// <summary>
                /// This logon type preserves the name and password in the authentication package, which allows the server to make
                /// connections to other network servers while impersonating the client. A server can accept plaintext credentials
                /// from a client, call LogonUser, verify that the user can access the system across the network, and still
                /// communicate with other servers.
                /// NOTE: Windows NT:  This value is not supported.
                /// </summary>
                LOGON32_LOGON_NETWORK_CLEARTEXT = 8,

                /// <summary>
                /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
                /// The new logon session has the same local identifier but uses different credentials for other network connections.
                /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
                /// NOTE: Windows NT:  This value is not supported.
                /// </summary>
                LOGON32_LOGON_NEW_CREDENTIALS = 9,
            }

            private enum LogonProvider : int
            {
                /// <summary>
                /// Use the standard logon provider for the system.
                /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name
                /// is not in UPN format. In this case, the default provider is NTLM.
                /// NOTE: Windows 2000/NT:   The default security provider is NTLM.
                /// </summary>
                LOGON32_PROVIDER_DEFAULT = 0,
            }

            public static bool VerifyADLogon(string DomainName, string Username, string Password)
            {
                IntPtr hToken;

                LogonUser(Username, DomainName, Password, LogonType.LOGON32_LOGON_NETWORK, LogonProvider.LOGON32_PROVIDER_DEFAULT, out hToken);

                if (hToken != IntPtr.Zero)
                {
                    CloseHandle(hToken);
                    return true;
                }
                else
                    return false;
            }
        }


    }
}

And here is the button_click event

private void btnLogon_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrWhiteSpace("mydomain".Trim()) &&!String.IsNullOrWhiteSpace(txtBxUser.Text.Trim()))
            {
                if (LogonHelper.VerifyADLogon("mydomain", txtBxUser.Text, txtBxPass.Text))
                    MessageBox.Show("Password verified.");
                else
                    MessageBox.Show("Password failed.");
            }
            else
                MessageBox.Show("You must enter at least a domain name and a username.");

           
        }
Still has same 3 errors
Todd GerbertSenior Engineer
Top Expert 2010

Commented:
Use String.IsNullOrEmpty(someTextBox.Text.Trim()) instead of String.IsNullOrWhitespace.
Take the LogonHelper class out of the Program class.

Your code:

namespace blahblah
{
  static class Program
  {
    // blah blah
    static class LogonHelper
    {
      // blah blah
    }
  }
}

Correct code:
namespace blahblah
{
  static class Program
  {
    // blah blah
  }
  static class LogonHelper
  {
    // blah blah
  }
}

Open in new window

Author

Commented:
Thats fixed - now 2 errors
Error 1 'string' does not contain a definition for 'IsNullOrWhiteSpace'      
Error 2 'string' does not contain a definition for 'IsNullOrWhiteSpace'      
Senior Engineer
Top Expert 2010
Commented:
I'm starting to feel like a broken record - you need to carefully read the comments. ;)
 
Use String.IsNullOrEmpty(someTextBox.Text.Trim()) instead of String.IsNullOrWhitespace

Author

Commented:
I guess Empty and Whitespace are different :)
1 last question can the individual tabs in a tabcontrol be enabled or disabled?
Thanks your solution was great by the way - point will be on the way soon :)
Todd GerbertSenior Engineer
Top Expert 2010

Commented:
You can enable/disable all the controls in a TabPage, but I don't think you can enable/disable the TabPage itself.
private void DisableSecondTabButton_Click(object sender, EventArgs e)
{
	TabPageEnabled(tabControl1.TabPages[1], false);
}

private void TabPageEnabled(TabPage tabPage, bool Enabled)
{
	foreach (Control control in GetAllControls(tabPage))
		control.Enabled = Enabled;
}

private List<Control> GetAllControls(Control StartingContainer)
{
	List<Control> controlList = new List<Control>();
	foreach (Control ctl in StartingContainer.Controls)
	{
		controlList.Add(ctl);
		if (ctl.Controls.Count > 0)
			controlList.AddRange(GetAllControls(ctl));
	}

	return controlList;
}

Open in new window

Author

Commented:
Excellent to work with