Authentication in web services with HttpWebRequest


Hatteras has three tiers: client, middle, and data.  The middle tier is an ASP.NET web service on a Windows 2003 Server running IIS 6.  When the client (we use C# for both it and the middle tier) connects to the middle tier, it must authenticate with IIS 6.  Depending upon the IIS configuration, that may be negotiate, NTLM, Kerberos, basic, or digest authentication.  Here’s a page on Internet Authentication in .NET.


NOTE:  The code below uses the .NET 2.0 framework (Visual Studio 2005).


In order to authenticate, the client must have credentials that the server recognizes as valid.  For Windows Integrated Authentication (comprising NTLM and Kerberos) using the current logged-on user, the client can use CredentialCache.DefaultCredentials.  Here’s how it looks in code.


using System;
using System.IO;
using System.Net;

class Creds
{
public static void Main(string[] args)
{
Uri uri = new Uri(args[0]);

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Credentials = CredentialCache.DefaultCredentials;

// Get the response.
using (HttpWebResponse res = (HttpWebResponse)req.GetResponse())
{
StreamReader sr = new StreamReader(res.GetResponseStream());
Console.WriteLine(sr.ReadToEnd());
}
}
}


You can find that same type of sample code in MSDN.  However, it gets more interesting if you want to use basic or digest authentication or use credentials other than the current logged-on user.


One interesting fact is that the HttpWebRequest.Credentials property is of type ICredentials, but it only uses instances of NetworkCredential and CredentialCache.  If you implement ICredentials on your own class that is not one of those two classes, you can assign it to the Credentials property, but HttpWebRequest will silently ignore it.


To go further, we need to look at the CredentialCache class itself.  This class is used to hold a set of credentials that are associated with hosts and authentication types.  It has two static properties, one of which we used above, that are the “authentication credentials for the current security context in which the application is running,” which means the logged-on user in our case.



The difference is very subtle.  The documentation for DefaultCredentials says, “The ICredentials instance returned by DefaultCredentials cannot be used to view the user name, password, or domain of the current security context.”  The instance returned by DefaultNetworkCredentials, being new in .NET 2.0 and of type NetworkCredential, would presumably let you get the user name and domain, but it didn’t work for me when I tried it with the following code (UserName returned an empty string).


Console.WriteLine(“User name: ” + CredentialCache.DefaultNetworkCredentials.UserName);

The NetworkCredential class implements both the ICredentials (NetworkCredential GetCredential(Uri uri, String authType)) and ICredentialsByHost (NetworkCredential GetCredential(String host, int port, String authType)) interfaces.  The ICredentialsByHost interface is new in .NET 2.0.


The CredentialCache class has methods that let you add, get, and remove credentials for particular hosts and authentication types.  Using this class, we can manually construct what setting req.Credentials = CredentialCache.DefaultCredentials accomplished in the original example.


        CredentialCache credCache = new CredentialCache();
credCache.Add(new Uri(“http://localhost”), “Negotiate”,
CredentialCache.DefaultNetworkCredentials);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Credentials = credCache;

The authentication type can also be explicitly specified as “NTLM” and “Kerberos” in separate calls to Add().  This page on authentication schemes explains using Negotiate as follows.



Negotiates with the client to determine the authentication scheme. If both client and server support Kerberos, it is used; otherwise NTLM is used.


Let’s say you want to work with basic or digest authentication.  The documentation for CredentialsCache.DefaultCredentials and CredentialsCache.DefaultNetworkCredential says that neither will work with basic or digest.  If we add basic to credentials cache, we get a runtime exception.


        credCache.Add(new Uri(“http://localhost”), “Basic”,
CredentialCache.DefaultNetworkCredentials);

The exception is thrown by the Add() method.


Unhandled Exception: System.ArgumentException: Default credentials cannot be supplied for the Basic authentication scheme.
Parameter name: authType
at System.Net.CredentialCache.Add(Uri uriPrefix, String authType, NetworkCredential cred)

So, in order to use basic or digest, we must create a NetworkCredential object, which is also what we need to do in order to authenticate as some identity other than the logged-on user.  To do that, we create NetworkCredential object and add it to the CredentialCache as follows.


        credCache.Add(new Uri(“http://localhost”), “Basic” /* or “Digest” */,
new NetworkCredential(“me”, “foo”, “DOMAIN”));

Basic authentication sends the password across the wire in plain text.  That’s okay for a secure connection, such as one using SSL, and for situations where you don’t need much security.  Digest authentication hashes the password along with other data from the server before sending a response over the wire.  It’s a significant step up from basic.


Now we need to have the user name and password to create the NetworkCredential object.  There are two parts to this.  First is prompting the user for the name and password.  The second is storing the information.  For prompting there is the Windows dialog that pops up any time you go to a web site that requires authentication.  That dialog includes a “Remember my password” checkbox.  I don’t yet know what the managed API is for that.


To store and retrieve the information, there is the new managed DPAPI explained by Shawn Farkas in several blog postings.



[Update 3:44pm]  The Windows dialog used when IE prompts for name and password is created by the CredUIPromptForCredentials() function.  CredUIConfirmCredentials() is used to save credentials that authenticated successfully, if desired.  Duncan Mackenzie’s MSDN article Using Credential Management in Windows XP and Windows Server 2003 explains how to use it from .NET.


[UPDATE 4/10/2006]  I updated the MSDN links that were broken.

Comments (21)

  1. Q: how do you connect this to a web services call?

  2. Buck Hodges says:

    Michael,

    You would set the credentials property of the SOAP proxy class to either a NetworkCredential or a CredentialCache object.

    If you are using wsdl.exe to generate a proxy class, it derives from System.Web.Services.Protocols.SoapHttpClientProtocol. That class has Credentials property. If you want to use the currently logged-in user’s credentials, you would assign CredentialCache.DefaultCredentials to the Credentials property.

  3. Ben Salemi says:

    Buck,

    I have an interesting situation using windows integrated authentication.

    When I add the credential to the cache with "Negotiated" as the AuthType, I get a 15 second delay before the webservice returns. This 15 second delay is repeated every 15 seconds.

    When I set the the AuthType to "NTLM" the delay does not occur. When I set the AuthType to "Kerberos" the call is immediately rejected with a 401.

    I believe the delay is caused by the failure to contact some resource desired by the negotiation process. We are running over multiple firewalls which have most ports blocked (to prevent hacking attempts). The domain I am attempting to login in to is a child domain of the one I am talking from, but that is not actually working correctly because of all the ports blocked.

    Have you seen anything like this? Any thoughts?

    TIA,

    Ben

  4. How can I use this to connect to a authenticated website, i.e

    http://username:password@www.mysite.com ?

  5. Buck Hodges says:

    Occasionally the question of whether to use the TeamFoundationServerFactory.GetServer() method or the…

  6. Buck Hodges says:

    Occasionally the question of whether to use the TeamFoundationServerFactory . GetServer() method or the

  7. Sebastian says:

    Buck,

    many thanks for your article.

    Without this, my application wouldn’t work.

    Thanks again,

    Sebastian

  8. dennis sacks says:

    I am trying to call a web service that uses basic authentication. I generated the web client proxy using wsdl.exe (I’m using .net 2.0)

    I create a CredentialCache, add credentials to it, and assign the cache to the service.Credentials but am still getting an exception when I call the service (unable to read data from the transport connection: An existing connection was forcibly closed by the remove host"). Do you have any ideas what else might be going on?

  9. buckh says:

    Dennis, that sounds like an issue either with a firewall or with an internet proxy on the network.  To test the latter, try configuring your IE settings not use any proxy (.NET network code uses the IE connection settings, unless you override them in code).

    Buck

  10. Ben says:

    Hey Buck,

    Thanks for this — works like a charm!

  11. Nick says:

    So when you say current logged in user is this user the user that has logged into the Web App or the user under which the asp.net app is running?

    That is, can you have

    End User Login via Internet Explorer to Web App –> Web App invokes Middle Tier with End User Identity?

  12. buckh says:

    Nick, you can have the web app call the middle tier with the identity used by the client if the middle tier is on the same computer if you are using NTLM.  If the web app and the middle tier are on different computers, you either need to have NTLM constrained delegation, which most IT departments will not grant, or you need to use Kerberos (aka Negotiate).

    Buck

  13. Nick says:

    Thanks Buck, Much appreciated!

  14. Caroline says:

    How can I use NetworkCredentials in my code to connect to a authenticated website, i.e

    https://username:password@www.mysite.com ?field1=value1&field2=value2&..

    I tried

    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://username:password@www.mysite.com ?field1=value1&field2=value2");

    req.Credentials = new NetworkCredential("user1", "foo");

    HttpWebResponse res = (HttpWebResponse)req.GetResponse();

    At dis point i m  getting the error – 400:bad request …

    Can someone fix it out… Thanks in advance

  15. buckh says:

    Caroline, unfortunately I don't know as I haven't had a need to do it.  Maybe someone else will comment. Your best bet may be the MSDN forums.

    Buck

  16. Caroline says:

    Thanks Buck,…

  17. IT says:

    Caroline did any one answered you?

    I'm also facing same issue…..

  18. Billa says:

    Is it possible to clear the credential before the request and use default credential?
    Having a problem and explained here http://stackoverflow.com/questions/38357792/set-network-credentials-via-config

Skip to main content