Overview
This part is for developers of web applications who want to use WCF services with Atomia Identity authorization. It will show you how a web application can issue calls to WCF service with the privileges of the user who authenticated to that web application. This mechanism is called Identity Delegation . In the Identity Delegation mechanism, web application is able to use WCF by providing the appropriate SAML token to the WCF service.
This token should contain:
- identity claims and
- delegation information
Web application configuration for identity delegation
Step 1: Adding a service reference
Add a service reference to the Web application
After adding a service reference to the web application, the web.config
file is automatically updated – we have a new section withinconfiguration
:
<system.serviceModel> <bindings> <wsFederationHttpBinding> <binding name="WSFederationHttpBinding_IUcpCoreService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <message algorithmSuite="Default" issuedKeyType="SymmetricKey" negotiateServiceCredential="true"> <claimTypeRequirements> <add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="false" /> <add claimType="http://schemas.microsoft.com/ws/2006/04/identity/claims/role" isOptional="true" /> </claimTypeRequirements> <issuer address="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/username/" /> <issuerMetadata address="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/mex" /> </message> </security> </binding> </wsFederationHttpBinding> </bindings> <client> <endpoint address="http://t098/UCP/UcpCoreService.svc" binding="wsFederationHttpBinding" bindingConfiguration="WSFederationHttpBinding_IUcpCoreService" contract="UcpCoreService.IUcpCoreService" name="WSFederationHttpBinding_IUcpCoreService"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAkI...." /> </identity> </endpoint> </client> </system.serviceModel>
Step 2: Configuring web.config
We need to adjust Web Application’s web.config
file in order to use Token delegation to the WCF service. First, we need to change the issuer
section within the security->message tag in our binding
section – our web application will be authenticated on the STS via certificate instead of username/password combination. So, instead of:
<issuer address="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/username/" />
we should have this kind of section:
<issuer address="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/cert/" binding="wsHttpBinding" bindingConfiguration="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/cert/"> <identity> <certificate encodedValue="MIIByzCCATSgAwIBAgIQ2PtXByKML5dI68y5...." /> </identity> </issuer>
XML SECTION | PROPERTY | DESCRIPTION | EXAMPLE |
identity-> certificate | encodedValue |
The certificate (public key) web application is using to sign and encrypt messages to STS (can be found at STS’s wsdl) | MIIByzCCATSgAwIBAgIQ2PtXByKML5dI68y5…. |
Since we’re specifying the way of communication between the web application and the STS via the binding
and bindingConfiguration
attributes, we need define this binding within the bindings
section:
<wsHttpBinding> <binding name="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/cert/" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <message clientCredentialType="Certificate" /> </security> </binding> </wsHttpBinding>
In the above example, the following part defines that the web application will be authenticated at the STS via its own certificate:
<security mode="Message"> <message clientCredentialType="Certificate" /> </security>
Step 3: Defining location of application certificate
The next thing we need to do is to define where web application’s certificate can be found. Let’s add behaviorConfiguration=”ClientCertificateBehavior” attribute to the <endpoint> tag of our WCF service:
<endpoint address="http://t098/UCP/UcpCoreService.svc" behaviorConfiguration="ClientCertificateBehavior" binding="wsFederationHttpBinding" bindingConfiguration="WSFederationHttpBinding_IUcpCoreService" contract="UcpCoreService.IUcpCoreService" name="WSFederationHttpBinding_IUcpCoreService"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAkI0urWJG...." /> </identity> </endpoint>
Then we add a behaviors
section above the client
section:
<behaviors> <endpointBehaviors> <behavior name="ClientCertificateBehavior"> <clientCredentials> <clientCertificate findValue="My web application" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" /> </clientCredentials> </behavior> </endpointBehaviors> </behaviors>
where the following setting needs to be adjusted
XML SECTION | PROPERTY | DESCRIPTION | EXAMPLE |
clientCertificate | findValue |
A string in the X.509 certificate store that contains the certificate used for authenticating web application at the STS | My web application |
Step 4: Modifying the service certificate
The last thing we need to do is to modify the serviceCertificate
within microsoft.identityModel
section in the web.config
file:
<serviceCertificate> <certificateReference x509FindType="FindBySubjectName" findValue="Your Application Name" storeLocation="LocalMachine" storeName="My"/> </serviceCertificate>
Change the following properties :
XML SECTION | PROPERTY | DESCRIPTION | EXAMPLE |
serviceCertificate-> certificateReference | findValue |
A string in the X.509 certificate store that contains the certificate STS for encrypting the issued tokens for the web application | Your Application Name |
Example of system.serviceModel section
The following is an overview of what the web.config system.serviceModel
section should look like:
<system.serviceModel> <bindings> <wsFederationHttpBinding> <binding name="WSFederationHttpBinding_IUcpCoreService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <message algorithmSuite="Default" issuedKeyType="SymmetricKey" negotiateServiceCredential="true"> <claimTypeRequirements> <add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="false" /> <add claimType="http://schemas.microsoft.com/ws/2006/04/identity/claims/role" isOptional="true" /> </claimTypeRequirements> <issuer address="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/cert/" binding="wsHttpBinding" bindingConfiguration="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/cert/"> <identity> <certificate encodedValue="MIIByzCCATSgAwIBAgIQ2PtXByKML5dI68y5ADms+DANBgkqhkiG9w0BAQUFADAkMSIwIAYDVQQDExlVQ1AgQXV0aG9yaXphdGlvbiBTZXJ2aWNlMB4XDTA5MDYwMTExMDEyOFoXDTM5MDYwMTExMDEyOFowJDEiMCAGA1UEAxMZVUNQIEF1dGhvcml6YXRpb24gU2VydmljZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAkT+edt1kwnjNE0JU0Yq4n2WHz/NK3O5a4h6V7kfG3D1iixvPEqzgbCaNw8OpM6+lLEMFoFvTRIMhSr3vM7Y9AxaoaXCY8oQ9oJgnK6XEnuh4zh234jAFokkvkiUx1t/E/J6vAgGmTdm84SrAK+0Lc7pONW8qnNnJzv18EglVkrcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAYlziJ46RFEWtarXbZlscFAOwyu8VQaafKrNLTMDfvv9i1rVCHeQZZK9DmyWHsgtreinVXr09Sio8djaKIroPXBod6HaeRLKaJT8xlqTJncc3zUn+NCNEDGr1OXcyeuldd3ckTZRzc9Up7jIi1kQjDnfe3A51IRGYc2dX9WwWE7Q==" /> </identity> </issuer> <issuerMetadata address="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/mex" /> </message> </security> </binding> </wsFederationHttpBinding> <wsHttpBinding> <binding name="http://localhost/AtomiaIdentityStS/AtomiaSts.svc/cert/" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Message"> <message clientCredentialType="Certificate" /> </security> </binding> </wsHttpBinding> </bindings> <behaviors> <endpointBehaviors> <behavior name="ClientCertificateBehavior"> <clientCredentials> <clientCertificate findValue="Atomia Web Frame" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" /> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> <client> <endpoint address="http://t098/UCP/UcpCoreService.svc" binding="wsFederationHttpBinding" bindingConfiguration="WSFederationHttpBinding_IUcpCoreService" behaviorConfiguration="ClientCertificateBehavior" contract="UcpCoreService.IUcpCoreService" name="WSFederationHttpBinding_IUcpCoreService"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAkI0urWJGQQsCDdWMWI4QlfxNP6kgAAAAAQAAAK0BAAAwggGpMIIBEqADAgECAhApKYqxXRgmq0D335zyfiZeMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNVBAMTCFVDUCBDb3JlMB4XDTA5MDYwMTExMDEyOVoXDTM5MDYwMTExMDEyOVowEzERMA8GA1UEAxMIVUNQIENvcmUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMqLb1jz9bB2AZpnASVmvxCB4iWxBb1IbvmPwfPDJTVuvbvAtihO34lt77z4SNa8ivxUOi1EFsj7V8DBiHGHiCc8Cgp6B2WT6fiOaKUXHKWHbEqVNfi6fKYidfjXtK6axIZH3sw1rceGYIHCHNO3n/Iq7Av7vNVU9GSAMhZ2qQqVAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAMJFJWpNirhnHhzws7O80V3dvV0XHWuhJIeGg8Ds35MUNO4y4K2aE/l9QtzH8De3+TECarAOabaD3iz8K+aBqgem1VfkIB+bWgckRYkdwimCpGuxKqSDO9xEOcSaQitUphmj63as/ddi7ccUTlHAGwtLMRJcD4iHiA144w3sD+Ms=" /> </identity> </endpoint> </client> </system.serviceModel>
Web application code changes for Identity delegation
There are a few things that need to be done in the code in order to use the Identity delegation.
- Open the Global.asax.cs of your web application.
public static readonly string CachedChannelFactory = "WFE_CachedChannelFactory"; void SessionAuthenticationModule_ConfigurationLoaded(object sender, EventArgs e) { ChannelFactory<UCPAuthPrototype.WebMvcApplication.CoreService.ICoreService> service2CF = new ChannelFactory<UCPAuthPrototype.WebMvcApplication.CoreService.ICoreService>("WSFederationHttpBinding_ICoreService"); FederatedClientCredentials.ConfigureChannelFactory<UCPAuthPrototype.WebMvcApplication.CoreService.ICoreService>(service2CF); Application[CachedChannelFactory] = service2CF; }
- Copy the code above.
- Here we initialize the ChannelFactory instance for the service we will use. In UCPAuthPrototype.WebMvcApplication.CoreService.ICoreService you provide the proxy class of your service.
- Put that instance in the global Application dictionary.
- Before the you call of the method of some service you need to initialize it first:
// Get the caller's token from custom state SessionSecurityToken sessionToken = null; if (System.Web.HttpContext.Current.Items.Contains(typeof(SessionSecurityToken).AssemblyQualifiedName)) { sessionToken = System.Web.HttpContext.Current.Items[typeof(SessionSecurityToken).AssemblyQualifiedName] as SessionSecurityToken; } SecurityToken callerToken = null; // We expect only one token to be specified during Bootstrap. if ((sessionToken != null) && (sessionToken.BootstrapTokens.Count == 1)) { callerToken = sessionToken.BootstrapTokens[0]; } if (callerToken == null) { // We lost the session state but the user still has the federated ticket // Let's sign the user off and start again FederatedAuthentication.SignOut(); return LogOff(); } // Get the channel factory to the backend service from the application state ChannelFactory<ICoreService> factory = (ChannelFactory<ICoreService>)System.Web.HttpContext.Current.Application[MvcApplication.CachedChannelFactory]; // Create and setup channel to talk to the backend service ICoreService channel; lock (factory) { // Setup the ActAs to point to the caller's token so that we perform a delegated call to the backend service // on behalf of the original caller. channel = factory.CreateChannelActingAs<ICoreService>(callerToken); //factory.Credentials.IssuedToken = callerToken; //channel = factory.CreateChannel(); }
- First you need to get callerToken (it represents the SAML token of the currently authenticated user).
- Then in the line: take the factory instance – initialized in the previous code section in the Global.asax.cs.
- Use the factory instance to create the Channel for your service:
channel = factory.CreateChannelActingAs<ICoreService>(callerToken);
- Change ICoreService to the interface of your service
- you can call your service method
var UcpAccounts = channel.ListAccounts();
Atomia Identity update to support authentication of web application using certificate
Web.config file of Atomia Identity update with:
- On element with xpath
configuration\microsoft.identityModel\service\issuerNameRegistry\trustedIssuers
add line like:<add name="CN=MyWebApplication" thumbprint="1C18704DF16069DCDD90CE6C6D2FFDE3002E2929" />
- On element with xpath
configuration\microsoft.identityModel\service\securityTokenHandlers\securityTokenHandlerConfiguration\issuerNameRegistry\trustedIssuers
add line like:<add name="CN=MyWebApplication" thumbprint="1C18704DF16069DCDD90CE6C6D2FFDE3002E2929" />