Posts Tagged ‘WIF’

Introduction: The most common scenario in any application development (desktop/web/mobile) is to build Authentication/Authorization. In Role based authorization, we define set of roles and each role is authorized to do some actions.  It tightly couples application security with business logic.  Number of roles would be needed if there are complex rules depending on business need. The alternative is Claims based Authorization.  In this method we define claims as Resource and Operation pairs.   Each user is assigned different claims and based on claims, we authorize user actions. Creation of Claims: When a new user is created, set of claims would be added to the user. WIF comes with a table called AspNetUserClaims for storing claims. UserId column is a foreign key to Id column in AspNetUsers table. In a typical application, claims can be created in following ways:

  • Populated using back end jobs based on details from Organization’s internal database/sources
  • Inserted inside application during/after user Registration/Signup process.

The creation of claims is a one time job and may be updated based on changes in business rules. Claim Example:

ClaimType: "http://abc.com/identity/claims/Account"

ClaimValue: "Update"

Loading Claims: Now, we have the claims saved in database for each user.   When a user logs into the system, we have to read/load those claims and associate them with User Identity.  We would perform this by implementing a custom http module and subscribe to PostAuthenticationRequest event in Init method.

public class CustomClaimsBasedAuthorization : IHttpModule, IDisposable
{
	public void Init(HttpApplication context)
	{
		context.PostAuthenticateRequest += PostAuthenticateRequestEvent;
	}

	void PostAuthenticateRequestEvent(object sender, EventArgs e)
	{
		var sessionAuthModule = FederatedAuthentication.SessionAuthenticationModule;
		if (sessionAuthModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies) &&
		sessionAuthModule.ContextSessionSecurityToken != null)
		{
			var ck = sessionAuthModule.ContextSessionSecurityToken;
			sessionAuthModule.AuthenticateSessionSecurityToken(ck, false);
		}
		else
		if (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated)
		{
			ClaimsPrincipal cp = CreateClaimsedBasedPrincipal();

			var sstoken = new SessionSecurityToken(cp);
			sessionAuthModule.WriteSessionTokenToCookie(sstoken);
		}
	}

        private static ClaimsPrincipal CreateClaimsedBasedPrincipal()
	{
		string userName = Thread.CurrentPrincipal.Identity.Name;
                //Load claims from Database/Service
		var claims = LoadClaims(userName);

		var cp = new RmsClaimsPrincipal(userName, claims);

		Thread.CurrentPrincipal = cp;
		if (HttpContext.Current != null)
		{
			HttpContext.Current.User = cp;
		}
		return cp;
	}
}

internal sealed class CustomClaimsPrincipal : ClaimsPrincipal
{
	public CustomClaimsPrincipal (string userName, IEnumerable<RmsUserClaim> userRoles)
	{
		var gIdentity = new GenericIdentity(userName, "RMS custom authentication");
		var cIdentity = new ClaimsIdentity(gIdentity);

		foreach (var claim in userRoles)
		{
			cIdentity.AddClaim(new Claim(claim.ClaimType, claim.ClaimValue));
		}
		AddIdentity(cIdentity);
	}
}

In the above example, we create ClaimsPrincipal and add claims to the Identity. However, the ClaimsPrincipal creation process will be executed for each and every request to the web application which could be potential performance issue.  To improve the performance, .Net Framework 4.5 supports writing ClaimsPrincipal with all of its claims to a Cookie.  To enable this, following web.config changes are required. Add following config section:

<configSections>
  <section name="system.identityModel.services"
   type="System.IdentityModel.Services.Configuration.
                SystemIdentityModelServicesSection,
 System.IdentityModel.Services, Version=4.0.0.0,
 Culture=neutral, PublicKeyToken=B77A5C561934E089" />

Add following Config elements to support creation of Cookie

<system.identityModel.services>
  <federationConfiguration>
    <cookieHandler requireSsl="false" />
  </federationConfiguration>
</system.identityModel.services>

Configure module that handles reading and writing the cookie.

<modules>
  <add name="SessionAuth"
       type="System.IdentityModel.Services.SessionAuthenticationModule,
             System.IdentityModel.Services, Version=4.0.0.0,
             Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

Restricting actions based on claims: We have claims in place and we would want to restrict controller actions based on claims. For this, we could apply PricipalPermissionAttribute or ClaimsPrincipalPermissionAttribute. For example:

[ClaimsPrincipalPermission(SecurityAction.Demand, Resource = "Account", Operation = "Update")]
public void UpdateDetails()
{
//Update logic
}

These attributes serve the purpose, as we could specify required resource and type of action. However, these attributes are invoked by CLR. If the role check fails it throws a Securityexception. Instead we want to show login page so that user could login to appropriate role. Alternately, we could use Authorize attribute. However it doesn’t support Resource/Action out of box. So, we want to write our own custom attribute by extending AuthorizeAttirbute.

internal class CustomAuthorizeAttribute : AuthorizeAttribute
{
	public string Resource { get; set; }

	public string Operation { get; set; }

	protected override bool AuthorizeCore(HttpContextBase httpContext)
	{
		var cPrincipal = (ClaimsPrincipal)httpContext.User;
		var resourceClaim = string.Format("{0}/{1}", "http://abc.com/identity/claims", Resource);
		return cPrincipal != null && cPrincipal.HasClaim(resourceClaim, Operation);
	}
}

Once we define custom attribute, it could be used as below:

[CustomAuthorize(Resource = "Account", Operation = "Update")]
public void UpdateDetails()
{
//Update logic
}

Please provide your valuable feedback/comments/suggestions that would help me improve my writing.