Master_T
Master_T

Reputation: 7913

OWIN-hosted web api: using windows authentication and allow anonymous access

I have a WebApi project self-hosted using OWIN.

I want to enable Windows Authentication on some of the controller's actions, but allow other actions to be called anonymously.

So, following some examples I found online, I setup my WebApi like this in my Statrup class:

public void Configuration(IAppBuilder appBuilder)
{
    HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
    listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication | AuthenticationSchemes.Anonymous; //Allow both WinAuth and anonymous auth

    //setup routes and other stuff
    //...

    //Confirm configuration
    appBuilder.UseWebApi(config);
}

Then, in my controller, I created two actions:

[HttpGet]
[Authorize]
public HttpResponseMessage ProtectedAction()
{
    //do stuff...
}

[HttpGet]
[AllowAnonymous]
public HttpResponseMessage PublicAction()
{
    //do stuff...
}

This, however, does not work. Calling the action marked AllowAnonymous works as expected, but calling the one marked Authorize always returns a 401 error and the following message:

{
    "Message": "Authorization has been denied for this request."
}

even if the caller supports windows authentication, tested on browsers (Chrome and Edge) and Postman.

What am I missing here?

Upvotes: 2

Views: 1697

Answers (2)

Master_T
Master_T

Reputation: 7913

Well, I found a workaround for this in another question. Instead of specifying multiple auth modes (which doesn't work), you can chose the auth mode for each request at runtime, by setting up an AuthenticationSchemeSelector method like this:

public void Configuration(IAppBuilder app)
{
    HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
            listener.AuthenticationSchemeSelectorDelegate = new 
    AuthenticationSchemeSelector(GetAuthenticationScheme);
}

private AuthenticationSchemes GetAuthenticationScheme(HttpListenerRequest httpRequest)
{
    if(/* some logic... */){
        return AuthenticationSchemes.Anonymous;                    
    }
    else{
        return AuthenticationSchemes.IntegratedWindowsAuthentication;
    }
}

While not ideal (you have to manually check the request URL or some other parameter of the request to decide which method to use) it works.

Upvotes: 1

jidh
jidh

Reputation: 172

Since your description about the question is bit limited I have set-up a demo app, where I implemented OAuthAuthorizationServerProvider as Provider for OAuthAuthorizationServerOptions and override GrantResourceOwnerCredentials and ValidateClientAuthentication

  public void Configuration(IAppBuilder app)
    {
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
        {
            Provider = new ApplicationOAuthBearerAuthenticationProvider()
        });
        app.Use<AuthenticationResponseMiddleware>();
        var options = new OAuthAuthorizationServerOptions
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/api/xxxx"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
            Provider = new OwinAuthorisationProvider()

        };
        app.UseOAuthAuthorizationServer(options);

    }

also tried to have a custom AuthorizeAttribute and added as filters in the configuration class .Filters.Add(new AuthorizeAttribute());

In AuthenticationResponseMiddleware i inherited OwinMiddleware and in the public override async Task Invoke(IOwinContext context) method please inspect the flow of the request.

It hits OAuthBearerAuthenticationProvider first in RequestToken method then to OwinMiddleware class, before going to any DelegatingHandler pipelines, mostly your authentication is implemented in this layer.

Please comment your findings after this check, parallelly I too modify the API and update you, hope it can help you.

Upvotes: 0

Related Questions