Integrated Windows Authentication

2,055 views
Skip to first unread message

Helge Lenuweit

unread,
Apr 13, 2012, 11:00:44 AM4/13/12
to servic...@googlegroups.com
I'm currently evaluating ServiceStack as an alternative option to porting an existing .NET Remoting based application over to WCF. Having experienced some of the usual pain with WCF already, I'm really impressed with the clean programming model and available options. However there's one thing I am missing which is integrated Windows authentication. I plan to self-host with HttpListener in a Windows service. Tweaking around, I have done the following steps:
- During service startup, I override the Start(urlBase) method and set this.Listener.AuthenticationSchemes = System.Net.AuthenticationSchemes.Ntlm;
- When setting up a (C#) client object (I've tried JsonServiceClient), I set client.Credentials = System.Net.CredentialCache.DefaultCredentials;
- Then in an actual service method, I would retrieve the "Authorization" header like so: base.RequestContext.GetHeader( "Authorization" );
Huuh, I'm getting a string that starts with NTLM, a blank and some weird stuff following: "NTLM TlRMTVNTUAAD...".

Now I'm quite stuck at that point. There are several things I would like to achieve. First is to find out the identity of the user sending the request. I have found an example that simply retrieves a User object on the request, however this is not present on ServiceStack.ServiceHost.HttpRequestContext, and I couldn't seem to cast it to HttpRequest. Second, I would want to mix other authentication schemes (like Anonymous) with NTLM and tried an approach from Ayende Rahien from here. From what I understand, the basic idea is that the authentication scheme is determined in the HttpListener's AuthenticationSchemeSelectorDelegate. While this actually does get called, I haven't been able to retrieve the "Authorization" header when this bit was in place, either in the delegate or in the service method itself.

Has anybody tried something similar or is this supported at all? I'd be happy to hear any ideas.
Helge

Demis Bellot

unread,
Apr 13, 2012, 12:04:15 PM4/13/12
to servic...@googlegroups.com
ServiceStack is just a set of ASP.NET IHttpHandlers and Integrated Windows Auth is only available when hosted via ASP.NET where you are able to take advantage of the built-in Windows Auth infrastructure in IIS/ASP.NET.

When you're running a HttpListener your running self-hosted which accepts the HTTP requests directly and the IIS/ASP.NET windows auth infrastructure is no longer available. 
Also ASP.NET's HttpRequest/HttpRequest types only exist in ASP.NET when you use HttpListener the equivalent types are HttpListenerRequest/HttpListenerResponse these underlying types are available at:

base.RequestContext.Get<IHttpRequest>().OriginalRequest; //When hosted in ASP.NET it returns the current instance of HttpRequest, when self-hosted using HttpListener it returns HttpListenerRequest

ServiceStack does implement its own clean-break auth provider model which supports multiple session and persistence back-ends and custom hooks where you can insert your own logic. You can read more about it at:


Cheers,

Helge Lenuweit

unread,
Apr 13, 2012, 4:36:18 PM4/13/12
to servic...@googlegroups.com
Hello Demis,

thanks for your answer. Especially getting the original request was helpful to understand the differences in hosting from ASP.NET to a plain HttpListener.

However I do think that NTLM/Windows authentication should be possible using HttpListener. Otherwise setting HttpListener.AuthenticationSchemes to IntegratedWindowsAuthentication or Ntlm would not make sense. So I tried further and have come up with the following ideas.

During initialization of the service (AppHostHttpListenerBase), I have overridden Start to set authentication schemes on the listener directly. It is possible to set two schemes, however when a non-authenticated scheme is mixed with e.g. Ntlm or IntegratedWindowsAuthentication a Selector needs to be implemented to determine which authentication scheme is to be used to process a request otherwise the server will never block any request and require authentication:
this.Listener.AuthenticationSchemeSelectorDelegate = request => {
  if( request.RawUrl.Contains("/secure") return AuthenticationSchemes.IntegratedWindowsAuthentication;
  else return AuthenticationSchemes.Anonymous;
};

So far this serves well at telling the server to require authentification on the detected URLs via the selector delegate. I have then used Fiddler to find out what is going on under the hoods. I have also found the following information about NTLM authentication via HTTP (it was referenced in a comment on Ayende Rahien's blog mentioned earlier):

    1: C  --> S   GET ...
    
    2: C <--  S   401 Unauthorized
                  WWW-Authenticate: NTLM
    
    3: C  --> S   GET ...
                  Authorization: NTLM <base64-encoded type-1-message>
    
    4: C <--  S   401 Unauthorized
                  WWW-Authenticate: NTLM <base64-encoded type-2-message>
    
    5: C  --> S   GET ...
                  Authorization: NTLM <base64-encoded type-3-message>
    
    6: C <--  S   200 Ok
    
So in essence we see three requests from the client coming in. The first actually produces an error and thus only informs the client that authentication is required to proceed, so a second request is sent accordingly with the "challenge". The server returns his "response" (type-2) which is actually another error and allows the client to send the third request with a type-3 message. This type3 message being the actual one containing the identity data of the logged-in user that I want to extract on the server side.

Only with the third message the program flow would actually reach the service implementation itself. However I haven't found a way to extract the identity data there, not even when I retrieved the "OriginalRequest". So what I've come up with looks more like a dirty hack, but it makes logical sense so far. I appreciate your comment on whether this might have any unwanted side-effects or makes no sense at all. What I do is override the ProcessRequest method which allows to access HttpListenerContext and inject the detected identity as a custom HTTP header:

protected override void ProcessRequest( HttpListenerContext context ) 
{
    //make sure the custom header is not present so injecting it from outside does no harm
    if( context.Request.Headers[ "MYINTEGRATEDUSER" ] != null ) context.Request.Headers.Remove( "MYINTEGRATEDUSER" );
    
    //check for successful authentication and inject the header
    if( context.User != null && context.User.Identity.IsAuthenticated && !string.IsNullOrEmpty( context.User.Identity.Name ) ) 
    {
        context.Request.Headers.Set( "MYINTEGRATEDUSER", context.User.Identity.Name );
    }

    base.ProcessRequest( context );
}

The header can then be read using base.RequestContext.GetHeader("MYINTEGRATEDUSER") in the service implementation.

Of course I appreciate your comment... Sorry if this all seems trivial to you but I'm new to this and try to make my way into the low-level http world.
Cheers,
Helge

Demis Bellot

unread,
Apr 13, 2012, 5:03:23 PM4/13/12
to servic...@googlegroups.com
Well I'd personally strive to package this functionality into a plugin rather than modify the existing source code so you can update ServiceStack much easier.

You can add Global or Instance Request Filters to inspect the incoming request and attach any metadata to the request. 
Here's some docs on how to register and create filters:

Also I normally add any custom metadata on the Request.Items property which can later be retrieved with either the Request.GetParam() extension method that looks for the property in Headers, QueryString, FormData, Cookies then finally Items:
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/ServiceHost/HttpRequestExtensions.cs#L40 

Or you can also just get it straight from the Request.Items Dictionary.

I generally have a strong dislike of Windows Auth since it requires multiple round-trips and effectively limits the availability of your web services to supporting WinAuth clients and ties your application to windows servers.

Cheers,

Helge Lenuweit

unread,
Apr 15, 2012, 10:06:39 AM4/15/12
to servic...@googlegroups.com
Yeah I agree with your thoughts about Windows Auth, however when only inside corporate environments (our current) customers strongly ask for it... I have to admit that I was attracted to ServiceStack mainly through its support for Mono, and after much pain with WCF on Mono just tried a small solution on both a Mac and a Linux server (using Mono 2.10). Both just worked out of the box which was impressive. However I could not make the move to ServiceStack and sacrifice the Single-Sign-On option on Windows...

Thanks for your information pointers, I will definitely look into Request filters and will use the Items collection. (By the way, is it safe to assume that Item contents cannot be injected from outside because it is just a virtual combination of all the other collections like QueryString, headers etc.?).
Best regards,
Helge

Demis Bellot

unread,
Apr 15, 2012, 10:10:46 AM4/15/12
to servic...@googlegroups.com
Yep, Request.Items is decoupled from the HTTP request and is just a Dictionary of items used to store objects you want to communicate to different parts of your request pipeline.

Cheers,
Reply all
Reply to author
Forward
0 new messages