Zulfiqar's weblog

Architecture, security & random .Net

Archive for the ‘WIF’ Category

NuGet Package to enable SWT in WIF

Posted by zamd on April 27, 2011

Just found Daniel Cazzulino@Clarius has packaged some of my work in a reusable NuGet package. Pretty cool…

image

http://nuget.org/List/Packages/netfx-Microsoft.IdentityModel.Swt

Posted in WIF | Leave a Comment »

Using Simple Web Token (SWT) with WIF

Posted by zamd on February 8, 2011

SAML 1.1/SAML 2.0 is the default token format when using ACS as the authentication service for your website. In this model, your website talks to ACS using WS-Federation protocol and what it normally gets back is a Saml token. This scenarios is fairly straight-forward as WIF natively supports WS-Federation protocol & SAML1.1/SAML 2.0 token formats.

There are cases where you might want to return a Simple Web Tokens (SWT) after a successful authentication. For example, you might want to use this same SWT (available as a bootstrap token) to call other downstream REST/OData services as depicted in the following diagram.

image

ACS fully supports returning an SWT token after a successfully WS-Fed authentication but WIF currently doesn’t support SWT tokens. You would have to write a custom Security Token Handler for WIF to process SWT tokens coming back to your website. I have created some extensions which enables this and other OAuth WRAP related scenarios. Feel free to download the code from my SkyDrive.

Posted in WIF, Windows Azure AppFabric | 13 Comments »

Silverlight Claim-Based-Security

Posted by zamd on February 8, 2011

This would hopefully be a multi-part series showing some tricks to enable claims-based-security in Silverlight 4.0. Silverlight 5.0 would have a much better story around claim-based-security as mentioned here.

In this first post, I’ll give you a high level overview of the solution. The main idea is to use the ‘WCF Routing Service’ in the DMZ to route both token issuance requests & business requests to the actual backend services.

1. Routing Service looks for a token issuing request and forwards it to the STS where the actual authentication is performed. After the successful authentication, STS issues a SAML token which goes back to Silverlight client via the routing service. Routing Service also terminates the SSL and backend is called using straight HTTP. This model offers strong security on the internet while keeping the internal deployment simpler & efficient. This model also resembles with the standard SSL offloading setup where a hardware load-balancer is used to terminate the SSL.

1: Token Issuance Path

image

2. Once the Silverlight client got a SAML token, it can attach it to all subsequent message sent to business service(s). Routing Services forwards al the business messages (messages which doesn’t match the token issuance filter) to the actual backend services again doing the protocol transitioning from HTTPS to HTTP. Please note, here you can use the rich filtering mechanism provided by the Routing services to decide which messages should to which services. I used a very simple MatchAll filter which forwards all the non-token-issuance messages to the business service.

2: Web Service Call Containing a SAML Token

image

To implement the 1st part of solution I have used the WSTrustClient class & the associated bindings from the identity training kit.

var vm = this.DataContext as MainPageViewModel;

var stsBinding = new WSTrustBindingUsernameMixed();

var stsCreds = new UsernameCredentials(vm.UserId, vm.Password);
var client = new WSTrustClient(
    stsBinding,
    new EndpointAddress(vm.SelectedEndpoint),
    stsCreds);

var rst = new RequestSecurityToken(WSTrust13Constants.KeyTypes.Bearer);
rst.AppliesTo = new EndpointAddress(vm.AppliesTo);

client.IssueCompleted += new System.EventHandler<IssueCompletedEventArgs>(client_IssueCompleted);
client.IssueAsync(rst);

For the 2nd part I have implemented a message inspector along with an extension method which makes it super easy to attach the SAML with outgoing messages.

var vm = this.DataContext as MainPageViewModel;
var client = new ServiceReference1.Service1Client();

client.AttachToken(vm.SecurityToken.RawToken);

client.GetDataCompleted += new EventHandler<ServiceReference1.GetDataCompletedEventArgs>(client_GetDataCompleted);
client.GetDataAsync(32);

Posted in Security, WCF, WIF | 11 Comments »

WIF based OAuth WRAP Issuer

Posted by zamd on August 13, 2010

WIF provides an API to develop Security Token Services (STSs) which can then be exposed using either WS-Trust (Active-STS) or WS-Federation(Passive-STS) protocols. As mentioned in last post, WIF currently doesn’t support OAuth WRAP protocol so out of box a WIF based SecurityTokenService cannot be used as an OAuth WRAP issuer. In this post, I’ll show you some extensions I have created to expose a service, based on WIF’s token issuance object model (SecurityTokenService, RequestSecurityTokenRequest etc), as an OAuth WRAP issuer.

1: Create an issuer using the standard WIF approach. The only difference is that I’m using a symmetric key for signatures.

public class OAuthIssuer : SecurityTokenService

{

    public OAuthIssuer(SecurityTokenServiceConfiguration config):base(config){}

 

    protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)

    {

        return new ClaimsIdentity(new Claim[] {

               

            new Claim(ClaimTypes.Name, "John"),

            new Claim("email", "John@test.com") });

    }

 

    protected override Scope GetScope(IClaimsPrincipal principal,

        RequestSecurityToken request)

    {

        var scope =  new Scope

        {

            AppliesToAddress = request.AppliesTo.Uri.AbsoluteUri

        };

 

        scope.TokenEncryptionRequired = false;

        scope.SymmetricKeyEncryptionRequired = false;

        scope.SigningCredentials = new SymmetricSigningCredentials("Sapm9PPZZHlo=");

        return scope;

    }

}

2: Host the issuer using following code:

var config = new OAuthIssuerConfiguration()

{

    SecurityTokenService = typeof(OAuthIssuer)

};

config.TokenIssuerName = "MyCustomIssuer";

 

config.SecurityTokenHandlers.AddOrReplace(new CustomUserNameSecurityTokenHandler

{

    UserNamePasswordValidator = (uid, pwd) =>

    {

        Console.WriteLine(uid + " validated.");

    }

});

 

var sh = new OAuthServiceHost(config, new Uri("http://localhost:9111&quot;));

sh.Open();

That’s it, A WIF based OAuth WRAP issuer is ready.

OAuthServiceHost inherits from WCF WebServiceHost and exposes a fixed OAuth WRAP contract to the outside world.

public class OAuthServiceHost : WebServiceHost

{

    internal OAuthIssuerConfiguration Configuration { get; set; }

 

    public OAuthServiceHost(OAuthIssuerConfiguration config)

        : this(config, null) { }

 

    public OAuthServiceHost(OAuthIssuerConfiguration config, Uri baseAddress)

        : base(typeof(OAuthIssuerContract), baseAddress)

    {

        this.Configuration = config;

    }

}

The implementation of OAuth WRAP contract transforms the incoming token issuance request into WIF’s token issuance object model (RequestSecurityTokenRequest etc) and starts the token issuance pipeline. At the end of the pipeline, it packages the final set of claim in a  Simple Web Token and returns it back.

Source code

Posted in WIF | 6 Comments »

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 »

Integrating WIF, WF 4.0 & AppFabric: Claims-Based-Delegation

Posted by zamd on June 3, 2010

Extending upon my last post, where I have talked about basic integration, this post will go into the details of claims-based-delegation in WF 4.0 & AppFabric.

Again I’ll be using activities from the Workflow Security Pack. Let’s start with a diagram which captures the main components of solution and their interactions:

image 

  1. Unauthenticated user browse to web application protected by WIF modules.
  2. WIF redirects user to Passive STS for authentication
  3. User authenticates @ passive STS and is redirected back to the ASP.net app along with the Issued Token. WIF modules processes this token and upon successful validation of the token, user is logged into the application. I have configured SaveBootstrapTokens on this app, so the raw incoming token is preserved as part of IClaimsIdentity.
  4. Users click the “Call Service” link to invoke following client workflow. GetBootstrapToken activity reads the bootstrap token and enlist it with the SecurityTokenHandle (specified on the InitializeActAsToken activity) as an ActAs token.image
  5. InitializeSamlSecurityToken activity issues a request (RST) to acquire a SAML token from the STS using the ActAs token enlisted in step 4. InitializeSamlSecurityToken is able to see the ActAs token (enlisted by InitializeActAsToken activity)  because both of these activities share the same SecurityTokenHandle. At this stage, Web App is authenticated by STS using windows authentication while ActAs token is a Saml token (acquired using forms authentication in step 3). The final Saml token will contain claims for both immediate (web app) and original caller (authenticated user).
  6. TokenFlowScope activity along with the workflowCredentials behaviour (configured on the endpoint used by the Echo activity) enhances the WCF security pipeline to attach the Saml token (acquired in step 5) with the outgoing message. As part of it’s execution, TokenFlowScope will detect that Echo activity requires a Saml token, it will then check it’s enlisted tokens to see if it can satisfy the token requirements of the Echo activity. In this example, there is already a Saml token enlisted with the handle so TokenFlowScope simply attaches that token with the outgoing message.

I have attached complete solution with this post. I tried to keep the solution self-contained by using file-based certificates and other shortcuts so hopefully you should be able to get it working by just hitting Ctrl-F5 :)

Feel free to download and experiment and let me know your thoughts…

Posted in WF4, WFSP, WIF | 10 Comments »

Integrating WIF with WF 4.0 and AppFabric

Posted by zamd on May 18, 2010

WIF is framework to claim-enable ASP.net applications and WCF services. WF 4.0 introduced a new paradigm for developing services (known as Workflow Services), whose implementation is based on workflow. Windows Server AppFabric provides the hosting, management & monitoring capabilities for services with a primary focus on workflow services.

WIF integration with Workflow services can be seen at couple of different scopes.

1. Claims-Enabled Workflow Service

As workflow services are standard WCF services, WIF can easily be enabled at the WCF layer using the standard configuration based approach documented in MSDN. This would claims-enable your workflow services and you can use various WIF’s extensions (code-based) for claims-transformation (ClaimsAuthenticationManager) and claims-based authorization (ClaimsAuthorizationManager).

2. Workflow Services calling other Claims-Enabled Services

WF 4.0 provides messaging activities to call other services from workflows. In most cases, Claims-Enabled services require a token from an STS. In a non-WF world, you can either use wsFederationHttpBinding or the new fine grained WIF API (WSTrustChannelFactory) to do this (you can use IssuedSecurityTokenProvider directly). The wsFederationHttpBinding approach kind of works (transparently) in Workflow services world as well but could be quite expensive in terms of performance. See my post on messaging activities for additional details. The wsFederationHttpBinding approach is also not suitable for scenarios, where you need fine-grained control of issued tokens or you want to use issued token in long-running scenarios without re-acquiring them.

3. WIF in middle-tier Workflow Services

WIF enables claims-based-delegation using then ActAs/OnBehalfOf element of WS-Trust protocol. In code-based services, you can access the incoming token in the middle-tier service and then use this when acquiring a SAML token to call a backend service. With this model, the backend service can see all the identities involved in the call chain. Claims-based delegation is not easily possible in workflow services when using wsFederationHttpBinding.

1 & 2 be greatly enhanced by introducing custom activities which can decouple token acquisition from its use; much like activity counterpart of WIF’s WSTrustChannelFactory API.

saml

In above diagram InitializeSamlSecurityToken custom activity encapsulate the functionality of acquiring a token from a STS using the WS-Trust protocol. Internally it uses the standard correlated Request-Reply pair configured for WS-Trust contract. I’ll talk more about TokenFlowScope in a future post but here it enables the Ping activity to use the acquired token when calling a service which requires Saml token. I have attached complete solution with post (STS, Test Service & a Test Client)which also includes alpha drop of WFSP binaries as well. Feel free to download and experiment and let me know your thoughts.

Posted in WF4, WFSP, WIF | 9 Comments »

Configuring SAML Assertion Subject Name and Format for a WIF STS

Posted by zamd on April 27, 2010

In some interop scenarios, subject name and its format needs to be included in the Saml token/assertion generated by the STS. You can easily configure a WIF based STS to generate this by adding a NameIdentifier claim and by settings it’s format property.

protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal,

    RequestSecurityToken request, Scope scope)

{

    var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, "me@zamd.com");

    nameIdentifierClaim.Properties[ClaimProperties.SamlNameIdentifierFormat] = "EMAIL";

 

    return new ClaimsIdentity(

        new Claim[]

        {

            new Claim(System.IdentityModel.Claims.ClaimTypes.Name, "Zulfiqar"),

            nameIdentifierClaim

        });

 

This generates following Saml Assertion where you can see the generated NameIdentifier & format attribute.

 <saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
   <saml:Subject>
    <saml:NameIdentifier Format="EMAIL">me@zamd.com</saml:NameIdentifier>
     <saml:SubjectConfirmation>
      <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
    </saml:SubjectConfirmation>
  </saml:Subject>
   <saml:Attribute AttributeName="name" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
    <saml:AttributeValue>Zulfiqar</saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>

Posted in WIF | 1 Comment »

ExtractSAMLAssertion

Posted by zamd on March 10, 2010

Few people have asked me for the source code of ExtractSAMLAssertion helper method/class, which I have used in my article and blog posts. So here you go…

Please note this is just raw test code and doesn’t have any error checking and other related goodies.

 

    class Program

    {

        static void Main(string[] args)

        {

            var binding = new WSHttpBinding(SecurityMode.Message);

            binding.Security.Message.ClientCredentialType = MessageCredentialType.None;

            binding.Security.Message.EstablishSecurityContext = false;

 

            var fac = new WSTrustChannelFactory(binding, http://localhost:9000/STS&#8221;);

            fac.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            fac.Open();

           

            var rst = new RequestSecurityToken {

                AppliesTo = new EndpointAddress(http://localhost&#8221;),

                RequestType = WSTrustFeb2005Constants.RequestTypes.Issue

            };

 

            var token = fac.CreateChannel().Issue(rst) as GenericXmlSecurityToken;

            var rpCert = new X509Certificate2(“localhost.pfx”, “a”);

            var xml = ExtractSAMLAssertion(token, rpCert);

        }

   

        private static string ExtractSAMLAssertion(GenericXmlSecurityToken token, X509Certificate2 rpCert)

        {

            string txtAssertion = string.Empty;

            var handlerCol = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();

            var han = handlerCol.OfType<EncryptedSecurityTokenHandler>().FirstOrDefault();

            if (han != null)

            {

                han.Configuration.ServiceTokenResolver = new EncryptedKeyResolver(rpCert);

                var sr = new StringReader(token.TokenXml.OuterXml);

                var rdr2 = XmlReader.Create(sr);

                var rtok = han.ReadToken(rdr2) as SamlSecurityToken;

 

                var stat = rtok.Assertion.Statements[0];

                var ms = new MemoryStream();

                var memWriter = XmlDictionaryWriter.CreateTextWriter(ms);

                stat.WriteXml(memWriter, new SamlSerializer(), new WSSecurityTokenSerializer());

                memWriter.Flush();

                ms.Seek(0, SeekOrigin.Begin);

 

                txtAssertion = new StreamReader(ms).ReadToEnd();

            }

            return txtAssertion;

        }

    }

 

 

 

    class EncryptedKeyResolver : SecurityTokenResolver

    {

        // Relying party certificate – must hold private key.

        X509Certificate2 rpCert;

        public EncryptedKeyResolver(X509Certificate2 rpCert)

        {

            this.rpCert = rpCert;

        }

        protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)

        {

            key = null;

            try

            {

                var ekec = keyIdentifierClause as EncryptedKeyIdentifierClause;

                if (ekec != null)

                {

                    switch (ekec.EncryptionMethod)

                    {

                        case http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p&#8221;:

                            {

                                var encKey = ekec.GetEncryptedKey();

 

                                var rsa = rpCert.PrivateKey as RSACryptoServiceProvider;

                                var decKey = rsa.Decrypt(encKey, true);

 

                                key = new InMemorySymmetricSecurityKey(decKey);

                                return true;

                            }

                    }

                    var data = ekec.GetEncryptedKey();

                    var id = ekec.EncryptingKeyIdentifier;

                }

            }

            catch (Exception eexx)

            {

                Console.WriteLine(eexx);

                throw eexx;

            }

            return false;

        }

 

        protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)

        {

            throw new NotImplementedException();

        }

 

        protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)

        {

            throw new NotImplementedException();

        }

    }

Posted in Federation/STS, WIF | 7 Comments »

 
Follow

Get every new post delivered to your Inbox.