Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle authenticatication with HttpWebRequest.AllowAutoRedirect?

According to MSDN, when HttpWebRequest.AllowAutoRedirect property is true, redirects will clear authentication headers. The workaround given is to implement IAuthenticationModule to handle authentication:

The Authorization header is cleared on auto-redirects and HttpWebRequest automatically tries to re-authenticate to the redirected location. In practice, this means that an application can't put custom authentication information into the Authorization header if it is possible to encounter redirection. Instead, the application must implement and register a custom authentication module. The System.Net.AuthenticationManager and related class are used to implement a custom authentication module. The AuthenticationManager.Register method registers a custom authentication module.

I created a basic implementation of this interface:

public class CustomBasic : IAuthenticationModule
{
    public CustomBasic() { }

    public string AuthenticationType { get { return "Basic"; } }

    public bool CanPreAuthenticate { get { return true; } }

    private bool checkChallenge(string challenge, string domain)
    {
        if (challenge.IndexOf("Basic", StringComparison.InvariantCultureIgnoreCase) == -1) { return false; }
        if (!string.IsNullOrEmpty(domain) && challenge.IndexOf(domain, StringComparison.InvariantCultureIgnoreCase) == -1) { return false; }
        return true;
    }

    public Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
    {
        return authenticate(request, credentials);
    }

    public Authorization Authenticate(String challenge, WebRequest request, ICredentials credentials)
    {
        if (!checkChallenge(challenge, string.Empty)) { return null; }
        return this.authenticate(request, credentials);
    }

    private Authorization authenticate(WebRequest webRequest, ICredentials credentials)
    {
        NetworkCredential requestCredentials = credentials.GetCredential(webRequest.RequestUri, this.AuthenticationType);
        return (new Authorization(string.Format("{0} {1}", this.AuthenticationType, Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", requestCredentials.UserName, requestCredentials.Password))))));
    }
}

and a simple driver to exercise the functionality:

public class Program
{
    static void Main(string[] args)
    {
        // replaces the existing handler for Basic authentication
        AuthenticationManager.Register(new CustomBasic());
        // make a request that requires authentication
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"https://www.SomeUrlThatRequiresAuthentication.com");
        request.Method = "GET";
        request.KeepAlive = false;
        request.ContentType = "text/plain";
        request.AllowAutoRedirect = true;
        request.Credentials = new NetworkCredential("userName", "password");
        HttpWebResponse result = (HttpWebResponse)request.GetResponse();
    }
}

When I make a request that doesn't redirect, the Authenticate method on my class is called, and authentication succeeds. When I make a request that reutrns a 307 (temporary redirect) response, no methods of my class are called, and authentication fails. What's going on here?

I'd rather not disable auto redirect and write custom logic to handle 3xx responses myself. How can I get my authentication logic to work with auto redirect?

like image 330
Kevin Kibler Avatar asked Oct 31 '12 13:10

Kevin Kibler


1 Answers

In place of the NetworkCredential, you should pass a CredentialCache for the request.Credentials.

CredentialCache cache = new CredentialCache();
cache.Add(new Uri(@"https://www.SomeUrlThatRequiresAuthentication.com", "Basic", new NetworkCredential("username", "password"));
request.Credentials = cache;

According to the MSDN documentation:

The CredentialCache class stores credentials for multiple Internet resources. Applications that need to access multiple resources can store the credentials for those resources in a CredentialCache instance that then provides the proper set of credentials to the Internet resource when required. When the GetCredential method is called, it compares the Uniform Resource Identifier (URI) and authentication type provided with those stored in the cache and returns the first set of credentials that match.

like image 51
Crater Avatar answered Sep 21 '22 11:09

Crater