Custom WCF Authorization using ServiceAuthorizationManager
I was recently tasked with implementing authentication and authorization in an existing WCF-based service using Windows Live ID. The service already had an extensive set of BVT tests that I didn't necessarily want to break. Integrating Live ID authentication in our service tests would require some additional code modifications and require another pass through those tests.
So, implement authentication and authorization and don't make modifications to existing test code. Seems relatively straight-forward! I grabbed my favorite group of triage to bounce design decisions around and our authorization scheme more or less ended up with two code paths: 1) If the Windows user account is present and in a Test security group OR 2) If the LiveID user is authenticated and authorized, return true.
That's alright; we're adding in some code to make things testable. And, of course, that code would require testing. And creating/maintaining a security group. But it could be better. In comes the ServiceAuthorizationManager class.
WCF allows you to create classes that derive off of ServiceAuthorizationManager. When you do this and override the CheckAccessCore method, you can implement an authorization scheme across all of your web service method calls. In my case, I created a LiveIdServiceAuthorizationManager class that attempts to cast the HttpContext.Current.User.Identity to a PassportIdentity. Some internal authorization checks are made based on that information.
Creating a custom ServiceAuthorizationManager is straightforward and looks like:
public class LiveIdServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
PassportIdentity currentUser = HttpContext.Current.User.Identity as PassportIdentity;
bool isAuthorized = false;
if (currentUser != null)
{
...
}
return isAuthorized;
}
}
Enabling our custom authorization manager is a configuration change that looks something like this:
<serviceBehaviors>
<behavior name="SomeBehavior">
<serviceAuthorization serviceAuthorizationManagerType="SomeNamespace.LiveIdServiceAuthorizationManager, SomeAssembly" />
</behavior>
</serviceBehaviors>
The <serviceAuthorization> node can be removed in BVT scenarios, allowing the service to successfully run through our BVT process without any code changes. However, when the node is present, callers must be authenticated through Live ID. This fulfills both of our requirements in a seemingly elegant way.