Security is important... period. There is no getting around it and we should all have it in mind when developing services of any type. So today I want to speak to the 1st of 2 different mainstream methods used for authenticating client calls to your WCF RESTful service. This post will expand on my last post here titled: Creating a WCF RESTful Service And Secure It Using HTTPS Over SSL. Keeping in the same genre of services types as before, I am speaking about WCF RESTful Services hosted on the internet and authentication methods prominent to this type of scenario. For intranet based RESTful services, you can employ the help of Windows based authentication to authenticate clients inside a Windows domain. However with the popularity of exposing data in a RESTful manner via the internet and the lack of built in security (as opposed to the cradle that Windows can be), I am keeping this focus to the services exposing data for internet scenarios.

In my last post I showed that once you secured the service using a SSL certificate, you could now view a security context when debugging. This is important because now we need to populate that context so we can determine if we want to allow the client to be authenticated to the service, and then check to see if they are authorized for whichever method or operation they have requested.

Once again we can fall back to our knowledge of the web in general for this configuration. Basic Authentication is nothing new to RESTful or even WCF services in general. It is a 401 HTTP challenge/response mechanism to prompt the client for credentials. As we also know, 'Basic' authentication can get a black-eye because it is just a base64 encoded non-encrypted string that is not natively secure, unless used in conjunction with a SSL certificate to secure the transport of this sensitive information.

From my last post we configured a simple REST service using a security mode of 'Transport' with a SSL certificate, and we now need to configure the clientCredentialType attribute. If we add a <transport> element within our existing <security> parent element, we can select Basic as our clientCredentialType. Notice there are several options for this attribute and you can read about all of them here: HttpClientCredentialType Enumeration. You might be wondering about 'Digest' as the security mode, but it is not actually that much more secure than 'Basic' and requires the hosting server to be joined to a domain. As for the others like Windows and NTLM they are good in intranet or extranet hosted scenarios. The 'None' option is the default option, but the whole point of this conversation is about securing our service, so we don't want to use that. The 'Certificate' option will be the focus of my next post on another mainstream way to secure our internet facing RESTful service. Our focus continues to be on using Basic authentication as displayed below:

Our next step is to configure the service to point to a custom user name and password validator method that we will create shortly. Within our <serviceBehaviors> element we can configure the <servicecredentials> element and dictate that we want to use a Custom 'userNamePasswordValidationMode' value. We need to do this so we can intercept the credentials provided by the client via the request message header.

Notice above that I have already provided a name for the class which will intercept and validate these credentials: 'CustomUserNameValidator'. The overridden 'Validate' method in this class will allow us to check if the user accessing our service is going to be authenticated. In the call stack, this method we are going to create will be called prior to the method being requested, so if authentication fails it will happen prior to accessing anything else inside the service. This code snippet that will follow is a close derivative to one that came from the MSDN (here: http://msdn.microsoft.com/en-us/library/aa702565.aspx).

public class CustomUserNameValidator : UserNamePasswordValidator
{
// This method validates users. It allows in two users, user1 and user2
// This code is for illustration purposes only and
// must not be used in a production environment because it is not secure.
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException("You must provide both the username and password to access this service");
}
if (!(userName == "user1" && password == "test") && !(userName == "user2" && password == "test"))
{
// This throws an informative fault to the client.
throw new FaultException("Unknown Username or Incorrect Password");
// When you do not want to throw an informative fault to the client,
// throw the following exception.
// throw new SecurityTokenException("Unknown Username or Incorrect Password");
}
}
}

Looking at the code above we see that we are able to inspect the username and password values to authenticate a user to the service. At this point you are seeing that this is preforming service level authentication and is more coarse grained than some of the method level authorization we will see in a minute. The point of this code is to validate if the client making the call has access to your service.

It should go without saying that you would not use the simplistic implementation from the code above. More than likely, you would probably make a call to a database to validate if the user's credentials are valid as opposed to hardcoding the logic. If the credentials are validated, control will pass on to the originally requested method. The 'Validate' method is void so there is nothing to set or return once authorized. It's a 'no news is good news' type of functionality, where exceptions should be raised only when there is an authentication issue.

This custom method of authenticating users is different as opposed to those of you that have overridden the 'CheckAccessCore' when using a defined 'serviceAuthorizationManagerType' that returns a bool indicating if the user is authorized. Since we are validating the username and password, configuring a value for the 'customUserNamePasswordValidatorType' is exactly what we need.

At this point test out what we have done, by starting your service (e.g. WCF Test Client) and make a call to the 'Customer' method as we built in my last post. This time we will be prompted for credentials by the browser.

Upon entering the correct credentials (username = "user1", password = "test") we get the returned JSON results expected. All of this authentication happened securely because our RESTful service is secured with a SSL certificate. Also note these credentials can be assigned programmatically in whatever language you are using. The beauty of REST services is they are platform and language agnostic and rely on the standards of the web and HTTP. If you happen to be a .NET client calling the service, then you would add the credentials to the request header as shown below:

Now try entering incorrect credentials (in code or in a browser) and make the same REST call. This time as expected and exception is thrown, the client receives a HTTP 403 Forbidden, and we are not permitted to view the results or access the service.

The next and final step here is to take the provided client context a step further with authorization at the method level. The reason for doing this is to offer fine grained security at the method level. For example if you are hosting a RESTful weather service with 10 methods, but only 7 of the methods are served up to everyone and the remaining 3 are for paid subscribers only. In this case we need to preform authorization at the method level.

Remember from my last post I mentioned the importance of the 'ServiceSecurityContext' object. Here is where it will come into play. This is populated with the client's context after being validated by our custom 'Validate' method. Within the ServiceSecurityContext instance the 'PrimaryIdentity' is populated with an instance of System.Principal.Identity.GenericIdentity that contains the properties we need to determine if this user is authorized to the requested method.

The code below will examine this instance and determine if the call can proceed:

//NOTE: This code is within the actual method call (e.g. GetCustomer CLR method)
//Get current SecurityContext to inspect below for authorizing
ServiceSecurityContext securityCtx;
securityCtx = OperationContext.Current.ServiceSecurityContext;
//This code is a bit primitive and ideally you would call off to another method here that would
//perform the logic and probably just return a bool value as in commented out line below:
//if (CheckIfAuthorized(securityCtx) != true)
if ((securityCtx.PrimaryIdentity.IsAuthenticated != true) || (securityCtx.PrimaryIdentity.Name != "user1"))
{
throw new UnauthorizedAccessException("You are permitted to call this method. Access Denied.");
}

If you use the "user1" account you can see that we are indeed both authenticated to the service and authorized to call this method. However, now try and log back into the service with the "user2" account. This account is authenticated to make calls to the site, but not authorized to call this method. Once again you would not hardcode this logic, but rather be calling out to a security file or database to determine the authorization for this user and returning a bool more than likely. This provides that method level fine grained security that many services require.

So to wrap this up, you can implement a well know HTTP authentication method in 'Basic' authentication to secure your RESTful services. We can then take the context of the authenticated client call a step further and implement fine grained authorization at a method level to limit access to methods when needed. By using a well know security protocol that has been secured with SSL over HTTPS, you will broaden your services use and popularity using well know security practices.

About Me

I am a Magenic Associate Principal Consultant that is an advocate of web and Microsoft .NET technologies both professionally and personally. I enjoy the challenge and creativity behind software engineering, and hope during this process to extract some of my thoughts and ideas in order to give back to others in the community through public speaking and here on this blog.