HttpListener-based WebDAV Server Authentication

Integrated Windows Authentication (IWA)

HttpListener has a built-in support for authentication against Windows users and Active Directory users. To activate IWA, you must set HttpListener.AuthenticationSchemes property to NTLM. The logged in user then will be available via HttpListenerContext.User property.

HttpListener listener = new HttpListener();
listener.Prefixes.Add(uriPrefix);
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
listener.Start();
while (true)
{
    WDEngine engine = new WDEngine();
    HttpListenerContext context = listener.GetContext();
    WDRequest request = new WDRequest(context.Request, context.User);
    WDResponse response = new WDResponse(context.Response);
    engine.Run(request, response);

    if (response.StatusCode == 401)
    {
       context.Response.AddHeader("WWW-Authenticate", "NTLM"); // show login dialog
       byte[] message = new UTF8Encoding().GetBytes("Access denied");
       context.Response.OutputStream.Write(message, 0, message.Length);
    }
    try
    {
      context.Response.Close();
    }
    catch
    {
        // client closed connection before the content was sent
    }
}
public WebDAVResponse WriteToStream(Stream output, long startIndex, long count)
{
    if (request.User.Identity.Name.ToLower() != "domain\\user1")
        return new AccessDeniedResponse(); // sets 401 status code

    ...

    return new OkResponse();
}

HttpListener does not attach WWW-Authenticate header, so you must set it yourself to display the login dialog.

Configuring Impersonation

To run your code on behalf of the Windows user account accessing the server configure IWA and call WindowsIdentity.Impersonate() method:

HttpListener listener = new HttpListener();
listener.Prefixes.Add(uriPrefix);
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
listener.Start();
...
HttpListenerContext context = listener.GetContext();
...
WindowsIdentity identity = (WindowsIdentity)context.User.Identity;
WindowsImpersonationContext wic = identity.Impersonate();
// Run code on behalf of the client
engine.Run(request, response);
wic.Undo();

HttpListener Kerberos Authentication

To access remote servers, you may need a delegation level of impersonation. To setup delegation, your server must use Kerberos authentication. By default, Active Directory allows only the Network Service and Local System accounts to use Kerberos. Usually to run your HttpListener WebDAV server under one of this accounts you will have to create a Windows Service.

To setup HttpListener for Kerberos authentication set HttpListener.AuthenticationSchemes property to IntegratedWindowsAuthentication:

listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;

Basic Authentication Against Custom Users Storage

While HttpListener has basic authentication support it does not authenticate against windows accounts and domain accounts. HttpListener will simply extract and decode username and password for you from HTTP headers. You can read user credentials as properties of HttpListenerBasicIdentity class and authenticate against your custom users store.

HttpListener listener = new HttpListener();
listener.Prefixes.Add(uriPrefix);
listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
listener.Start();

while (true)
{
    WDEngine engine = new WDEngine();
    HttpListenerContext context = listener.GetContext();
    HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
    if ((identity.Name == "User1") && (identity.Password == "pwd"))
    {
        WDRequest request = new WDRequest(context.Request, context.User);
        WDResponse response = new WDResponse(context.Response);
        engine.Run(request, response);
    }
    else
    {
        context.Response.StatusCode = 401;
    }
    if (context.Response.StatusCode == 401)
    {
        context.Response.AddHeader("WWW-Authenticate",
            "Basic Realm=\"My WebDAV Server\""); // show login dialog
        byte[] message = new UTF8Encoding().GetBytes("Access denied");
        context.Response.ContentLength64 = message.Length;
        context.Response.OutputStream.Write(message, 0, message.Length);
    }
    try
    {
        context.Response.Close();
    }
    catch
    {
        // client closed connection before the content was sent
    }
}

public WebDAVResponse CreateFolder(string name)
{
    if (request.User.Identity.Name != "User1")
        return new AccessDeniedResponse();
    ...

    return new CreatedResponse();
}

HttpListener does not send WWW-Authenticate header, so you must set it yourself to display the login dialog.

 

See Also

Using Basic Authentication with Windows Vista Client

Using Digest Authentication