The previous article on
Role Based Access Control described the
IIdentity and
IPrincipal implementations available with Microsoft .NET 1.0 through 4.0. Designed in 2002, these soon began to creak under the stress of increasingly distributed systems of rising complexity. With
WCF in 2006, Microsoft provided another security model incompatible with the old one, and in 2009, succeeded in combining the best parts of these two implementations into
WIF, and ultimately in 2012, into .NET 4.5.
IIdentity and IPrincipal Revisited
The new hotness was
Claims Based Authorization. A
System.Security.Claim is a statement about an entity, made by another entity known as the
Issuer of the claim. The statement takes the form of a key/value pair, which is obviously much more powerful than the true/false settings used in
Role Based. For example, Active Directory might issue claims that the current user is named "alice", is an administrator, has email address "alice@microsoft.com", and so on. We would treat these claims, coming from AD, with a high level of confidence.
public class Claim
{
public virtual string Type { get; }
public virtual string Value { get; }
public virtual string Issuer { get; }
// ...
}
We could set up claims using syntax like this:
var claim = new Claim("name", "alice");
However, recall that previously we found reasons (of localization and breaking admin changes) to avoid using literal labels to identify security resources such as groups. It turns out that we might also want to use the predefined members - essentially namespace URIs - of the
ClaimTypes class, to ensure that our claims can be serialized, shared and understood across a federated identity space:
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "alice"),
new Claim(ClaimTypes.Email, "alice@microsoft.com"),
new Claim(ClaimTypes.Role, "Sales"),
new Claim(ClaimTypes.Role, "Marketing")
};
ClaimsIdentity and ClaimsPrincipal
Obviously, claims can carry a lot more detailed information about an entity than roles. For excellent reasons of backward compatibility, claims were integrated into the existing
IIdentity /
IPrincipal framework through the creation of two new container classes:
public class ClaimsIdentity : IIdentity
{
public virtual IEnumerable<Claim> Claims { get; }
// ...
}
public class ClaimsPrincipal : IPrincipal
{
public virtual IEnumerable<ClaimsIdentity> Identities { get; }
// ...
}
Now we can set up Alice's principal like this:
var id = new ClaimsIdentity(claims, "Console App");
var user = new ClaimsPrincipal(id);
Thread.CurrentPrincipal = user;
The second parameter to the
ClaimsIdentity constructor,
authenticationType, is needed because, unlike the case with Role Based Identity, Claims Based Identity allows claims to be attached to e.g. anonymous principals. By default then, if
authenticationType is not provided,
IsAuthenticated will return
false.
Backward Compatibility
Examining the framework source, it's interesting to note that every (non
static)
public in these new classes is
virtual. That's because they have been "interposed" in .NET 4.5, so that all the implementation classes we looked at last time (
WindowsIdentity /
WindowsPrincipal, and
GenericIdentity /
GenericPrincipal) as well as those we didn't (e.g.
FormsIdentity,
RolePrincipal) are now derived from these common base classes. This design gives all consumer code access to the claims collection if desired, while still preserving the old interfaces (
Name,
IsInRole, etc).
The base classes implement the legacy security model with the help of the claims collection. For example, accessing the
ClaimsIdentity.Name property causes this collection to be traversed, searching for an entry with type
ClaimTypes.Name, while checking for a role is done by searching for
ClaimTypes.Role entries.
Incidentally, both of these associations can be overridden using the supplied, overloaded,
ClaimsIdentity constructors. So you can use these base classes to benefit from the power of the claims approach in your own custom implementation, while still retaining the flexibility of selecting which
ClaimsType values you want the legacy support to treat as names and roles:
public class MyClaimsIdentity : ClaimsIdentity
{
public MyClaimsIdentity(IEnumerable<Claim> claims, string authenticationType) :
base(claims, authenticationType, ClaimTypes.Email, ClaimTypes.GroupSid)
{
}
}
In the above example, accessing the
Name property will search for a claim of type
Email (often used as the unique user identifier), while a call to
IsInRole will search for a match within the
groups to which the user belongs.
Consuming the Principal
To get access to the claims from runtime code, we simply retrieve the
IPrincipal from the thread as before, and cast it to a
ClaimsPrincipal. Alternatively, and equivalently, we can obtain it in a single step via the handy, static
ClaimsPrincipal.Current property:
var user = ClaimsPrincipal.Current;
With this we can now run arbitrary LINQ queries on the
Claims collection property, or take advantage of the special
FindFirst,
FindAll and
HasClaim methods, all of which accept either string parameters or lambdas.
var email = user.FindFirst(ClaimTypes.Email).Value;
var adClaims = user.FindAll(claim => claim.Issuer == "AD AUTHORITY");
Credential Types and Transformations
.NET 4.5 boasts out-of-the-box support, via descendants of the
SecurityTokenHandler class, for a wide range of industry standard authentication protocols, which it translates automatically to
ClaimsPrincipal format. These include:
- Windows (SPNEGO, Kerberos, NTLMSSP)
- Forms authentication
- basic HTTP authentication
- SSL client certificates
- WS-Security tokens
- SAML
Incoming credentials arrive, are processed and transformed, and then delivered to your application in the "final"
ClaimsPrincipal. This processing begins with an appropriate
SecurityTokenHandler deserializing and validating the incoming token, from which it constructs the "initial"
ClaimsPrincipal. This can then be further processed or transformed, for example to validate the incoming claims or translate security-domain groups into application-domain user permissions, via your custom
ClaimsAuthenticationManager (to run the following sample code, add references to
System.IdentityModel and
System.Security):
public class MyClaimsTransformer : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(
string resourceName, ClaimsPrincipal oldPrincipal)
{
// Validate the supplied claims
var name = oldPrincipal.Identity.Name;
if (string.IsNullOrWhiteSpace(name))
throw new SecurityException("User name is missing!");
// Process claims
var creditLimit = LookupCreditLimit(name);
var newClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, name),
new Claim("http://myclaims/creditlimit", creditLimit.ToString())
};
// Create & return the new ClaimsPrincipal
var newId = new ClaimsIdentity(newClaims, "Local");
return new ClaimsPrincipal(newId);
}
}
Obviously we have to find somewhere to tell the system to use our new custom
ClaimsAuthenticationManager class, and the equally obvious place to do that is in the configuration file. There's a new
"system.identityModel" section:
<configuration>
<configSections>
<section
name="system.identityModel"
type="System.IdentityModel.Configuration.SystemIdentityModelSection" />
</configSections>
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager
type="MyNamespace.MyClaimsTransformer, MyAssemblyName" />
</identityConfiguration>
</system.identityModel>
</configuration>
Finally, at the point in our application code where previously we would perform our
ad hoc claims processing, polluting our beautiful domain code with extraneous user security permissions logic, we now have essentially this fixed one-liner (no, don't laugh):
Thread.CurrentPrincipal =
FederatedAuthentication
.FederationConfiguration
.IdentityConfiguration
.ClaimsAuthenticationManager
.Authenticate(
"none", // Can pass eg a URL resource for context
new WindowsPrincipal(WindowsIdentity.GetCurrent()));
Next time: session management.