4

I'm trying to use the ResourceAuthorize attribute from Thinktecture.IdentityModel, but everything stops because there is no owin context.

I have a owin startup class which setups the authorization manager

[assembly: OwinStartup(typeof(My.WebApi.Startup))]

namespace My.WebApi
{
    public class Startup
    {        
        public void Configuration(IAppBuilder app)
        {
            AuthConfig.Configure(app);
        }
    }
}

public class AuthConfig
{
    public static void Configure(IAppBuilder app)
    {         
        app.UseResourceAuthorization(new ResourceAuthorizationMiddlewareOptions
        {
            Manager = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IResourceAuthorizationManager)) as IResourceAuthorizationManager
        });
    }
}

and I know that it is detected and invoked. But later on, when hitting the following code from IdentityModel, I get a null pointer exception:

    public static Task<bool> CheckAccessAsync(this HttpRequestMessage request, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
    {
        var authorizationContext = new ResourceAuthorizationContext(
            request.GetOwinContext().Authentication.User ?? Principal.Anonymous,
            actions,
            resources);

        return request.CheckAccessAsync(authorizationContext);
    }

I have stepped through and sees that it's caused by the GetOwinContext() returning null, since there is no MS_OwinContext or MS_OwinEnvironment property on the request.

What am I missing?

UPDATE:

I have found that i have an owin.environment property available, but it's part of the `HttpContextWrapper, not the request.

By searching around, I found some code inside of System.Web.Http.WebHost.HttpControllerHandler that looks like it should have converted the owin.environment to an MS_OwinEnvironment, but apparently, that code is never called in my case...

internal static readonly string OwinEnvironmentHttpContextKey = "owin.Environment";
internal static readonly string OwinEnvironmentKey = "MS_OwinEnvironment";

internal static HttpRequestMessage ConvertRequest(HttpContextBase httpContextBase, IHostBufferPolicySelector policySelector)
{
  HttpRequestBase requestBase = httpContextBase.Request;
  HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethodHelper.GetHttpMethod(requestBase.HttpMethod), requestBase.Url);
  bool bufferInput = policySelector == null || policySelector.UseBufferedInputStream((object) httpContextBase);
  httpRequestMessage.Content = HttpControllerHandler.GetStreamContent(requestBase, bufferInput);
  foreach (string str in (NameObjectCollectionBase) requestBase.Headers)
  {
    string[] values = requestBase.Headers.GetValues(str);
    HttpControllerHandler.AddHeaderToHttpRequestMessage(httpRequestMessage, str, values);
  }
  HttpRequestMessageExtensions.SetHttpContext(httpRequestMessage, httpContextBase);
  HttpRequestContext httpRequestContext = (HttpRequestContext) new WebHostHttpRequestContext(httpContextBase, requestBase, httpRequestMessage);
  System.Net.Http.HttpRequestMessageExtensions.SetRequestContext(httpRequestMessage, httpRequestContext);
  IDictionary items = httpContextBase.Items;
  if (items != null && items.Contains((object) HttpControllerHandler.OwinEnvironmentHttpContextKey))
    httpRequestMessage.Properties.Add(HttpControllerHandler.OwinEnvironmentKey, items[(object) HttpControllerHandler.OwinEnvironmentHttpContextKey]);
  httpRequestMessage.Properties.Add(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, (object) HttpControllerHandler._retrieveClientCertificate);
  httpRequestMessage.Properties.Add(HttpPropertyKeys.IsLocalKey, (object) new Lazy<bool>((Func<bool>) (() => requestBase.IsLocal)));
  httpRequestMessage.Properties.Add(HttpPropertyKeys.IncludeErrorDetailKey, (object) new Lazy<bool>((Func<bool>) (() => !httpContextBase.IsCustomErrorEnabled)));
  return httpRequestMessage;
}

UPDATE 2:

Inside of mvc controllers, the context is available. But not in webapi controllers.

Vegar
  • 12,828
  • 16
  • 85
  • 151
  • you could safely delete the global.asax file so you get only one entry point in your application. I'm not sure if this will solve something, but it definitely makes things cleaner.... – Ric .Net Oct 20 '14 at 12:51
  • The web app template that comes with VS creates both global.asax and the startup class. Some says its ok to 'keep around' since it makes it easy to hook event to... ...I'm not sure it will help me anything removing it, but will try. – Vegar Oct 20 '14 at 16:45

1 Answers1

5

A team mate found a solution. He simply added the following line to the owin startup class:

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

Why this solves the issue is another mystery, though. But we are using wsFederation, so I guess it's needed some how. But what if we didn't use wsFed? Would we still need it to get a context? Who knows...

Vegar
  • 12,828
  • 16
  • 85
  • 151
  • Actually it would be enough to call `app.UseStageMarker(PipelineStage.Authenticate);` cos `UseExternalSignInCookie` call it inside itself. – Alexander Lysenko Jan 03 '23 at 13:18