AlvinfromDiaspar
AlvinfromDiaspar

Reputation: 6814

How to customize the System.Web.Http.AuthorizeAttribute with Microsoft.Owin.Security?

I've implemented a custom AuthorizeAttribute in my WebAPI (note that this is different from the MVC AuthorizeAttribute).

I've overridden the OnAuthorization method. In this method I check if the user is authenticated. If not authenticated, I challenge the user to login.

Part of my custom logic is to check authenticated users if they are authorized to continue (basically I check their name/email. if it exists in a predefined list, then they have access).

The issue I see is this: After the user successfully authenticates BUT FAILS to be authorized, I see that there is an infinite loop redirection to the login page.

Again, the challenege for user credentials is in the OnAuthorization method. What might be causing this infinite looping, and how to prevent this once user has been determined to have no authorization?

* Updated with snippet *

public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
    base.OnAuthorization(actionContext); // Should this be here?

    var owinContext = HttpContext.Current.GetOwinContext();
    var authenticated = owinContext.Authentication.User.Identity.IsAuthenticated;
    var request = System.Web.HttpContext.Current.Request;

    if (!authenticated)
    {    
        // Challenge user for crednetials
        if (!request.IsAuthenticated)
        {
            // This is where the user is requested to login.
            owinContext.Authentication.Challenge(
                new AuthenticationProperties { RedirectUri = "/" },
                WsFederationAuthenticationDefaults.AuthenticationType);
        }
    }
    else
    {
        // At this point the user ia authenticated.
        // Now lets check if user is authorized for this application.
        var isAuthorized = SecurityHelper.IsUserAuthorized();
        if (isAuthorized)
        {
            // authorized.
            return;
        }

        // not authorized.
        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
    }
}

Upvotes: 3

Views: 6113

Answers (2)

Craig W.
Craig W.

Reputation: 18155

I'm wondering about this bit of code right here:

actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);

Somewhere you must be configuring your OWIN layer using something like the following:

var cookieAuthenticationOptions = new CookieAuthenticationOptions
    {
        LoginPath = new PathString(loginPath)
    }

app.UseCookieAuthentication(cookieAuthenticationOptions);

When you return a 401 from the authentication filter the OWIN infrastructure is automatically going to redirect you to whatever LoginPath you specified. But when trying to satisfy that request it's invoking your filter, but because the user isn't authorized it returns a 401 which causes a redirect to the LoginPath, and so on, and so on.

Because this is an API call you need to handle the 401 differently. The following blog post talks about this situation.

http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/

In a nutshell, when configuring your CookieAuthenticationOptions you need to specify your own Provider and only direct if it's not an AJAX request.

var cookieAuthenticationOptions = new CookieAuthenticationOptions
    {
        LoginPath = new PathString(loginPath),
        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = context =>
            {
                if (!context.Request.IsAjaxRequest())
                { context.Response.Redirect(context.RedirectUri); }
            }
        }
    }

Upvotes: 1

matt_lethargic
matt_lethargic

Reputation: 2786

You could try removing OnAuthorization and adding this:

protected override bool IsAuthorized(HttpActionContext actionContext)
{
    var owinContext = HttpContext.Current.GetOwinContext();
    var authenticated = owinContext.Authentication.User.Identity.IsAuthenticated;

    return authenticated & SecurityHelper.IsUserAuthorized(); 
}

I don't get why you're redirecting on failed authentication, surely an API should just return 401?

Upvotes: 1

Related Questions