views:

31

answers:

2

So, I've implemented my IPrincipal.IsInRole(...) and I'm using FormsAuthentication like so:

<authentication mode="Forms">
     <forms loginUrl="Login.aspx" name="someName" timeout="600"/>
</authentication>

Then I have a page that requires you to be authenticated and that you have "roleA". This is configured like so:

 <location path="SomePage.aspx">
  <system.web>
   <authorization>
    <allow roles="roleA" />
    <deny users="*"/>
   </authorization>
  </system.web>
 </location>

Now, I login to my web application, but with a user that does NOT have roleA. When I visit SomePage.aspx I get redirected to Login.aspx, the url specified in loginUrl of the forms element. So, my question is shouldn't I be able be specify an authorization denied message or url? If the user is authenticated, but not authorized why would I want to redirect to the login page. It's confusing as hell to the user. Please tell me I am missing something simple.

Thanks for reading!

A: 

Yeah, this is a little annoying. Maybe someone has a simpler idea, but the solution (hack?) that we came up with was to look for the originally-requested URL that ASP.NET appends to the query string when the user is redirected to the login page.

We created a new web.config section that stores a set of keys/values that match a fragment of the redirect URL to an authorization message:

<configSections>
    <section name="authorizationFailureMessages" type="System.Configuration.NameValueSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    ...etc...
</configSections>

<authorizationFailureMessages>
    <add key="MemberResources" value="MembershipRequired" />
    <add key="Staff" value="StaffOnly" />
    <add key="Departments/Administration/BoardOfDirectors" value="BoardOfDirectorsOnly" />
    ...etc...
</authorizationFailureMessages>

In the Page_Load() event of the Login.aspx page, we call a method that uses this URL to determine which (un)authorization event occurred then redirect them to a message page that displays the appropriate text:

private void DisplayAppropriateAuthorizationMessage ()
{
    if ( !Page.User.Identity.IsAuthenticated )
        return;

    string redirectUrl = FormsAuthentication.GetRedirectUrl( Page.User.Identity.Name, false );

    if ( string.IsNullOrEmpty( redirectUrl ) )
        return;

    NameValueCollection authorizationFailureMessages = ConfigurationManager.GetSection( "authorizationFailureMessages" ) as NameValueCollection;

    if ( authorizationFailureMessages == null )
        return;

    foreach ( string key in authorizationFailureMessages.AllKeys )
    {
        if ( redirectUrl.Contains( key ) )
        {
            Response.Redirect( String.Format( "Message.aspx?{0}={1}", Constants.QueryStringKeys.ERRORMESSAGENAME, authorizationFailureMessages[ key ] ), true );
        }
    }
}
Matt Peterson
ugh, this has to be the right answer because I was hoping I wouldn't have to resort to something like this :( This helps though, and I feel ever so slightly less stupid. Thanks a lot!
internet man
I'm still holding out hope that someone else has come up with something more clever. :) Of course, the ideal solution would be if ASP.NET exposed (say, on the Request object) a reference to the specific role that caused the authorization failure.
Matt Peterson
A: 

Roles.IsUserInRole - If you're just using it for this page, throw this in the code behind. If you have a lot of pages, you could consider putting this in a base class and reading either from the web.config or the database per page. I believe this will give you the most control.

MCain