In my last post, I described the identity architecture of ASP.NET Web API. The short
version was, that Web API (beta 1) does not really have an authentication system on
its own, but inherits the client security context from its host. This is fine in many
situations (e.g. AJAX style callbacks with an already established logon session).
But there are many cases where you don’t use the containing web application for authentication,
but need to do it yourself. Examples of that would be token based authentication and
clients that don’t run in the context of the web application (e.g. desktop clients
/ mobile).
Since Web API provides a nice extensibility model, it is easy to implement whatever
security framework you want on top of it. My design goals were:
Easy to use.
Extensible.
Claims-based.
..and of course, this should always behave the same, regardless of the hosting environment.
In the rest of the post I am outlining some of the bits and pieces, So you know what
you are dealing with, in case you want to try the code.
At the very heart…
is a so called message handler. This is a Web API extensibility point that gets to
see (and modify if needed) all incoming and outgoing requests. Handlers run after
the conversion from host to Web API, which means that handler code deals with HttpRequestMessage and HttpResponseMessage.
See Pedro’s post for
more information on the processing pipeline.
This handler requires a configuration object for initialization. Currently this is
very simple, it contains:
Settings for the various authentication and credential types
Settings for claims transformation
Ability to block identity inheritance from host
The most important part here is the credential type support, but I will come back
to that later.
The logic of the message handler is simple:
Look at the incoming request.
If the request contains an authorization header, try to authenticate the client.
If this is successful, create a claims principal and populate the usual places.
If not, return a 401 status code and set the Www-Authenticate header.
Look at outgoing response, if the status code is 401, set the Www-Authenticate header.
Credential type support
Under the covers I use the WIF security token handler infrastructure to validate
credentials and to turn security tokens into claims. The idea is simple: an authorization
header consists of two pieces: the schema and the actual “token”. My configuration
object allows to associate a security token handler with a scheme. This way you only
need to implement support for a specific credential type, and map that to the incoming
scheme value. The current version supports HTTP Basic Authentication as well as SAML
and SWT tokens.
(I needed to do some surgery on the standard security token handlers, since WIF does
not directly support string-ified tokens. The next version of .NET will fix that,
and the code should become simpler then).
You can e.g. use this code to hook up a username/password handler to the Basic scheme
(the default scheme name for Basic Authentication).
config.Handler.AddBasicAuthenticationHandler(
(username, password) => username == password);
You simply have to provide a password validation function which could of course point
back to your existing password library or e.g. membership.
The following code maps a token handler for Simple Web Tokens (SWT) to the Bearer scheme
(the currently favoured scheme name for OAuth2). You simply have to specify the issuer
name, realm and shared signature key:
config.Handler.AddSimpleWebTokenHandler(
"Bearer",
http://identity.thinktecture.com/trust,
Constants.Realm,
"Dc9Mpi3jaaaUpBQpa/4R7XtUsa3D/ALSjTVvK8IUZbg=");
For certain integration scenarios it is very useful if your Web API can consume SAML
tokens. This is also easily accomplishable. The following code uses the standard WIF
API to configure the usual SAMLisms like issuer, audience, service certificate and
certificate validation. Both SAML 1.1 and 2.0 are supported.
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(Constants.Realm));
adfsConfig.IssuerNameRegistry = registry;
adfsConfig.CertificateValidator = X509CertificateValidator.None;
// token decryption (read from
configuration section)
adfsConfig.ServiceTokenResolver =
FederatedAuthentication.ServiceConfiguration.CreateAggregateTokenResolver();
config.Handler.AddSaml11SecurityTokenHandler("SAML",
adfsConfig);
Claims Transformation
After successful authentication, if configured, the standard WIF ClaimsAuthenticationManager is
called to run claims transformation and validation logic. This stage is used to transform
the “technical” claims from the security token into application claims. You can either
have a separate transformation logic, or share on e.g. with the containing web application.
That’s just a matter of configuration.
Adding the authentication handler to a Web API application
In the spirit of Web API this is done in code, e.g. global.asax for web hosting:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ConfigureApis(GlobalConfiguration.Configuration);
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
}
private void ConfigureApis(HttpConfiguration configuration)
{
configuration.MessageHandlers.Add(
new AuthenticationHandler(ConfigureAuthentication()));
}
private AuthenticationConfiguration ConfigureAuthentication()
{
var config
= new AuthenticationConfiguration
{
//
sample claims transformation for consultants sample, comment out to see raw claims
ClaimsAuthenticationManager
= new ApiClaimsTransformer(),
//
value of the www-authenticate header,
// if not set, the first scheme added to the handler collection is used
DefaultAuthenticationScheme
= "Basic"
};
// add token
handlers - see above
return config;
}
You can find the full source code and some samples here.
In the next post I will describe some of the samples in the download, and then move
on to authorization.
HTH