You should look into implementing a ServiceAuthorizationManager
for your WCF service to handle the HTTP Authorization header authorization.
Create a class that inherits from System.ServiceModel.ServiceAuthorizationManager
, and override one or more of the CheckAccess
functions to examine the incoming web request and decide whether to allow it in or reject it. Rough sketch:
public class MyServiceAuthorizationManager: System.ServiceModel.ServiceAuthorizationManager
{
public override bool CheckAccess(OperationContext operationContext, ref Message message)
{
var reqProp = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
var authHeader = reqProp.Headers[HttpRequestHeader.Authorization];
var authorized = // decide if this message is authorized...
if (!authorized)
{
var webContext = new WebOperationContext(operationContext);
webContext.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
webContext.OutgoingResponse.Headers.Add(HttpResponseHeader.WwwAuthenticate, String.Format("Bearer realm=\"{0}\"", baseUri.AbsoluteUri));
}
return authorized;
}
}
Wire this into your WCF service where you create the service host:
restAPIServiceHost = new DataServiceHost(typeof(API.RestAPIService), restUris);
var saz = restAPIServiceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
if (saz == null)
{
saz = new ServiceAuthorizationBehavior();
restAPIServiceHost.Description.Behaviors.Add(saz);
}
saz.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
restAPIServiceHost.Open();
This will inject an authorization check into every method exposed by the WCF service, without requiring any changes to the service methods themselves.
Your MyServiceAuthorizationManager implementation can also be installed into your WCF service using web.config magic, but I find direct code easier to understand and debug.
Note that it will be difficult to have multiple authorization check systems in force on the same service without them stomping on each other or leaving a gap in your security coverage. If you have a UserNamePasswordValidator
in force to handle the SOAP user credentials case, it will reject a message that contains only an HTTP Authorization header. Similarly, a ServiceAuthorizationManager
that only checks for HTTP Authorization header will fail a web request containing SOAP user credentials. You will most likely need to figure out how to check for both kinds of auth credential representations in the same auth check. For example, you could add code to the CheckAccess function above to look for, extract, and test the SOAP user credentials if an HTTP Authorization header is not present in the message.
When you have to accept multiple auth representations you'll need to decide on precedence, too. If an HTTP Authorization header is present, I suspect it should take precedence over anything contained in the SOAP message. If the HTTP Authorization header is present but invalid, full stop - reject the request as unauthorized. It doesn't matter what's in the SOAP stuff - an invalid HTTP Authorization header is always bad news. If there is no HTTP Authorization header at all, then you can poke around to see if there is a SOAP security element from which you can get SOAP user credentials and test them for validity.