views:

485

answers:

3

Hi folks,

I've got an API setup. When the user provides an invalid/missing API key, I'm trying to set the Response.StatusCode to 401, something keeps bouncing me to my login page. It's an API ... so I don't want that. I wish to send them the json error message with the code being 401.

url for this sample api is : /api/search/foo?apikey=12345&bar=hi+stack+overflow

What have I done wrong?

Here's some sample code :-

// Do we have an Api Key that is legit?
if (!CheckAPIKey(context))
{
    json = JsonConvert.ExportToString("Invalid API key or no API key was provided.");
    context.Response.StatusCode = 401; // Not authorised.
}
else
{
    ... get json data ...
}

context.Response.Write(json);

Also, i have the following in my web.config, if this helps...

<authentication mode="Forms">
    <forms loginUrl="~/Pages/Login.aspx" protection="Validation" timeout="1000000000" requireSSL="false" slidingExpiration="true" defaultUrl="Default.aspx">
    </forms>
</authentication>

Any ideas?

+1  A: 

Because ASP.NET is handling the "401 Not authorised" status and bouncing the user to the login page - as that's how it handles a 401 message from the server.

You could try setting it to "403 Forbidden" instead, although I think ASP.NET also tends to send those to the login handler as well (which is a bit of a pain), or just a plain "400 Bad Request"

Zhaph - Ben Duguid
"403 Forbidden: -- Authorization will not help and the request SHOULD NOT be repeated. -- If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead." http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
jholster
A: 

This behavior depends on the System.Web.Security.FormsAuthenticationModule that you are using (and is active by default if I can remember right). It monitors not only the AuthenticateRequest event, but also the EndRequest event looking for the HTTP code 401. This is the way other modules (e.g. the System.Web.Security.UrlAuthorizationModule ) signal their "evaluations".

If your url is an API, perhaps you could use the

<location />

element in web.config and fine tune the configuration. HTH

EDIT: something more...

I'm not sure that my suggestion will help you :-) Maybe Zhaph - Ben Duguid's idea is better.

Anyway...

Here is the reference for location Element. In short, you can decide a different set of configurations for a particular path. There is another way not completely equivalent: exploiting the hierarchical nature of .config files.

I assumed that your API is part of a bigger web application...

In your case you should evaluate if, for the URL /api/search/foo (in your example), you want a different behavior. If you need a different one, see the reference for "location" and try to figure out if you can switch off something.

If your application is "just" API, then you could remove all the modules that you don't need (in particular the FormsAuthenticationModule which is responsible for redirect).

Fabrizio C.
Hi Fabrizio ... can u please elaborate on this config element, please? what does it do?
Pure.Krome
Ok, I added something, but... I think Zhaph's solution is better. ;-)
Fabrizio C.
+2  A: 

Try to handle the 401 in the Application_EndRequest of the Globals.asax. You have the forms authentication mode set so it's doing what it's supposed to do, which is to redirect to the Login.aspx page on a 401 status code.

Your code becomes something like:

HttpContext context = HttpContext.Current;
// Do we have an Api Key that is legit?
if (!CheckAPIKey(context))
{
    context.Response.StatusCode = 401; // Not authorised.
}
else
{
    ... get json data ...
}
context.Response.Write(json);

And in the Application_EndRequest something like:

protected void Application_EndRequest(object sender, EventArgs e)
{
    HttpContext context = HttpContext.Current;
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        json = JsonConvert.ExportToString("Invalid API key or no API key was provided.");
        context.Response.Write(json);
    }
}
David Glass