Setting Thread.CurrentPrincipal kills WebApi response. (NOT REALLY - RESOLVED)

Topics: Web Api
Jul 12, 2011 at 2:45 PM
Edited Jul 12, 2011 at 2:46 PM

I have a custom channel which handles session authentication.  It simply looks for a session header, uses the application's security manager which in turn uses the cache manager to look-up a valid cache entry for the session id. Returns the cache item (IPrincipal) if found, and sets the Thread.CurrentPrincipal accordingly.

Everything works perfectly it seems. The principal gets set. The request hits the correct service operation. I can authorize the request further on based upon the Thread's principal. The response gets json serialized into a stream. However, no response ever makes it back to the user.

If I comment out the line "Thread.CurrentPrincipal = authenticatedUserPrincipal;", than everything works correctly and I get my 200 OK response with the serialized data. As soon as I set the CurrentPrincipal, the response just hangs even though the everything seems to work as it should.

Does anyone have any ideas?

internal class ApiSessionAuthenticationChannel : DelegatingChannel
{
    ....

    /// <summary>
    /// Sends the async request.
    /// </summary>
    /// <param name="request">The request.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns><see cref="Task&lt;HttpResponseMessage&gt;"/> instance to send.</returns>
    /// <remarks></remarks>
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var securityManager = _Container.Resolve<ISecurityManager>();

        // TODO: (DG) Figure out how client will be calling the network services!
        IEnumerable<String> sessions;
        if (request.Headers.TryGetValues("NetworkSession", out sessions))
        {
            var authenticatedUserPrincipal = securityManager.GetPrincipal(new GenericToken(sessions.First()));
            if (authenticatedUserPrincipal != null)
            {
                Thread.CurrentPrincipal = authenticatedUserPrincipal;
            }
        }

        return base.SendAsync(request, cancellationToken);
    }
}
Jul 12, 2011 at 3:08 PM

I don't know the cause of your problem. However, due to the async nature of message handlers, I would say that attaching the principal to the current thread is a bad idea. There is no guarantee that the operation thread is the same as the message handler thread.

I would instead attach the principal to the message properties. Then, I have the following choices:

1) On the operation, retrieve the principal from the message's properties

2) Create an operation handler that fetches the principal from the message's properties and injects it as an operation parameter or attaches it to the current thread (the operation handler pipeline is synchronous)

Jul 12, 2011 at 7:26 PM
Edited Jul 12, 2011 at 7:27 PM

I really appreciate the reply.  I've tried using HttpRequestMessage.Properties instead.  Still no luck.  In my service base class I set

Thread.CurrentPrincipal = (IPrincipal)_RequestMessage.Properties["RequestPrincipal"];

The response still hangs and never returns.  It's very strange.

Jul 12, 2011 at 8:48 PM

Well I figured it out!  After trying to set CurrentPrincipal to a new GenericPrincipal(new GenericIdentity()) ... the service returned perfectly.

So I figured it must have something to do with the serialization of my custom IPrincipal / IIdentity concrete implementations.  I originally had a [Serializable] attribute on both classes.  After switching to [DataContract] and [DataMember] attributes, everything worked great.  I've no idea why.  I'm not even sending those in the service response.