3

How do I make my WCF client authenticate using the ACS to my internally hosted WCF service? The issue revolves around setting a custom Realm (which I can't figure out how to set.)

My ACS is configured similar to the ACS Samples however the "Realm" is defined as shown below.

Excerpt from Azure ACS Configuration page


realm definition


Client Side Code

      EndpointAddress serviceEndpointAddress = new EndpointAddress( new Uri( "http://localhost:7000/Service/Default.aspx"),  
                                                                      EndpointIdentity.CreateDnsIdentity( GetServiceCertificateSubjectName() ),
                                                                      new AddressHeaderCollection() );

        ChannelFactory<IStringService> stringServiceFactory = new ChannelFactory<IStringService>(Bindings.CreateServiceBinding("https://agent7.accesscontrol.appfabriclabs.com/v2/wstrust/13/certificate"), serviceEndpointAddress );

        // Set the service credentials.
        stringServiceFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
        stringServiceFactory.Credentials.ServiceCertificate.DefaultCertificate = GetServiceCertificate();

        // Set the client credentials.
        stringServiceFactory.Credentials.ClientCertificate.Certificate = GetClientCertificateWithPrivateKey();

Server Side Code

 string acsCertificateEndpoint = String.Format( "https://{0}.{1}/v2/wstrust/13/certificate", AccessControlNamespace, AccessControlHostName );

        ServiceHost rpHost = new ServiceHost( typeof( StringService ) );

        rpHost.Credentials.ServiceCertificate.Certificate = GetServiceCertificateWithPrivateKey();

        rpHost.AddServiceEndpoint( typeof( IStringService ),
                                   Bindings.CreateServiceBinding( acsCertificateEndpoint ),
                                   "http://localhost:7000/Service/Default.aspx"
                                   );

        //
        // This must be called after all WCF settings are set on the service host so the
        // Windows Identity Foundation token handlers can pick up the relevant settings.
        //
        ServiceConfiguration serviceConfiguration = new ServiceConfiguration();
        serviceConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;

        // Accept ACS signing certificate as Issuer.
        serviceConfiguration.IssuerNameRegistry = new X509IssuerNameRegistry( GetAcsSigningCertificate().SubjectName.Name );

        // Add the SAML 2.0 token handler.
        serviceConfiguration.SecurityTokenHandlers.AddOrReplace( new Saml2SecurityTokenHandler() );

        // Add the address of this service to the allowed audiences.
        serviceConfiguration.SecurityTokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add( new Uri( "urn:federation:customer:222:agent:11") );

        FederatedServiceCredentials.ConfigureServiceHost( rpHost, serviceConfiguration );

        return rpHost;

... where urn:federation:customer:222:agent:11 is the Relying party ID

... and http://localhost:7000/Service/Default.aspx is the location I want the above WCF / WIF client to bind to once the ACS authentication is made.

Question

How do I edit the code above so that the client and server will both operate against a certain port (localhost:700) and also with a realm of urn:federation:customer:222:agent:11

I think I have the server code correct; however how do I set AudienceRestriction on the client?

Andrew Lavers
  • 8,023
  • 1
  • 33
  • 50
makerofthings7
  • 60,103
  • 53
  • 215
  • 448

3 Answers3

4

Your server side code looks fine, but Sixto is right about standard channel factories. Luckily, you can request a security token from ACS yourself using a WSTrustChannelFactory. In the context of your sample, your code would look like this:

//
// Get the token from ACS
//
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(
    Bindings.CreateAcsCertificateBinding(),
    new EndpointAddress( acsCertificateEndpoint ) );
trustChannelFactory.Credentials.ClientCertificate.Certificate = GetClientCertificateWithPrivateKey();

RequestSecurityToken rst = new RequestSecurityToken()
{
    RequestType = RequestTypes.Issue,
    AppliesTo = new EndpointAddress( new Uri( "urn:federation:customer:222:agent:11" ) ),
    KeyType = KeyTypes.Symmetric
};

WSTrustChannel wsTrustChannel = (WSTrustChannel)trustChannelFactory.CreateChannel();
SecurityToken token = wsTrustChannel.Issue( rst );

//
// Call StringService, authenticating with the retrieved token
//
WS2007FederationHttpBinding binding = new WS2007FederationHttpBinding( WSFederationHttpSecurityMode.Message );
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.NegotiateServiceCredential = false;

ChannelFactory<IStringService> factory = new ChannelFactory<IStringService>(
    binding,
    new EndpointAddress(
            new Uri( ServiceAddress ),
            EndpointIdentity.CreateDnsIdentity(GetServiceCertificateSubjectName()) ) );
factory.ConfigureChannelFactory<IStringService>();
factory.Credentials.SupportInteractive = false;
factory.Credentials.ServiceCertificate.DefaultCertificate = GetServiceCertificate();

IStringService channel = factory.CreateChannelWithIssuedToken<IStringService>( token );
string reversedString = channel.Reverse( "string to reverse" );
Andrew Lavers
  • 8,023
  • 1
  • 33
  • 50
  • Thanks for the code! This was well worth the +145 rep points. – makerofthings7 Apr 13 '11 at 18:10
  • What would need to be done if I wanted to have another trust in the the chain? ADFS to ACS to RP for example? or ACS to ACS to RP? – makerofthings7 Apr 14 '11 at 14:58
  • 1
    In this scenario, you can still use a WSTrustChannel to get the token, except you would use an IssuedTokenWSTrustBinding, and you'd point it to ACS's issued token endpoint (which is v2/wstrust/13/issuedtoken-symmetric, v2/wstrust/13/issuedtoken-asymmetric, or v2/wstrust/13/issuedtoken-bearer, depending). You then configure the binding's IssuerBinding and IssuerAddress properties to point to your ADFS certificate endpoint. A search for IssuedTokenWSTrustBinding will yield plenty of code samples. Once you have the token from ACS, the rest looks the same. – Andrew Lavers Apr 15 '11 at 23:52
1

Some answers may be better late than never. I've been unable to find any official documentation on using WCF in this fashion, however in reading the WS-Trust papers and the MSDN documentation on configuration, I have come up with the following solution which appears to work.

From the service consuming client's config at configuration/system.serviceModel/bindings/ws2007FederationHttpbinding/binding/security/message. It overrides the AppliesTo element of the token request message.

<tokenRequestParameters>
  <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <EndpointReference xmlns="http://www.w3.org/2005/08/addressing">
      <Address>urn:x-Organization:Testing</Address>
    </EndpointReference>
  </wsp:AppliesTo>
</tokenRequestParameters>

Adding this same snippet in the configuration of the service, will cause the Service Reference utility to include this within the trust:SecondaryParameters element of the service client. It must be moved into the parent tokenRequestParameters element to work properly.

psaxton
  • 1,693
  • 19
  • 24
0

Haven't actually tried the approach referenced in this MSDN article but from reading it sounds like the standard channel factory doesn't have the right hooks to do what you want. The WSTrustChannelFactory is built for WIF & SAML but I'm not familiar enough with ACS to determine if it is applicable. This article in this six-part series will probably be worthwhile perusing too.

Sixto Saez
  • 12,610
  • 5
  • 43
  • 51