Token based Authentication for WCF HTTP/REST Services: Authentication

This post shows some of the implementation techniques for adding token and claims based security to HTTP/REST services written with WCF. For the theoretical background, see my previous post.

Disclaimer
The framework I am using/building here is not the only possible approach to tackle the problem. Based on customer feedback and requirements the code has gone through several iterations to a point where we think it is ready to handle most of the situations.

Goals and requirements

  • The framework should be able to handle typical scenarios like username/password based authentication, as well as token based authentication
  • The framework should allow adding new supported token types
  • Should work with WCF web programming model either self-host or IIS hosted
  • Service code can rely on an IClaimsPrincipal on Thread.CurrentPrincipal that describes the client using claims-based identity

Implementation overview
In WCF the main extensibility point for this kind of security work is the ServiceAuthorizationManager. It gets invoked early enough in the pipeline, has access to the HTTP protocol details of the incoming request and can set Thread.CurrentPrincipal. The job of the SAM is simple:

  1. Check the Authorization header of the incoming HTTP request
  2. Check if a “registered” token (more on that later) is present
  3. If yes, validate the token using a security token handler, create the claims principal (including claims transformation) and set Thread.CurrentPrincipal
  4. If no, set an anonymous principal on Thread.CurrentPrincipal. By default, anonymous principals are denied access – so the request ends here with a 401 (more on that later).

To wire up the custom authorization manager you need a custom service host – which in turn needs a custom service host factory. The full object model looks like this:

 

Token handling
A nice piece of existing WIF infrastructure are security token handlers. Their job is to serialize a received security token into a CLR representation, validate the token and turn the token into claims.

The way this works with WS-Security based services is that WIF passes the name/namespace of the incoming token to WIF’s security token handler collection. This in turn finds out which token handler can deal with the token and returns the right instances.

For HTTP based services we can do something very similar. The scheme on the Authorization header gives the service a hint how to deal with an incoming token. So the only missing link is a way to associate a token handler (or multiple token handlers) with a scheme and we are (almost) done.

WIF already includes token handler for a variety of tokens like username/password or SAML 1.1/2.0. The accompanying sample has a implementation for a Simple Web Token (SWT) token handler, and as soon as JSON Web Token are ready, simply adding a corresponding token handler will add support for this token type, too.

All supported schemes/token types are organized in a WebSecurityTokenHandlerCollectionManager and passed into the host factory/host/authorization manager.

Adding support for basic authentication against a membership provider would e.g. look like this (in global.asax):

var manager = new WebSecurityTokenHandlerCollectionManager();

manager.AddBasicAuthenticationHandler((username, password) =>
Membership.ValidateUser(username, password));

Adding support for Simple Web Tokens with a scheme of Bearer (the current OAuth2 scheme) requires passing in a issuer, audience and signature verification key:

manager.AddSimpleWebTokenHandler(
“Bearer”
,
http://identityserver.thinktecture.com/trust/initial”
,
https://roadie/webservicesecurity/rest/”
,
“WFD7i8XRHsrUPEdwSisdHoHy08W3lM16Bk6SCT8ht6A=”);

In some situations, SAML token may be used as well. The following configures SAML support for a token coming from ADFS2:

var registry = new ConfigurationBasedIssuerNameRegistry();
registry.AddTrustedIssuer(
“d1 c5 b1 25 97 d0 36 94 65 1c e2 64 fe 48 06 01 35 f7 bd db”, “ADFS”
); var adfsConfig = new SecurityTokenHandlerConfiguration();
adfsConfig.AudienceRestriction.AllowedAudienceUris.Add(
new Uri(https://roadie/webservicesecurity/rest/”
));
adfsConfig.IssuerNameRegistry = registry;
adfsConfig.CertificateValidator =
X509CertificateValidator
.None; // token decryption (read from config) adfsConfig.ServiceTokenResolver =
IdentityModelConfiguration
.ServiceConfiguration.CreateAggregateTokenResolver();
manager.AddSaml11SecurityTokenHandler(
“SAML”, adfsConfig);

Transformation
The custom authorization manager will also try to invoke a configured claims authentication manager. This means that the standard WIF claims transformation logic can be used here as well. And even better, can be also shared with e.g. a “surrounding” web application.

Error handling
A WCF error handler takes care of turning “access denied” faults into 401 status codes and a message inspector adds the registered authentication schemes to the outgoing WWW-Authenticate header when a 401 occurs.

The next post will conclude with authorization as well as the source code download.

(Wanna learn more about federation, WIF, claims, tokens etc.? Click here.)

This entry was posted in IdentityModel, IdentityServer. Bookmark the permalink.

One Response to Token based Authentication for WCF HTTP/REST Services: Authentication

  1. bpeikes says:

    A couple of questions:
    Do you have full source for this example?

    How would you go about having different Auth methods depending on the method? ie I need OPTIONS requests to support Anonymous Auth, since CORS requires no Auth in the OPTIONS preflight request, but everything else should require either basic or windows Auth.

    Also, some of the links in the article are broken.

    I’ve been banging my head against the wall trying to get this to work in a self hosted WCF REST based service so any help would be appreciated.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s