3. Accepting tokens from ACS
You’ll need to upgrade the
service so it can receive and work with ACS tokens. This code is fairly
trivial, and much of it is supplied in the AppFabric SDK, which you’ll
have to install in order to follow these next steps. You can find the
SDK on the Azure portal. It also includes several tools that we’ll look
at in the next section.
Exactly how you get the
token and where you process it might change, depending on your business
situation and system architecture, but the steps will be generally the
same.
The first step is to grab the
token from the incoming message. The token will usually be included in
the header as an authorization entry. In some situations, it can also be
in the URL or in the body of the message, depending on the capabilities
of the client.
Exactly how you grab this header will differ based on how you’re receiving the message. In WCF it’s best to do this in a custom ServiceAuthorizationManager
class that’s added to the WCF pipeline when you set up the channel.
Every message will flow through this class, and there you can make a
decision about whether to let it through or deny it access.
In a normal WCF service, you need to use the WebOperationContext to retrieve the header from the request:
string authorizationHeader = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Authorization];
This code will get the raw
header. You now need to do a few things to make sure this token is
valid, and then you can use it to make decisions.
The SDK has all the sample code you need to build a class called TokenValidator. The validator will do a
series of checks for you, and if they all pass, it’ll return true. If the validation fails, the validator will deny access.
validator = new ACSTokenValidator("dqSsz5enDOFjUvUnrUe5p1ozEkp1ccAfUFyuYpawGW0=", "StringReversalInc", "http://localhost/stringservice");
if (!validator.ValidateAuthorizationHeader(authorizationHeader))
DenyAccess();
To initialize the validator, you need to pass in three pieces of information:
You’re passing in the key, the namespace you set up, called StringReversalInc, and the URL of the service you’re protecting, http://localhost/stringservice.
You then call the ValidateAuthorizationHeader on the header you pulled off the message. If this returns false, you’ll deny access by calling a simple little method, DenyAccess, that sets up the deny message:
private static void DenyAccess()
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
WebOperationContext.Current.OutgoingRequest.Headers.Add("WWW-Authenticate", "WRAP");
}
That’s all you need to
receive the header. Most of the work involves making sure it’s a valid
header and something you can trust. This is the same job the bouncer at
the bar does, when he looks at your driver’s license to make sure it’s a
real license and hasn’t been tampered with.
4. Checking the token
We’ve put all of the token-checking logic into the ACSTokenValidator class, and we’ve just discussed how to new up a validator. The validator includes some custom methods, namely Validate and IsHMACValid.
When you pass in the header, the validator will verify several aspects
of it to make sure it’s valid. All of these checks test for the
negative; if the test passes, you have a bad token and the validator
returns false.
Table 1 summarizes the checks that we do in the code.
Table 1. Validation checks performed on a token
Check to be made | Purpose |
---|
string.IsNullOrEmpty(authHeader) | Makes sure you received a header. |
!authHeader.StartsWith("WRAP ") | Ensures the header starts with WRAP. |
nameValuePair[0] != "access_token" | Checks that there are two pieces to the header, and that the first is equal to access_token. |
!nameValuePair[1].StartsWith("\"") || !nameValuePair[1].EndsWith("\"")) | Checks that the second piece starts and ends with a slash. |
!Validate(GetTokenFromHeader(authHeader)) | Grabs the token part of the header and makes sure it’s valid. |
IsHMACValid(token, signingKey) | Makes sure the token has been signed properly. If this is correct, you know who sent it. |
this.IsExpired(token) | Checks that the token hasn’t expired.
Tokens are vulnerable to replay attacks, so this is important. |
this.IsIssuerTrusted(token) | Ensures the sender is recognized as a trusted source. We’ll cover this shortly. |
this.IsAudienceTrusted(token) | Checks that the audience is the intended destination. |
If
the header passes all of these checks, you know you have a secure token
from a trusted source, and that it’s meant for you. This is the minimum
you’ll want to do to allow the message through to the service. You may
also want to crack open the claim set in the token to look at what
claims have been sent, and make decisions on those claims. In our
example, we’ve mapped in some claims. One is the customer ID number, and
the other is the customer’s service level. This might be used to
determine how long the strings they submit to our service can be. They
might have to pay more to reverse longer strings.
That’s all you have to do to
enable the service and consume and use ACS tokens for authorization.
Next we’ll look at how you can configure a client to add the
authorization header to their requests.