CHAPTER 5 ■ AZURE .NET SERVICES—ACCESS CONTROL 158 Table 5-1. Class Definition in FederateAccessManager Assembly Class Derived From Override Input Parameters Output CloneCore UserRegister Service Credentials ServiceCredentials CreateSecurity TokenManager SecurityTokenManager UserRegister Security TokenManager ServiceCredentials SecurityTokenManager CreateSecurity TokenAuthenticator SecurityToken Requirement SecurityToken Authenticator SecurityTokenResolver UserRegister Token Authenticator SamlSecurityToken Authenticator ValidateTokenCore SecurityToken ReadOnlyCollection <IAuthorizationPlicy> The UserRegisterServiceCredentials class is derived from ServiceCredentials and accepts the name of the corresponding Azure solution and overrides two methods of its base class, CloneCore() and CreateSecurityTokenManager(). At runtime the certificate that has been installed on a local device and registered from Azure Access Control will be assigned to the instance of this class. The type of the ServiceCredentials class must be specified when you instantiate a service host instance, and an instance of the UserRegisterServiceCredentials class needs to be added to the host behaviors collection after the host has been instantiated. The responsibility of the class UserRegisterTokenAuthenticator is to communicate to the Access Control Service to validate the security token for authentication. This class is used by the UserRegisterSecurityTokenManager class. Listing 5-13. Implementations for Class UserRegisterServiceCredentials using System; using System.IdentityModel.Selectors; using System.ServiceModel.Description; namespace AzureForDotNetDeveloper.DotNetService.ServiceBus { public class UserRegisterServiceCredentials : ServiceCredentials { String solutionName; public UserRegisterServiceCredentials(String solutionName) : base() { this.solutionName = solutionName; } protected override ServiceCredentials CloneCore() { CHAPTER 5 ■ AZURE .NET SERVICES—ACCESS CONTROL 159 return new UserRegisterServiceCredentials(solutionName); } public override SecurityTokenManager CreateSecurityTokenManager() { return new UserRegisterSecurityTokenManager(this, solutionName); } } } Listing 5-14. Implementations of Class UserRegisterSecurityTokenManager using System; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; using System.ServiceModel.Security; namespace AzureForDotNetDeveloper.DotNetService.ServiceBus { public class UserRegisterSecurityTokenManager : ServiceCredentialsSecurityTokenManager { UserRegisterServiceCredentials UserRegisterServiceCredentials; String solutionName; public UserRegisterSecurityTokenManager( UserRegisterServiceCredentials UserRegisterServiceCredentials, String solutionName ) : base(UserRegisterServiceCredentials) { this.UserRegisterServiceCredentials = UserRegisterServiceCredentials; this.solutionName = solutionName; } public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator( SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver ) { if (tokenRequirement.TokenType.Equals( "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1", StringComparison.OrdinalIgnoreCase)) { base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver); return new UserRegisterTokenAuthenticator( new SecurityTokenAuthenticator[] { new X509SecurityTokenAuthenticator(X509CertificateValidator.None), new RsaSecurityTokenAuthenticator() }, CHAPTER 5 ■ AZURE .NET SERVICES—ACCESS CONTROL 160 solutionName ); } else { return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver); } } } } Listing 5-15. Implementations of Class UserRegisterTokenAuthenticator using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IdentityModel.Claims; using System.IdentityModel.Policy; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; using System.ServiceModel; namespace AzureForDotNetDeveloper.DotNetService.ServiceBus { class UserRegisterTokenAuthenticator : SamlSecurityTokenAuthenticator { IList<SecurityTokenAuthenticator> supportingAuthenticators; SamlSecurityTokenAuthenticator innerSamlSecurityTokenAuthenticator; String solutionName; public UserRegisterTokenAuthenticator( IList<SecurityTokenAuthenticator> supportingAuthenticators, String solutionName) : base(supportingAuthenticators) { this.supportingAuthenticators = new List<SecurityTokenAuthenticator>(supportingAuthenticators); this.innerSamlSecurityTokenAuthenticator = new SamlSecurityTokenAuthenticator(supportingAuthenticators); this.solutionName = solutionName; } public UserRegisterTokenAuthenticator( IList<SecurityTokenAuthenticator> supportingAuthenticators, TimeSpan maxClockSkew) : base(supportingAuthenticators, maxClockSkew) { this.supportingAuthenticators = new List<SecurityTokenAuthenticator>(supportingAuthenticators); this.innerSamlSecurityTokenAuthenticator = new SamlSecurityTokenAuthenticator(supportingAuthenticators, maxClockSkew); CHAPTER 5 ■ AZURE .NET SERVICES—ACCESS CONTROL 161 } protected override ReadOnlyCollection<IAuthorizationPolicy>ValidateTokenCore(SecurityToken token) { if (token == null) { throw new ArgumentNullException("token"); } SamlSecurityToken samlToken = token as SamlSecurityToken; if (samlToken == null) { throw new SecurityTokenException("Not a SamlSecurityToken."); } if (!samlToken.Assertion.Issuer.Equals( String.Format("http://accesscontrol.windows.net/{0}", this.solutionName), StringComparison.OrdinalIgnoreCase)) { throw new SecurityTokenException("Not expected issuer."); } return this.innerSamlSecurityTokenAuthenticator.ValidateToken(token); } } } Now insert code on the server to use security credentials as Listing 5-16 shows. Listing 5-16. Insert Security Credential Code into Server Implementations using System; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using System.ServiceModel.Description; namespace AzureForDotNetDeveloper.DotNetService.ServiceBus { class Program { static void Main(string[] args) { ServiceHost host = new ServiceHost(typeof(AzureForDotNetDeveloperWCFservice)); String solutionName = ReadSolutionName(); ServiceCredentials sc = host.Credentials; X509Certificate2 cert = sc.ServiceCertificate.Certificate; UserRegisterServiceCredentials serviceCredential = new UserRegisterServiceCredentials(solutionName); CHAPTER 5 ■ AZURE .NET SERVICES—ACCESS CONTROL 162 serviceCredential.ServiceCertificate.Certificate = cert; host.Description.Behaviors.Remove((typeof(ServiceCredentials))); host.Description.Behaviors.Add(serviceCredential); host.Open(); Console.WriteLine(" UserRegister service is running."); Console.WriteLine(" Press <Enter> to terminate server"); Console.ReadLine(); host.Close(); } private static string ReadSolutionName() { Console.Write(string.Format(" Please enter your solution name: {0}", Environment.NewLine)); return Console.ReadLine(); } } } Add a new class AccessControlHelper to the WCF service project AzureForDotNetDeveloperWCFserviceLibrary. This is a helper class used to validate the claim token string. The string parameters passed in should match those that we defined in the rules when we configured the rule from Azure (see Figure 5-14). Listing 5-17. Implementation of Class AccessControlHelper using System; using System.Collections.Generic; using System.IdentityModel.Claims; using System.IdentityModel.Policy; using System.ServiceModel; namespace AzureForDotNetDeveloper.DotNetService.ServiceBus { public class AccessControlHelper { public static void DemandActionClaim(string claimValue) { foreach ( ClaimSet claimSet in OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets ) { foreach (Claim claim in claimSet) { if (AccessControlHelper.CheckClaim( claim.ClaimType, . SamlSecurityToken."); } if (!samlToken.Assertion.Issuer.Equals( String.Format("http://accesscontrol .windows. net/{0}", this.solutionName), StringComparison.OrdinalIgnoreCase)) { throw new