1

I'm trying to move my standard WebApi app over to OWIN, but having a problem with identities and $batch requests.

I currently have a DelegatingHandler that detects and assigns the identity in SendAsync:

// Detect bearer token and build the identity above.
IOwinContext owinContext = request.GetOwinContext();
owinContext.Authentication.User = new ClaimsPrincipal(identity);

And for normal requests, this carries on through to the ODataController.User. However on $batch requests the property returns to an unauthenticated ClaimsIdentity.

Even GetOwinContext returns an IOwinContext without any User. I assume it has created a new context for each batch part, but I cannot see any way of finding the original context.

Any help would be great!

Tim
  • 2,968
  • 5
  • 29
  • 55
  • 1
    Are you using `.ConfigureAwait(false)` anywhere with an `await'? This would cause the http context not to be reused when execution resumes. Also it would be interesting to see what is actually sent to the server (like http headers) when a `batch` request is made from the caller, maybe the fault lies in the client implementation (if a bearer token was not sent) and not on the server. Is there any other insight you can provide on this or relevant code? – Igor Jun 24 '16 at 15:17
  • No I am not using `.ConfigureAwait(false)` anywhere. – Tim Jun 30 '16 at 08:05

3 Answers3

1

In your SendAsync method try with:

request.GetRequestContext().Principal = new ClaimsPrincipal(identity);

This will assign both Thread.CurrentPrincipal and the current OWIN context, (check this link). I am not 100% sure but my guess is that the Principal is not set in the current thread because OData executes $batch sub-requests in different threads, and the OWIN context is lost.

Another option is to assign the identity from the OWIN middleware, in Configuration method, instead of doing it in a DelegatingHandler. This is explained in this answer: https://stackoverflow.com/a/21420794/4067893, that is also somehow related to your issue.

Hope it helps.

Community
  • 1
  • 1
elbecita
  • 2,616
  • 19
  • 28
  • Afraid neither worked. :( I had tried the first method before. `app.Use((context, func)` is only called for the batch request and no for the child requests, so the identity is still lost. – Tim Jun 30 '16 at 11:27
  • But you say that you have the `identity` in your `DelegatingHandler`, right? So, ok, forget about the OWIN middlewere since it is called only on the batch request but not on the sub-requests, and in the `DelegatingHandler`, set the `request.GetRequestContext().Principal = new ClaimsPrincipal(identity);` as I first say in my answer. Is quite weird that this is not working. :( I will edit my answer with another try in a few hours. – elbecita Jul 01 '16 at 07:19
  • The Delegating handler is called for $batch request and again for each child request for WebApi. However OWIN seems to create a new thread for each sub request, which isn't what standard WebApi does. Previously they User would fall through to the children, but I've had to load the identity to the OWIN environment and pull it back out later as a workaround. – Tim Jul 01 '16 at 10:39
1

Currently I've found a work around. If anyone finds something better I'd love to hear it.

In my DelegatingHandler, I have stored the identity in the OwinContext environment:

owinContext.Set<ClaimsIdentity>("customdata:identity", principal);

Then I created a custom AuthorizeAttribute which pulls the current identity out and assigns to the current User.

IOwinContext context = actionContext.Request.GetOwinContext();
owinContext.Authentication.User = context.Get<ClaimsIdentity>("customdata:identity");
actionContext.RequestContext.Principal = owinContext.Authentication.User;
Tim
  • 2,968
  • 5
  • 29
  • 55
0

The sub-requests of batch are executed in separate threads and loose the authenticate principal in those requests.

Try this in your DelegatingHandler.

var principal = new ClaimsPrincipal(identity);

System.Threading.Thread.CurrentPrincipal = principal;
if (System.Web.HttpContext.Current != null) {
    System.Web.HttpContext.Current.User = principal;
}

// Detect bearer token and build the identity above.
IOwinContext owinContext = request.GetOwinContext();
owinContext.Authentication.User = principal;
Nkosi
  • 235,767
  • 35
  • 427
  • 472