I\'m trying to implement OAuth security for a WCF SOAP service. I could find samples online which talks about OAUTH and REST service. Is there any best approach to use OAuth
The short answer is a simple yes, you can do this. I tried to find an "official" way to do this and I was not successful, mostly because OAuth is not really designed for this scenario, more on that later. First though how to actually do it. One way to do it would be to provide a custom ServiceAuthorizationManager and inside of it do something like this
public class OAuthAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
// Extract the action URI from the OperationContext. Match this against the claims
// in the AuthorizationContext.
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
try
{
//get the message
var message = operationContext.RequestContext.RequestMessage;
//get the http headers
var httpHeaders = ((System.ServiceModel.Channels.HttpRequestMessageProperty)message.Properties.Values.ElementAt(message.Properties.Keys.ToList().IndexOf("httpRequest"))).Headers;
//get authorization header
var authHeader = httpHeaders.GetValues("Authorization");
if (authHeader != null)
{
var parts = authHeader[0].Split(' ');
if (parts[0] == "Bearer")
{
var tokenClaims = ValidateJwt(parts[1]);
foreach (System.Security.Claims.Claim c in tokenClaims.Where(c => c.Type == "http://www.contoso.com/claims/allowedoperation"))
{
var authorized = true;
//other claims authorization logic etc....
if(authorized)
{
return true;
}
}
}
}
return false;
}
catch (Exception)
{
throw;
}
}
private static IEnumerable ValidateJwt(string jwt)
{
var handler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters()
{
ValidAudience = "urn://your.audience",
IssuerSigningKey = new InMemorySymmetricSecurityKey(Convert.FromBase64String("base64encoded symmetric key")),
ValidIssuer = "urn://your.issuer",
CertificateValidator = X509CertificateValidator.None,
RequireExpirationTime = true
};
try
{
SecurityToken validatedToken;
var principal = handler.ValidateToken(jwt, validationParameters, out validatedToken);
return principal.Claims;
}
catch (Exception e)
{
return new List();
}
}
}
be sure to also set the web.config to use this custom class using the serviceAuthorizationElement
This example requires the System.IdentityModel.Tokens.Jwt nuget package as well, your tokens might be in another format though and in that case you would need to just replace that logic in the example. Also, note that this example is assuming you will be passing our token on the Authorization header in the http request, the OAuth 2.0 Authorization Framework: Bearer Token Usage documentation also specifies that both form encoded body parameters and URI query paramters may be used as well. The form encoded body parameter method is probably entirely incompatible with SOAP services but I see no reason you could not adapt this code to also look at the query parameter method if needed.
What this code does is for every single request to your service the CheckAccessCore method will fire, inside it attempts to extract and validate the JWT oauth token then you can use the extracted principle and associated claims to authorize or deny authorization to the request.
All of this said, I think the best approach would be to not use OAuth at all, the above works but it is a hack to how WCF SOAP services are meant to be secured. OAuth is also not meant to authenticate the user, so you will need to do that in some other way prior to passing the bearer token obtained from authentication on to your service. If you absolutely must use OAuth you can use the above to get you started, there may be better ways but it is not easy by any measure to make it work and be readable. If you have not looked into WS-Security you should do that and familiarize yourself with the abundance of information and possibilities that exist for securing a soap based service most of which have numerous examples to go on here.