3

I am trying to setup a very basic Forms authentication example.

It is correctly redirecting unauthenticated users to the login page and on submit verifying the credentials and if correct calling:

FormsAuthentication.RedirectFromLoginPage(username.Text, false);

If the user is one named in the authorization section they get their page. If not it bounces them back to the login page with no error.

How can I redirect correctly authenticated but unauthorized users to a specific error page or detect the authorization error to display an error message on the login page bounce back?

Here is my web.config

   <authentication mode="Forms">
      <forms name=".ASPXAUTH" loginUrl="/forms/Login" />
    </authentication>
    <authorization>
      <deny users="?" />
      <allow users="username1, username2" />
      <deny users="*" />
    </authorization>

Update:

Based on the answers / comments / research I've got two working solutions.

  1. Put the following in the Page_Load method of your Login form:

        if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
        {
            // This is an unauthorized, authenticated request... 
            Response.Redirect("FailedAuthorization.aspx");
        }
    

OR

  1. Put the following in your Global.aspx file:

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        if (Response.StatusCode == 401)
        {
            //Use the built in 403 Forbidden response
            Response.StatusCode = 403;
    
            //OR redirect to custom page
            //Response.Redirect("FailedAuthorization.aspx");
        }
    }
    
    protected void Application_AuthenticateRequest(object sender, EventArgs e)
    {
        if (Request.IsAuthenticated)
        {
            // Requires ASP.NET >= 4.5
            Response.SuppressFormsAuthenticationRedirect = true;
        }
    }
    

Thank you for all the help with this!

Baxter
  • 5,633
  • 24
  • 69
  • 105
  • 1
    How many locations are you re-directing users to? If you are branching your users to different locations you'll have to implement your own redirect logic. Your example should have had a – jtimperley Mar 17 '15 at 00:57
  • I only need to redirect to a single location. A page stating they are not authorized. How would I tie into the authorization system to implement my own redirect logic? – Baxter Mar 17 '15 at 02:49

3 Answers3

3

Unfortunately, this is one of those things that ASP.NET continually gets wrong. Even though MS and the .NET framework team full well understand the difference between authentication and authorization, they still insist on treating unauthorized as unauthenticated. I don't know why that is.

This is just a quirk of the FormsAuthentication module handler, in that it returns a 401 Unauthorized instead of a 403 Forbidden. (it doesn't help that the HTTP standard confuses Authentication with authorization as well in this manner).

This is not something you can easily override, so your only recourse would be to do something like checking in your Login page to see if they are already logged in, and if they were redirected... it's not foolproof, but it's one way to handle it.

You don't say what version of .NET you're using, but if you are using .net 4.5 then you have another option, which is to use the SuppressFormsAuthenticationRedirect option as in this article:

Forms authentication: disable redirect to the login page

Community
  • 1
  • 1
Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • I put Response.SuppressFormsAuthenticationRedirect = true; in the Application_EndRequest method of the Global.asax.cs file. However, it now doesn't do the original redirect to the login page it immediately goes to a 401 error when I try to access the page. Any suggestions? – Baxter Mar 17 '15 at 15:12
  • 1
    You would only use that when you don't want to redirect, not all the time. – Erik Funkenbusch Mar 17 '15 at 15:56
  • How could I determine when I do not want to redirect? I know I don't want to redirect when they are not authorized. However, I can't find a method or anything along the lines of IsAuthorized() to wrap it in an if statement in the global.asax? – Baxter Mar 17 '15 at 16:06
  • 1
    @Baxter - use Application_AuthorizeRequest – Erik Funkenbusch Mar 17 '15 at 17:52
  • Can you elaborate a bit more on what would go in the Application_AuthorizeRequest method? I have updated my question above to show how I am using Application_AuthenticateRequest. – Baxter Mar 17 '15 at 18:03
  • 1
    Actually, AuthorizeRequest probably won't work in your case because you're using web.config configuration. Instead, use the answer suggested here, but instead of using Ajax, add some logic to determine if the redirect (302) is to the login page and the request is already authenticated http://stackoverflow.com/a/18728746/61164, then return the 403. This saves the extra 2 redirects (redirecting to login, then redirecting to the error) – Erik Funkenbusch Mar 17 '15 at 18:27
2

2 checks: if they're authenticated && if there is a return url (which will be there if sent to the log-in page).

if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
{
    // This is an unauthorized, authenticated request... 
    Response.Redirect("~/somewhere.aspx");
}
wazz
  • 4,953
  • 5
  • 20
  • 34
  • This is a very clever answer. I put that code in the Page_Load method of my login page code behind. That seems to work, would you recommend a different location for the checks? – Baxter Mar 17 '15 at 04:47
  • 1
    that's where i put it, after a check for postback (if (!Page.IsPostBack)). – wazz Mar 17 '15 at 05:36
0

The Unauthorized redirect Status Code is 302 but this overrides with status 200 when it's redirected to the login page. In order to redirect the user to Unauthorize Page rather than to the login page, the Hack is to implement Application_EndRequest in Global and check for Response Status Code 302, which is a temporary redirect from the current called to action.

protected void Application_EndRequest(object sender, EventArgs e)
    {
        if(HttpContext.Current.Response.StatusCode == 302 && User.Identity.IsAuthenticated)
        {
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.Redirect("/UnauthorizedPageUrl");
        }
    }