Asp.net-mvc – Using Custom RoleProvider with Windows Identity Foundation – STS

asp.netasp.net-mvcclaims-based-identitywif

I created STS that does the authentication part. It uses Custom Membership provider.
After successful login I get redirected to my RP website. All works fine in terms of authentication.

I have defined a CustomRolesProvider defined in web.config of my RP website. It uses the username returned by STS to fetch the roles for that user from RP's database.
When I use Roles.GetRolesForUser I do get the right roles.

I have the following in the web.config of my RP to allow only admin to give access to admin folder.

And the sitemap provider has securityTrimmingEnabled="true"

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

<add name="default" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true" />

Problem:
When the user is in the admin role, the menu tabs for admin pages won't showup. I did check that Roles.IsUserInRole("admin") returns true. So the role is recognized by roles provider but not by authorization rules and sitemap provider in the web.config.

If I comment out the "location" from the web.config i.e. allowing every logged-in user to admin folder, my menu items show up fine.

From my understanding of WIF, RP can have it's own implementation of Roles and does not have to rely on Roles Claim from STS.

Does anyone has any ideas?

Update 2(01/20/2012): I found that the STS returns role claims as below:

http://schemas.microsoft.com/ws/2008/06/identity/claims/role = Manager

So if I change <allow roles="admin" /> to <allow roles="Manager" /> the role is picked up and menu tabs are shown appropriately.

So I am sure I am missing a link on how to make use of my roles and not the one returned via claims.

Update 2(01/20/2012):
If I add the role to the claimsIdentity like below it works:

void Application_AuthenticateRequest(object sender, EventArgs e) {
  if (Request.IsAuthenticated) {    
    IClaimsPrincipal claimsPrincipal = HttpContext.Current.User as IClaimsPrincipal;
    IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
    if (!claimsIdentity.Claims.Exists(c => c.ClaimType == ClaimTypes.Role))
    {
      claimsIdentity.Claims.Add(new Claim(ClaimTypes.Role, "admin"));
    }
  }
}

But then what would be the best place to add that code? If I add it in Application_AuthenticateRequest it's added upon each request and it keeps adding.(I fixed this by adding if statement)

*Update 3(01/24/2012):*Version 2 of my code that uses my CustomRoleProvider to get the Roles and then add it to the ClaimsCollection:

void Application_AuthenticateRequest(object sender, EventArgs e) {
 if (Request.IsAuthenticated) {
    string[] roleListArray = Roles.GetRolesForUser(User.Identity.Name);
       IClaimsPrincipal claimsPrincipal = HttpContext.Current.User as IClaimsPrincipal;
       IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
       var roleclaims = claimsIdentity.Claims.FindAll(c => c.ClaimType == ClaimTypes.Role);
       foreach (Claim item in roleclaims)
       {
         claimsIdentity.Claims.Remove(item);
       }

       foreach(string role in roleListArray)
       {
         claimsIdentity.Claims.Add(new Claim(ClaimTypes.Role, role));
       }

       HttpContext.Current.User = claimsPrincipal;
    }

But I am not sure if that's the right way.

Is there anyone who has done something like this??

Update 4 (01/26/2012): Found that I can use Custom ClaimsAuthencationManager(Step 4) to transform my claims.
I moved the code in AuthenticateRequest method in Global.asax to Authenticate method in ClaimsAuthenticationManager class.

I doubt it can get any better than this. I will post my solution as answer. But still if anyone has any other better solution feel free to comment.

Best Solution

You could use a custom ClaimsAuthencationManager, however, it will be called on every request. My recommendation would be to use WSFederationAuthenticationModule.SecurityTokenValidated. Use the ClaimsPrincipal property of SecurityTokenValidatedEventArgs class and add the roles using your provider. Also, instead of hard coding the role claim type, you may wish to consider using ClaimsIdentity.RoleClaimType.

The looked up roles will be saved in the encrypted cookie (assuming you are using the default).

Related Question