views:

725

answers:

2

This might seem like a basic question and back to Http protocol 101. But I am having difficulty in understanding how Basic Authentication works. I am implementing a windows service and need it to be secure. I would like to obtain the user name and password and authenticate the user to a custom user store. I also want to minimize the number of login calls, as a login call represents a call to our SQL server. What I have started so far is something like this:

The way I see it the UserAuthorized function has to make my custom login call. But, I don't want to have to do that every single time. Does basic authentication maintain if you are logged in, or is there a caching thread safe solution that I should explore.

Oh yeah I'd also like to make it so that when a user is authenticate and object is created and maintained in the thread for the listener to refer to on subsequent callbacks for a user/connection. But since ListenerCallback is static I'm not sure how this can be accomplished.

Thanks in advance for any help, I truly appreciate it and StackOverflow.

public void ThreadProc() {
    string uriPrefix = ConfigurationManager.AppSettings["ListenerPrefix"];
    HttpListener listener = new HttpListener();
    listener.Prefixes.Add(uriPrefix);
    listener.AuthenticationSchemes = AuthenticationSchemes.Basic;

    listener.Start();

    Console.WriteLine("Start listening on " + uriPrefix);
    Console.WriteLine("Press Control-C to stop listener...");

    while (listening) {
        IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
        result.AsyncWaitHandle.WaitOne();
    }
}

public static void ListenerCallback(IAsyncResult result) {
    HttpListener listener = (HttpListener)result.AsyncState;
    HttpListenerContext context = listener.EndGetContext(result);
    WebDavEngine engine = new WebDavEngine(context.User);

    context.Response.SendChunked = false;
    FileLogger.Level = LogLevel.Debug;

    engine.IgnoreExceptions = false;

    if (UserAutorized(context)) {
        try {
            engine.Run(context, listener.Prefixes);
            engine.CommitTransaction();
        } catch {
            engine.RollBackTransaction();
        } finally {
            engine.CloseConnection();
        }
    } else
        context.Response.StatusCode = 401;

    if (context.Response.StatusCode == 401)
        ShowLoginDialog(context, context.Response);

    try {
        context.Response.Close();
    } catch {
        // client closed connection before the content was sent
    }
}

private static bool UserAutorized(HttpListenerContext context) {
    HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;

    if (!identity.IsAuthenticated) {
        string username = identity.Name;

        // workaround for Windows Vista Basic authentication. User name may be submitted in the following format: Machine\User.
        int ind = username.LastIndexOf('\\');
        if (ind > 0)
            username = username.Remove(0, ind + 1);


        Console.WriteLine("Trying Authentication since identity is NOT authenticated");

        return false;
    } else {
        Console.WriteLine("identity.IsAuthenticated: " + identity.IsAuthenticated.ToString());
        return identity.IsAuthenticated;
    }            
}

EDIT: +1 for the document alone, that really opened my eyes to the caparison of authentication schemes and how they work. Unless I a readying this wrong, it does appear that a Digest scheme might be able to maintain a "session" or at least an expiration to retry my custom authentication.

+3  A: 

HTTP Basic requires the login credentials with every request. HTTP doesn't have any concept of session, so you can't really tell if someone is "already logged on".

Hank Gay
A: 

Thanks for the spec, I appreciated it. I did manage to solve my issue and it was not spec related but rather a design/hosting issue.

Kyle LeNeau