Created
August 13, 2014 21:52
-
-
Save jimbuck/f56a10a3edcaa6079e27 to your computer and use it in GitHub Desktop.
.NET helper class for checking user credentials against Windows.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
namespace System.Security.Principal | |
{ | |
/// <summary> | |
/// Leverages the windows UI to prompt for a password | |
/// </summary> | |
public static class AuthenticationHelper | |
{ | |
#region Interop Setup | |
private struct CREDUI_INFO | |
{ | |
public int cbSize; | |
public IntPtr hwndParent; | |
public string pszMessageText; | |
public string pszCaptionText; | |
public IntPtr hbmBanner; | |
} | |
[DllImport("credui")] | |
private static extern CredUIReturnCodes CredUIPromptForCredentials(ref CREDUI_INFO creditUR, | |
string targetName, | |
IntPtr reserved1, | |
int iError, | |
StringBuilder userName, | |
int maxUserName, | |
StringBuilder password, | |
int maxPassword, | |
[MarshalAs(UnmanagedType.Bool)] ref bool pfSave, | |
CREDUI_FLAGS flags); | |
// obtains user token | |
[DllImport("advapi32.dll", SetLastError = true)] | |
private static extern bool LogonUser(string pszUsername, | |
string pszDomain, | |
string pszPassword, | |
int dwLogonType, | |
int dwLogonProvider, | |
ref IntPtr phToken); | |
[Flags] | |
private enum CREDUI_FLAGS | |
{ | |
NONE = 0x0, | |
INCORRECT_PASSWORD = 0x1, | |
DO_NOT_PERSIST = 0x2, | |
REQUEST_ADMINISTRATOR = 0x4, | |
EXCLUDE_CERTIFICATES = 0x8, | |
REQUIRE_CERTIFICATE = 0x10, | |
SHOW_SAVE_CHECK_BOX = 0x40, | |
ALWAYS_SHOW_UI = 0x80, | |
REQUIRE_SMARTCARD = 0x100, | |
PASSWORD_ONLY_OK = 0x200, | |
VALIDATE_USERNAME = 0x400, | |
COMPLETE_USERNAME = 0x800, | |
PERSIST = 0x1000, | |
SERVER_CREDENTIAL = 0x4000, | |
EXPECT_CONFIRMATION = 0x20000, | |
GENERIC_CREDENTIALS = 0x40000, | |
USERNAME_TARGET_CREDENTIALS = 0x80000, | |
KEEP_USERNAME = 0x100000, | |
} | |
private enum CredUIReturnCodes | |
{ | |
NO_ERROR = 0, | |
ERROR_CANCELLED = 1223, | |
ERROR_NO_SUCH_LOGON_SESSION = 1312, | |
ERROR_NOT_FOUND = 1168, | |
ERROR_INVALID_ACCOUNT_NAME = 1315, | |
ERROR_INSUFFICIENT_BUFFER = 122, | |
ERROR_INVALID_PARAMETER = 87, | |
ERROR_INVALID_FLAGS = 1004, | |
} | |
#endregion | |
#region Interop Implementation | |
/// <summary> | |
/// Prompts for password. | |
/// </summary> | |
/// <param name="username">The username.</param> | |
/// <param name="password">The password.</param> | |
/// <param name="extraFlags"></param> | |
/// <param name="title">The title of the login window.</param> | |
/// <param name="message">The message displayed on the login window.</param> | |
/// <returns>True if not errors.</returns> | |
private static bool PromptForPassword(ref string username, out string password,CREDUI_FLAGS extraFlags = CREDUI_FLAGS.NONE, string title = null, string message = null) | |
{ | |
// Setup the flags and variables | |
var userPassword = new StringBuilder(); | |
var userID = new StringBuilder(username); | |
var credUI = new CREDUI_INFO() | |
{ | |
pszCaptionText = title, | |
pszMessageText = message | |
}; | |
credUI.cbSize = Marshal.SizeOf(credUI); | |
bool save = false; | |
var flags = CREDUI_FLAGS.ALWAYS_SHOW_UI | CREDUI_FLAGS.GENERIC_CREDENTIALS | extraFlags; | |
// Prompt the user | |
var returnCode = CredUIPromptForCredentials(ref credUI, ApplicationProductName, IntPtr.Zero, 0, userID, 100, userPassword, 100, ref save, flags); | |
username = userID.ToString(); | |
password = userPassword.ToString(); | |
return (returnCode == CredUIReturnCodes.NO_ERROR); | |
} | |
/// <summary> | |
/// Returns the name of the currently executing assembly. | |
/// </summary> | |
private static string ApplicationProductName | |
{ | |
get | |
{ | |
var assembly = System.Reflection.Assembly.GetExecutingAssembly(); | |
return assembly.GetName().Name; | |
} | |
} | |
#endregion | |
#region Public Methods | |
/// <summary> | |
/// Verifies Windows credentials. | |
/// </summary> | |
/// <param name="username">The username.</param> | |
/// <param name="password">The password.</param> | |
/// <param name="domain">The domain. Defaults to machine name.</param> | |
/// <returns>True if they are logged in.</returns> | |
public static bool VerifyWindowsUser(string username, string password, string domain = null) | |
{ | |
var userHandle = IntPtr.Zero; | |
const int LOGON32_PROVIDER_DEFAULT = 0; | |
const int LOGON32_LOGON_INTERACTIVE = 2; | |
domain = domain ?? Environment.MachineName; | |
// Call LogonUser to get a token for the user | |
var loggedOn = LogonUser(username, | |
domain, | |
password, | |
LOGON32_LOGON_INTERACTIVE, | |
LOGON32_PROVIDER_DEFAULT, | |
ref userHandle); | |
return loggedOn; | |
} | |
/// <summary> | |
/// Prompts the user for credentials. | |
/// </summary> | |
/// <returns></returns> | |
public static bool PromptAndValidate(string title = null, string message = null) | |
{ | |
string username = null; | |
string password; | |
return PromptForPassword(ref username, out password,CREDUI_FLAGS.DO_NOT_PERSIST, title, message) && VerifyWindowsUser(username, password); | |
} | |
/// <summary> | |
/// Prompts the user for credentials and verifies their identity. | |
/// </summary> | |
/// <returns>True if the credentials do not match.</returns> | |
public static bool IsNotCurrentUser(string title = null, string message = null) | |
{ | |
var expectedUsername = Environment.UserName; | |
var flags = CREDUI_FLAGS.DO_NOT_PERSIST | CREDUI_FLAGS.KEEP_USERNAME; | |
string username = expectedUsername; | |
string password; | |
var notLoggedIn = !(PromptForPassword(ref username, out password, flags, title, message) && VerifyWindowsUser(username, password)); | |
var isWrongUser = expectedUsername != username; | |
return notLoggedIn || isWrongUser; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment