How to: Create a Custom Client Identity Verifier
The identity feature of Windows Communication Foundation (WCF) enables a client to specify in advance the expected identity of the service. Whenever a server authenticates itself to the client, the identity is checked against the expected identity. (For an explanation of identity and how it works, see Service Identity and Authentication.)
If needed, the verification can be customized using a custom identity verifier. For example, you can perform additional service identity verification checks. In this example, the custom identity verifier checks additional claims in the X.509 certificate returned from the server. For a sample application, see Identity.
To extend the EndpointIdentity class
Define a new class that derives from the EndpointIdentity class. This example names the extension
OrgEndpointIdentity
.Add private members along with properties that will be used by the extended IdentityVerifier class to perform the identity check against claims in the security token returned from the service. This example defines one property: the
OrganizationClaim
property.public class OrgEndpointIdentity : EndpointIdentity { private string orgClaim; public OrgEndpointIdentity(string orgName) { orgClaim = orgName; } public string OrganizationClaim { get { return orgClaim; } set { orgClaim = value; } } }
To extend the IdentityVerifier class
Define a new class that derives from IdentityVerifier. This example names the extension
CustomIdentityVerifier
.public class CustomIdentityVerifier : IdentityVerifier { // Code to be added. public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext) { throw new Exception("The method or operation is not implemented."); } public override bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity) { throw new Exception("The method or operation is not implemented."); } }
Override the CheckAccess method. The method determines whether the identity check succeeded or failed.
The CheckAccess method has two parameters. The first is an instance of the EndpointIdentity class. The second is an instance of the AuthorizationContext class.
In the method implementation, examine the collection of claims returned by the ClaimSets property of the AuthorizationContext class, and perform authentication checks as required. This example begins by finding any claim that is of type "Distinguished Name" and then compares the name to the extension of the EndpointIdentity (
OrgEndpointIdentity
).public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext) { bool returnvalue = false; foreach (ClaimSet claimset in authContext.ClaimSets) { foreach (Claim claim in claimset) { if (claim.ClaimType == "https://schemas.microsoft.com/ws/2005/05/identity/claims/x500distinguishedname") { X500DistinguishedName name = (X500DistinguishedName)claim.Resource; if (name.Name.Contains(((OrgEndpointIdentity)identity).OrganizationClaim)) { Console.WriteLine("Claim Type: {0}", claim.ClaimType); Console.WriteLine("Right: {0}", claim.Right); Console.WriteLine("Resource: {0}", claim.Resource); Console.WriteLine(); returnvalue = true; } } } } return returnvalue; }
To implement the TryGetIdentity method
Implement the TryGetIdentity method, which determines whether an instance of the EndpointIdentity class can be returned by the client. The WCF infrastructure calls the implementation of the TryGetIdentity method first to retrieve the service's identity from the message. Next, the infrastructure calls the CheckAccess implementation with the returned EndpointIdentity and AuthorizationContext.
In the TryGetIdentity method, put the following code:
public override bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity) { return IdentityVerifier.CreateDefault().TryGetIdentity(reference, out identity); }
To implement a custom binding and set the custom IdentityVerifier
Create a method that returns a Binding object. This example begins creates an instance of the WSHttpBinding class and sets its security mode to Message, and its ClientCredentialType to None.
Create a BindingElementCollection using the CreateBindingElements method.
Return the SecurityBindingElement from the collection and cast it to a SymmetricSecurityBindingElement variable.
Set the IdentityVerifier property of the LocalClientSecuritySettings class to a new instance of the
CustomIdentityVerifier
class created previously.public static Binding CreateCustomSecurityBinding() { WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message); //Clients are anonymous to the service. binding.Security.Message.ClientCredentialType = MessageCredentialType.None; //Secure conversation is turned off for simplification. If secure conversation is turned on, then //you also need to set the IdentityVerifier on the secureconversation bootstrap binding. binding.Security.Message.EstablishSecurityContext = false; // Get the SecurityBindingElement and cast to a SymmetricSecurityBindingElement to set the IdentityVerifier. BindingElementCollection outputBec = binding.CreateBindingElements(); SymmetricSecurityBindingElement ssbe = (SymmetricSecurityBindingElement)outputBec.Find<SecurityBindingElement>(); //Set the Custom IdentityVerifier. ssbe.LocalClientSettings.IdentityVerifier = new CustomIdentityVerifier(); return new CustomBinding(outputBec); }
The custom binding that is returned is used to create an instance of the client and class. The client can then perform a custom identity verification check of the service as shown in the following code.
using (CalculatorClient client = new CalculatorClient(customSecurityBinding, serviceAddress)) {
Example
The following example shows a complete implementation of the IdentityVerifier class.
class CustomIdentityVerifier : IdentityVerifier
{
public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext)
{
bool returnvalue = false;
foreach (ClaimSet claimset in authContext.ClaimSets)
{
foreach (Claim claim in claimset)
{
if (claim.ClaimType == "https://schemas.microsoft.com/ws/2005/05/identity/claims/x500distinguishedname")
{
X500DistinguishedName name = (X500DistinguishedName)claim.Resource;
if (name.Name.Contains(((OrgEndpointIdentity)identity).OrganizationClaim))
{
Console.WriteLine("Claim Type: {0}", claim.ClaimType);
Console.WriteLine("Right: {0}", claim.Right);
Console.WriteLine("Resource: {0}", claim.Resource);
Console.WriteLine();
returnvalue = true;
}
}
}
}
return returnvalue;
}
public override bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity)
{
return IdentityVerifier.CreateDefault().TryGetIdentity(reference, out identity);
}
}
The following example shows a complete implementation of the EndpointIdentity class.
public class OrgEndpointIdentity : EndpointIdentity
{
private string orgClaim;
public OrgEndpointIdentity(string orgName)
{
orgClaim = orgName;
}
public string OrganizationClaim
{
get { return orgClaim; }
set { orgClaim = value; }
}
}
See Also
Reference
ServiceAuthorizationManager
EndpointIdentity
IdentityVerifier
Other Resources
Identity Sample
How To: Create a Custom AuthorizationManager on a Service
Authorization Policy Sample
© 2007 Microsoft Corporation. All rights reserved.
Build Date: 2009-08-07