The holy grail of Enterprise SOA security

A couple of years ago, the platform was not rich enough to create complex security solutions for service oriented applications based on standards. WSE was a half way path. With the advent of WCF we finally have a foundation to build a security subsystem flexible and robust for the enterprise.

The following illustration shows the different components involved and how they interact between each other.

soa

Most of enterprise line of business apps have used a login to authenticate their users and roles to authorize them. When webservices were not there, the business logic was hosted in the application itself and advanced users hosted in COM+. The authentication and access checks were mixed in the same code that performed the business logic. Sometime later enterprises started to realize that integration between applications was getting harder: SOA implemented with webservices came to the rescue.

Now, the business logic lived in the application server and was accessed through webservices. The problem became to "how can I secure my services?". I've seen many different implementations: using kerberos, using custom tickets, certificates, etc. However they were coupled to the platform or they were custom solutions. Single sign on and access check across applications of different platform is hard to accomplish: WS-Trust, STS and SAML comes to the rescue.

Let's describe the scenario:

1. A user browses to Login.aspx, enters his username and password and click "Login". As this is the only time when we have username and password available from the user, we can fill the UserName token on the proxy. We call a Ping service which is just a dummy service that we use to obtain a SamlToken.

using (SystemServiceChannel channel = new SystemServiceChannel())
{
    channel.ClientCredentials.UserName.UserName = LoginControl.UserName;
    channel.ClientCredentials.UserName.Password = LoginControl.Password;
    channel.Ping();
}

2. The client endpoint is configured to use wsFederationHttpBinding and the issuer of the token is the Authorization STS. Since the client does not have a SamlToken yet, it will send the UserName token to the Authorization STS so he can issue one. Below is the binding.

<binding name="SecureConversationBinding">
  <security mode="Message">
    <message issuedKeyType="SymmetricKey" 
issuedTokenType="http://....saml-token-profile-1.1#SAMLV1.1"> <issuer address="http://services.litwarehr.com/Authz/STS.svc" binding="wsFederationHttpBinding" bindingConfiguration="AuthorizationSTS"> <identity> <dns value="SaasyLongTailCert" /> </identity> </issuer> </message> </security>

3.  The Authorization STS trust on the Authentication STS and request him a SamlToken. The Authentication STS message security is configured to use wsHttpBinding with UserName token.

<binding name="AuthorizationSTS">
  <security mode="Message">
    <message issuedKeyType="SymmetricKey" 
                     issuedTokenType="http://...-saml-token-profile-1.1#SAMLV1.1">
      <issuer address="http://services.litwarehr.com/Auth/Sts.svc"
        binding="wsHttpBinding" bindingConfiguration="AuthenticationSTS">
        <identity>
          <dns value="SaasyLongTailCert" />
        </identity>
      </issuer>
    </message>
  </security>
</binding>
<binding name="AuthenticationSTS">
  <security mode="Message">
    <message clientCredentialType="UserName" 
                     negotiateServiceCredential="true"
                     establishSecurityContext="false" />
  </security>
</binding>

4. The Authentication STS service has a custom UserName Password validator behavior configured. Before the request actually gets to the STS it will go through the username/password validation. LitwareHr default implementation uses ADAM.

<serviceBehaviors>
   <behavior name="AuthenticationSTS">
      <serviceCredentials>
        <userNameAuthentication 
userNamePasswordValidationMode="Custom" 
customUserNamePasswordValidatorType="CustomValidator, Sts"/>
       </serviceCredentials>
   </behavior>
</serviceBehaviors>

5. If the authentication was succesful , the Authentication STS will issue a basic SamlToken containing the identity name of the caller.

Collection GetIssuedClaims(RequestSecurityToken rst)
{
    string caller = ServiceSecurityContext.Current.PrimaryIdentity.Name;
    Collection samlAttributes = 
                    new Collection();
    samlAttributes.Add(
        new SamlAttribute(
        new Claim(ClaimTypes.Authentication, 
                  caller, 
                  Rights.PossessProperty)));

    return samlAttributes;
}

6. Since the Authorization STS trusts on the SamlTokens issued by the Authentication STS, it will grab the token, extract the username claim and retrieve the actions available for the user. This happens in the IAuthorizationPolcy configured on the STS.

public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
    // check if this context was updated for this user
    if (state == null)
    {
        // Create an empty list of Claims
        IList claims = new List();
        // Add list of actions the user can perform
        string user = GetUserFromClaimSets(evaluationContext.ClaimSets);
        foreach (string action in GetActionsForUser(user))
        {
            claims.Add(new Claim(
                    ClaimTypes.AuthorizationDecision, 
                    action, 
                    Rights.PossessProperty));
        }
        ...
     }
}

7. The SamlToken enriched now is ready to go through the service pipeline.

8. This is where the access check happens. The ServiceAuthorizationManager class has access to the AuthorizationContext which exposes the SamlToken with the claims.

public override bool CheckAccess(OperationContext operationContext)
{
    // Extract the AuthorizationContext from the ServiceSecurityContext
    AuthorizationContext authContext = 
        operationContext.ServiceSecurityContext.AuthorizationContext;

    // Guard denies exectuion if the action is not in the token as a claim
    string action = operationContext.IncomingMessageHeaders.Action;
    IEnumerable authorizationDecisionClaims = 
        claimSet.FindClaims(ClaimTypes.AuthorizationDecision, Rights.PossessProperty);

    foreach (Claim claim in authorizationDecisionClaims)
    {
        string authzAction = claim.Resource as string;
        if (!string.IsNullOrEmpty(authzAction) &&
            authzAction.Equals(action, 
                              StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }

    // If no AuthorizationDecision claim had a resource value that matched the 
    // current action name, return false (Access Denied)
    return false;
}

9. Finally, the SOAP message gets to the service implementation and the response is sent back to the client with the SamlToken attached.

10. The SamlToken is cached on the client using an HTTP cookie. This is achieved by using a custom IssuedSecurityTokenProvider.

Conclusion and a bit of SaaS

WCF provides an extensible foundation that allow taking the service oriented on the enterprise to the next level using standards like WS-Trust and SAML.

Since SOA is part of the deal for Software as a Service apps we implemented this architecture on LitwareHR to get ready for future scenarios like Federated Security. In this scenario the tenant wants to manage authentication and authorization using its own infrastructure. He owns an STS that issues SamlTokens for every app in the enterprise and the IT guys don't want to manage yet another user/password. The SaaS provider (LitwareHR) will allow the tenant to configure the claims mapping and the tenant STS to rely on. By doing this, the IT administrator for Contoso (the tenant) will manage a single authorization store and will configure the claim mappings on the "cloud" apps: the "Administrator" role in my enterprise is the "ConfigureAndCustomize" role in the SaaS application.

If you want to see a working implementation of the scenario described in the figure above download LitwareHr Software as a Service sample application and look for Shp.Security.BrokeredReceiver and Shp.Security.BrokeredSender projects. If you are taking SOA seriously, then it might worths looking at it.

Published: March 11 2007

blog comments powered by Disqus