Skip to content

Instantly share code, notes, and snippets.

@jimbuck
Created August 13, 2014 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimbuck/f56a10a3edcaa6079e27 to your computer and use it in GitHub Desktop.
Save jimbuck/f56a10a3edcaa6079e27 to your computer and use it in GitHub Desktop.
.NET helper class for checking user credentials against Windows.
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