Zulfiqar's weblog

Middleware, security & random .Net

Archive for July, 2010

Using WIF for securing REST Service

Posted by zamd on July 31, 2010

OAuth WRAP and SWT (Simple Web Token) have emerged as a standard way for employing claim-based-security to the REST services.  Conceptually this model is very similar to the one used in SOAP world. A client goes to an issuer, authenticates itself using some token and gets back a different token (containing claims) in SWT format. 

WIF provides a rich API and object model to claim-enable your .Net applications but currently it doesn’t natively support OAuth or SWT. In this post, I’ll show how you can extend WIF to bring claim-based-security to REST services.  My goal is to implement this functionality in such a way that all the WIF goodness around claims-transformation (ClaimsAuthenticationManager), claim-based-authorization (ClaimsAuthorizationManager) etc can be used exactly the same way in the REST world.

I have achieved this by introducing an OAuth WRAP channel which sits in the WCF channel stack and perform an almost identical job to its SOAP counterpart WS-Security channel.  I have implemented the OAuth WRAP channel using the interceptor API from the REST Starter Kit.

image WebServiceHost2 host = new WebServiceHost2(typeof(TestService), new Uri(http://localhost:9090));

host.Interceptors.Add(new OAuthWrapSecurityChannel());

host.Open();

The OAuth channel takes care of extracting the token from the incoming message, running it through the validation pipeline (based on WIF SecurityTokenHandler framework), calling ClaimsAuthenticationManager, setting the WCF authorization context so that ClaimsAuthorizationManager can be called, and finally presenting the claims to the service method in the standard WIF way: Thread.CurrentPrincipal :)

Now with OAuth channel plugged in, if I call the service with a token issues by the Windows AppFabric ACS:

clip_image002

I get following output on the service side.

clip_image001

And from the service code, you would notice that standard WIF API is used to access incoming claims.

[WebGet]

string Hello()

{

    Console.WriteLine("Hello called…");

    Console.WriteLine("——————");

    var cp = Thread.CurrentPrincipal as IClaimsPrincipal;

    if (cp != null)

    {

        foreach (var id in cp.Identities)

        {

            Console.WriteLine("Authentication Type: " + id.AuthenticationType);

            Console.WriteLine("Is Authenticated: " + id.IsAuthenticated);

            Console.WriteLine("Name: " + id.Name);

            Console.WriteLine();

            Console.WriteLine("Claims…");

            foreach (var c in id.Claims)

            {

                Console.WriteLine(c.ClaimType + ": " + c.Value);

            }

        }

    }

    return "Hello, World";

}

 

WIF would be configured using exactly same configuration settings required for a SOAP service.

  1. <configuration>
  2.   <configSections>
  3.     <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  4.   </configSections>
  5.  
  6.   <system.serviceModel>
  7.     <behaviors>
  8.       <serviceBehaviors>
  9.         <behavior>
  10.           <federatedServiceHostConfiguration />
  11.           <serviceDebug includeExceptionDetailInFaults="true" />
  12.         </behavior>
  13.       </serviceBehaviors>
  14.     </behaviors>
  15.     <extensions>
  16.       <behaviorExtensions>
  17.         <add name="federatedServiceHostConfiguration" type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  18.       </behaviorExtensions>
  19.     </extensions>
  20.   </system.serviceModel>
  21.   <!–WIF configuration–>
  22.   <microsoft.identityModel>
  23.     <service>
  24.       <securityTokenHandlers>
  25.         <add type="Microsoft.IdentityModel.OAuth.SWTSecurityTokenHandler, Microsoft.IdentityModel.OAuth, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  26.       </securityTokenHandlers>
  27.       <issuerNameRegistry type="SampleService.MySimpleRegistry, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  28.       <claimsAuthorizationManager type="SampleService.MySimpleClaimsAuthorizationManager, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  29.       <audienceUris>
  30.         <add value="http://localhost/" />
  31.       </audienceUris>
  32.       <issuerTokenResolver type="SampleService.WrapIssuerTokenResolver, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  33.     </service>
  34.   </microsoft.identityModel>
  35.   <startup>
  36.     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
  37.   </startup>
  38. </configuration>

 

In summary, these extensions implement OAuth WRAP protocol head for Windows Identity Foundation and re-uses all the neat WIF APIs for deriving the service behaviour. In the next post, I’ll discuss how to create a local OAuth WRAP issuer using the SecurityTokenService WIF API.

Feel free to download the source and experiment.

Posted in WCF, WIF, Windows Azure AppFabric | 6 Comments »

PasswordDigest authentication in WCF

Posted by zamd on July 12, 2010

WS-Security UsernameToken Profile describes how a client can authenticate to a web service using a "username" and a password. Two variations of the password are defined as part of the specification which includes "PasswordText" and "PasswordDigest". Neither WCF nor WIF out of box support "PasswordDigest" however there are some interop scenarios which might require "PasswordDigest". For example, WSE 3.0 supports "PasswordDigest" and if you have to interop your WSE 3.0 clients with WCF services you might need PasswordDigest functionality. In this post I’ll show you a basic implementation of PasswordDigest closely integrated with WCF API. Please note, this could be much easier to implement using the SecurityTokenHandler based API shipped with WIF.

I have created a new assembly and added all the security extensions in this new assembly named Microsoft.ServiceModel.SecurityExtensions.dll.

Microsoft.ServiceModel.SecurityExtensions.dll extends WCF to support Password Digest authentication while retaining WCF’s public programming model. You need to reference this assembly in all your web service projects where you want to enable PasswordDigest authentication. The security extensions are very closely aligned to the standard WCF model of userName/Password validation so you should be able to leverage all of your existing knowledge. 

Please note this is a trivial implementation primarily focused on WCF integration rather than spec implementation. This example doesn’t contain any countermeasure code against replay attacks. You can add such functionality by maintaining a cache of used nonces on the server side and then checking against the replay.

To use these extensions in your web service(s), you need to do following:

  • Specify the type attribute of serviceCredentials to Microsoft.ServiceModel.SecurityExtensions.ServiceCredentialsEx for the extensions to kick in.
  • Specify your custom validator using the standard WCF syntax. Your custom validator MUST inherit from Microsoft.ServiceModel.SecurityExtensions.UserNamePasswordDigestValidator

<behaviors>

  <serviceBehaviors>

    <behavior>

      <serviceCredentials type="Microsoft.ServiceModel.SecurityExtensions.ServiceCredentialsEx, Microsoft.ServiceModel.SecurityExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">

        <userNameAuthentication userNamePasswordValidationMode="Custom"

                                customUserNamePasswordValidatorType="PasswordDigest.MyValidator, PasswordDigest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

      </serviceCredentials>

    </behavior>

  </serviceBehaviors>

</behaviors>

  • Finally implement your custom validator by inheriting from Microsoft.ServiceModel.SecurityExtensions.UserNamePasswordDigestValidator. This class integrates with various extensions and implements the logic of PasswordDigest validation.

I have attached the source code and sample project with this post.

Posted in Extensibility, Security, WCF | 3 Comments »

Using WIF with Workflow Services

Posted by zamd on July 4, 2010

Windows Identity Foundation integrates with WCF at the host level (ServiceHost) and then various WIF extensibility points are invoked at different stages of WCF message processing pipeline. In this post, I will show how WIF can be integrated with Workflow Services to enable common security scenarios. Additional rich security scenarios can also be enabled by using activities which are currently part of WF Security Pack project.

Basic Walkthrough

Create a new WCF Workflow Service Application by choosing File -> New Project:

clip_image002[4]

You would notice generated web.config is using simplified WCF 4.0 configuration. There are no explicit endpoint definitions rather service would get default endpoints at runtime.

  <system.serviceModel>

    <behaviors>

      <serviceBehaviors>

        <behavior>

          <!– To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment –>

          <serviceMetadata httpGetEnabled="true"/>

          <!– To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information –>

          <serviceDebug includeExceptionDetailInFaults="false"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

  </system.serviceModel>

A workflow service is a standard WCF service whose implementation is a workflow, so we should be able to integrate WIF using exactly the same techniques we know for code based WCF services.

Externalize the authentication to an STS

Inside Solution Explorer, right click on your workflow service project and select “Add STS Reference” option to launch FedUtil.exe.

On the Welcome Page, specify the Application URI and click next. On Security Token Service Page, select “Use an existing STS” option and specify the URL of the WS-Federation metadata document. For this example, I’m using AD FS 2.0 as the existing STS.

clip_image002[6]

Leave the default value “Disable certificate chain validation” on next page as I’m using self-signed certificates.

On the “Security token encryption”, choose “Enable encryption” option and specify the private key certificate of your service.

clip_image002[8]

Finish the wizard by choosing default values for the next two pages.

Wizard would generate all the configuration required to enable WIF on this Workflow Service. Lets briefly examine the generated configuration:

A new WIF related section is registered…

  <configSections>

    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

  </configSections>

 

<microsoft.IdentityModel> configuration section is generated to externalize the authentication responsibility for this service to AD FS 2.0.

 

  <microsoft.identityModel>

    <service>

      <audienceUris>

        <add value="http://localhost:2534/Service1.xamlx" />

      </audienceUris>

      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">

        <trustedIssuers>

          <add thumbprint="A1D6790E1CD8EAF2A23768E5789805E1AE58C9D6" name="http://ids.bccoss.com/adfs/services/trust" />

        </trustedIssuers>

      </issuerNameRegistry>

      <certificateValidation certificateValidationMode="None" />

    </service>

  </microsoft.identityModel>

 

A new Service Behaviour is added to the default behaviour collection, which configures WIF on this Workflow Service at the host level (WorkflowServiceHost in this case)

      <serviceBehaviors>

        <behavior>

          <federatedServiceHostConfiguration />

          <!– To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment –>

          <serviceMetadata httpGetEnabled="true" />

          <!– To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information –>

          <serviceDebug includeExceptionDetailInFaults="false" />

          <serviceCredentials>

            <!–Certificate added by FedUtil.  Subject=’CN=SampleService’, Issuer=’CN=Root Agency’.–>

            <serviceCertificate findValue="F042616A35AD30BE5069B468A67FF1233824F5F1" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />

          </serviceCredentials>

        </behavior>

      </serviceBehaviors>

 

Default binding for the http scheme is changed to a wizard generated wsFederationHttpBinding. This enables all http based default endpoints to be configured to use this wsFederationHttpBinding.

    <protocolMapping>

      <add scheme="http" binding="ws2007FederationHttpBinding" />

    </protocolMapping>

A new default configuration of ws2007FederationHttpBinding is also generated by the wizard based on the selection made in Wizard. This generated binding has an issuerMetadata element specifying the metadata URL of AD FS 2.0 which would be used by the clients to fetch all the endpoints supported by AD FS 2.0

 

    <bindings>

      <ws2007FederationHttpBinding>

        <binding>

          <security mode="Message">

            <message>

              <issuerMetadata address="https://ids.bccoss.com/adfs/services/trust/mex" />

              <claimTypeRequirements>

                <!–Following are the claims offered by STS ‘http://ids.bccoss.com/adfs/services/trust&#8217;. Add or uncomment claims that you require by your application and then update the federation metadata of this application.–>

                <add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="true" />

                <add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" isOptional="true" />

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/claims/CommonName&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/claims/EmailAddress&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/claims/Group&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/claims/UPN&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid&quot; isOptional="true" />–>

                <!–<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname&quot; isOptional="true" />–>

              </claimTypeRequirements>

            </message>

          </security>

        </binding>

      </ws2007FederationHttpBinding>

    </bindings>

 

That’s it. Your claim-aware Workflow Service is ready now.

At this stage, clients can generate the required configuration by simply using “Add Service Reference” wizard which would generate config  (endpoint addresses & bindings) for all the different endpoints supported by AD FS 2.0.

The main service endpoint would automatically be configured to acquire a token from AD FS 2.0 before calling the Workflow Service. 

As AD FS 2.0 supports multiple endpoints, generated wsFederationHttpBinding simply picks the first endpoint returned by the AD FS 2.0. This first endpoint choice might not be appropriate in all scenarios and you can easily change it in the client config file.

 

 

        <client>

            <endpoint address="http://localhost:2534/Service1.xamlx" binding="customBinding"

                bindingConfiguration="WS2007FederationHttpBinding_IService"

                contract="ServiceReference1.IService" name="WS2007FederationHttpBinding_IService">

                <identity>

                    <certificate encodedValue="AwAAAAEAAAAUAAAAhxjyEc1MCXsA5XXteMRn0iyQBuwgAAAAAQAAALgBAAAwggG0MIIBYqADAgE==" />

                </identity>

            </endpoint>

        </client>

 

 

Claims Transformation

ClaimsAuthenticationManager can be used to perform claims-transformation exactly in the same way as in code based service.

 

    public class SimpleClaimsAuthenticationManager:ClaimsAuthenticationManager

    {

        public override IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)

        {

            // Claims transformation…

            return base.Authenticate(resourceName, incomingPrincipal);

        }

    }

 

This class can then be configured on the workflow service using following configuration. All other config except the highlighted line was automatically generated by FedUtil.exe.

 

 

    <service>

      <audienceUris>

        <add value="http://localhost:2534/Service1.xamlx" />

      </audienceUris>

      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">

        <trustedIssuers>

          <add thumbprint="A1D6790E1CD8EAF2A23768E5789805E1AE58C9D6" name="http://ids.bccoss.com/adfs/services/trust" />

        </trustedIssuers>

      </issuerNameRegistry>

      <certificateValidation certificateValidationMode="None" />

      <claimsAuthenticationManager type="DeclarativeServiceLibrary1.SimpleClaimsAuthenticationManager"/>

    </service>

 

Claims based Authorization

 

Similarly Claim-Based-Authorization can be enforced at the host level using a ClaimsAuthorizationManager.

 

    public class SimpleClaimsAuthorizationManager:ClaimsAuthorizationManager

    {

        public override bool CheckAccess(AuthorizationContext context)

        {

            if (((IClaimsIdentity)context.Principal.Identity).Claims.Where(c=>

                c.ClaimType== ClaimTypes.Upn &&

                c.Value == "zam1d@bccoss.com").Count() ==0)

                return false;

 

            return true;

        }

    }

 

      <claimsAuthorizationManager type="DeclarativeServiceLibrary1.SimpleClaimsAuthorizationManager"/>

 

With above authorization manager plugged in, you can verify client is failing with “Access is denied” error.

 

clip_image002

 

Summary

Most of the WIF integration simply works with Workflow Services without any issues and is exactly the same as code based services. If you need to access claims inside your workflows or you want to implement more advance security scenarios such as

  • Fine grained control over token issuance and it’s usage
  • Claims-based delegation to other services

You can use WF Security Pack which provides additional activities and accompanying framework code to accomplish above mentioned security scenarios.

Posted in WF4, WFSP | 5 Comments »

WF Security Pack is live now

Posted by zamd on July 1, 2010

  Wow, CTP1 of WF Security Pack is gone live now. Go get it from:

 http://wf.codeplex.com/releases/view/48114

Check out following post for an introduction. 

http://blogs.msdn.com/b/endpoint/archive/2010/06/30/introducing-the-wf-security-pack-ctp-1-on-wf-codeplex-com.aspx

My blog also has a slightly old intro post.

http://zamd.net/2010/02/23/introducing-workflow-security-pack/

Over the next few days, we will cover all the interesting scenarios enabled by WF Security Pack. Stay tuned…  Please let us know what do you think about this pack. This would help us bring this functionality in next version of .Net Framework.

Posted in Uncategorized | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.