Sunday, March 11, 2007

How to use IE authentication programmatically

So, there are two methods deals with credential dialog of IE

CredUIPromptForCredentials – for pop it up and CredUIConfirmCredentials for persist those settings in password manager (optional). So, in order to call it explicitly, you should implement them via P/Invoke like this

[DllImport("credui.dll", EntryPoint = "CredUIConfirmCredentialsW", CharSet = CharSet.Unicode)]

       private static extern CredUIReturnCodes CredUIConfirmCredentials(string targetName, [MarshalAs(UnmanagedType.Bool)] bool confirm);

[DllImport("credui.dll", EntryPoint = "CredUIPromptForCredentials", CharSet = CharSet.Ansi)]

       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);

Please pay attention, that you’ll need CREDI_INFO structure and CREDI_FLAG and CredUIReturnCodes enums, that those function using

public struct CREDUI_INFO

{

   public int cbSize;

   public IntPtr hwndParent;

   public string pszMessageText;

   public string pszCaptionText;

   public IntPtr hbmBanner;

}

[Flags]

enum CREDUI_FLAGS

{

   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,

}

public 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,

}

Now, when you have those calls, you may want to make some wrappers (like those)

const int MAX_USER_NAME = 100;

const int MAX_PASSWORD = 100;

const int MAX_DOMAIN = 100;

static CredUIReturnCodes PromptForCredentials(ref CREDUI_INFO creditUI, string targetName, int netError, ref string userName, ref string password, ref bool save, CREDUI_FLAGS flags)

{

                         StringBuilder user = new StringBuilder(MAX_USER_NAME);

                         StringBuilder pwd = new StringBuilder(MAX_PASSWORD);

                         creditUI.cbSize = Marshal.SizeOf(creditUI);

                         CredUIReturnCodes result = CredUIPromptForCredentials(ref creditUI, targetName,IntPtr.Zero, netError,user, MAX_USER_NAME, pwd, MAX_PASSWORD, ref save, flags);

                                                  userName = user.ToString();

                                                  password = pwd.ToString();

                         return result;

}

CredUIReturnCodes AskCredit(ref string username, ref string password)

{

                         string host = url.Host;

                         CREDUI_INFO info = new CREDUI_INFO();

                         info.pszCaptionText = host;

                         info.pszMessageText = "The server "+host+" requires a username and password. \r\n\r\n\r\nWarning: This server is requesting that your username and password be sent in an insecure manner (basic                                                                            authentication without a secure connection).";

                         CREDUI_FLAGS flags = CREDUI_FLAGS.GENERIC_CREDENTIALS |

                                                  CREDUI_FLAGS.SHOW_SAVE_CHECK_BOX |

                                                  CREDUI_FLAGS.ALWAYS_SHOW_UI |

                                                  CREDUI_FLAGS.EXPECT_CONFIRMATION;

                         bool savePwd = false;

                         CredUIReturnCodes result = PromptForCredentials(ref info, host, 0, ref username,

                         ref password, ref savePwd, flags);

return result;

}

After it, you should call AskCredit to get the credit window pop up and get secured username and password from them. Something like this

CredUIReturnCodes code = AskCredit(ref user, ref pwd);

                         if (code == CredUIReturnCodes.NO_ERROR)

                         {

//Do something useful

                         }

The next part is to provide those credentials to your HTTPRequest. See the sample doing that

//create some string to get response into

string str = string.Empty;

HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;

HttpWebResponse res = null;

//while you don’t OK keep trying

while (res == null || res.StatusCode != HttpStatusCode.OK)

{

                         try

                         {

//you are fine, do the rest

                         res = req.GetResponse() as HttpWebResponse;

                         using (Stream s = res.GetResponseStream())

                         {

                                                  using (StreamReader sr = new StreamReader(s))

                                                  {

                                                                           str = sr.ReadToEnd();

                                                                           sr.Close();

                                                  }

                         s.Close();

                         }

                         res.Close();

                         }

//You got an exception

                         catch (WebException e)

{

//If this exeption is not security one, you have nothing to do, the only thing is, maybe, check you network connection

                         if (e.Status == WebExceptionStatus.ProtocolError)

                                                  {

                                                                           res = e.Response as HttpWebResponse;

//Now check what the problem and if it’s security, threat it

                                                                           if (res.StatusCode == HttpStatusCode.Unauthorized)

                                                                           {

                                                                                                    string user = "";

                                                                                                    string pwd = "";

//pop you window and get information

                                                                                                    CredUIReturnCodes code = AskCredit(ref user, ref pwd);

                                                                                                    if (code == CredUIReturnCodes.NO_ERROR)

                                                                                                    {

//create new credential set and use it as required

                                                                                                                             CredentialCache creds = new CredentialCache();

                                                                                                                             creds.Add(url, "Basic", new NetworkCredential(user,pwd));

                                                                                                    //creds.Add(url, "Digest", new NetworkCredential(user, pwd));

                                                                                                    //creds.Add(url, "Negotiate", CredentialCache.DefaultNetworkCredentials);

                                                                                                                             req.Credentials = creds;

                                                                                                    }

                                                                           }

                                                  }

                         }

}

Well done. Now you have an application, that pops Internet Explorer authentication window and optionally saves your passwords into security password store within the browser

No comments: