Just as I was singing Ingo's praises in the last posting my SharpReader icon turned yellow with news of Ingo's a new MSDN article on role-based security with WSE 2.0. The article is mostly on using X.509 tokens together with roles and policy files. The latest project I worked on used the WSE custom token managers to authenticate a SAML token as well as a custom XML token (a substitute for the ASP.NET Session Id HTTP Header, but for web services). However, I wasn't sure whether you could use the same technique for X.509 Certificates as this seemed to be handled automatically by the WSE framework.
The solution Ingo demonstrates is to derive a class from WSE's X509SecurityTokenManager and override it's AuthenticateToken method, calling the WSE implementation after doing any custom code, as follows:
public class X509RoleBasedSecurityTokenManager:
X509SecurityTokenManager
{
protected override void AuthenticateToken(X509SecurityToken token)
{
base.AuthenticateToken(token)
// do some custom work, like setting the token.Principal
}
}
Once a TokenManager's authenticate (for binary tokens) or validate (for Username tokens) method has been fired (it's hooked up using the AppDomain's config file, usually the web.config file, or using WSE Visual Studio add-in) then the policy file is applied. This allows you to make declarations in the policy file about the roles the authenticated/validated user must be in to access the method, without having to write any code on the [WebMethod]. Ingo uses the WSE Policy Editor tool rather than writing the Policy XML by hand (good choice), but just to make it real, the policy file would have the following:
<wsp:Policy wsu:Id="CertificateRoles" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy">
<wsse:Integrity wsp:Usage="wsp:Required" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext">
<wsse:TokenInfo>
<SecurityToken xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext">
<wsse:TokenType>wsse:X509v3</wsse:TokenType>
<wsse:Claims>
<wse:Role value="Accountant" xmlns:wse="http://schemas.microsoft.com/wse/2003/06/Policy" />
</wsse:Claims>
</SecurityToken>
</wsse:TokenInfo>
<wsse:MessageParts Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">wsp:Body()</wsse:MessageParts>
</wsse:Integrity>
</wsp:Policy>
This snippet defines a policy that says a soap request must contain an X.509 certificate security token that plays the role of Accountant in this application and that the soap:body of the request must be digitally signed with this certificate.
As I've mentioned before, this is is a great idea as it separates the code from the security settings.
In the article, Ingo maps from the incoming certificate to some server-side certificate-role mappings to look up the roles that the user (represented by the certificate) is authorised to play in the application. In his case he stores mappings between the certificate thumbprint (think Certificate Id) and roles in the configuration file. I was thinking that it would be better to carry the role information inside the XML that represents the X509 binary security token (for example, SAML tokens have a collection of attributes that can be used for this value). Perhaps it is better that each application map the certificate to local application roles as Ingo demonstrates rather than carry these with the tokens. If the roles are defined with the tokens it means the issuer/creator of the tokens has to understand what roles the application's roles, which might be too tight a coupling between the token issuer and the application.
This was another good article from Ingo (he's certainly got his finger on the pulse. I wonder if WSE and Service Oriented Architectures are part of his new book?). It's great to see more articles on using WSE 2.0. I can't wait for the other articles that Matt Powell mentioned.