ASP.NET WebAPI Security 2: Identity Architecture
- by Your DisplayName here!
Pedro has beaten me to the
punch with a detailed post (and diagram) about the WebAPI hosting architecture. So
go read his post first,
then come back so we can have a closer look at what that means for security.
The first important takeaway is that WebAPI is hosting independent- currently
it ships with two host integration implementations – one for ASP.NET (aka web host)
and WCF (aka self host). Pedro nicely shows the integration into the web host. Self
hosting is not done yet so we will mainly focus on the web hosting case and I will
point out security related differences when they exist.
The interesting part for security (amongst other things of course) is the HttpControllerHandler (see
Pedro’s diagram) – this is where the host specific representation of an HTTP request
gets converted to the WebAPI abstraction (called HttpRequestMessage). The ConvertRequest method
does the following:
Create a new HttpRequestMessage.
Copy URI, method and headers from the HttpContext.
Copies HttpContext.User to the Properties<string, object> dictionary
on the HttpRequestMessage. The key used for that can be found on HttpPropertyKeys.UserPrincipalKey (which
resolves to “MS_UserPrincipal”).
So the consequence is that WebAPI receives whatever IPrincipal has been set
by the ASP.NET pipeline (in the web hosting case). Common questions are:
Are there situations where is property does not get set?
Not in ASP.NET – the DefaultAuthenticationModule in the HTTP pipeline
makes sure HttpContext.User (and Thread.CurrentPrincipal – more
on that later) are always set. Either to some authenticated user – or to an anonymous
principal. This may be different in other hosting environments (again more on that
later).
Why so generic?
Keep in mind that WebAPI is hosting independent and may run on a host that materializes
identity completely different compared to ASP.NET (or .NET in general). This gives
them a way to evolve the system in the future.
How does WebAPI code retrieve the current client identity?
HttpRequestMessage has an extension method called GetUserPrincipal() which
returns the property as an IPrincipal.
A quick look at self hosting shows that the moral equivalent of HttpControllerHandler.ConvertRequest() is HttpSelfHostServer.ProcessRequestContext().
Here the principal property gets only set when the host is configured for Windows
authentication (inconsisteny).
Do I like that?
Well – yes and no. Here are my thoughts:
I like that it is very straightforward to let WebAPI inherit the client identity context
of the host.
This might not always be what you want – think of an ASP.NET app that consists of
UI and APIs – the UI might use Forms authentication, the APIs token based authentication.
So it would be good if the two parts would live in a separate security world.
It makes total sense to have this generic hand off point for identity between the
host and WebAPI.
It also makes total sense for WebAPI plumbing code (especially handlers) to use the
WebAPI specific identity abstraction.
But – c’mon we are running on .NET. And the way .NET represents identity is via IPrincipal/IIdentity.
That’s what every .NET developer on this planet is used to. So I would like to see
a User property of type IPrincipal on ApiController.
I don’t like the fact that Thread.CurrentPrincipal is not populated. T.CP
is a well established pattern as a one stop shop to retrieve client identity on .NET.
That makes a lot of sense – even if the name is misleading at best.
There might be existing library code you want to call from WebAPI that makes use of
T.CP (e.g. PrincipalPermission, or a simple .Name or .IsInRole()).
Having the client identity as an ambient property is useful for code that does not
have access to the current HTTP request (for calling GetUserPrincipal()).
I don’t like the fact that that the client identity conversion from host to WebAPI
is inconsistent. This makes writing security plumbing code harder. I think the logic
should always be:
If the host has a client identity representation, copy it.
If not, set an anonymous principal on the request message.
Btw – please don’t annoy me with the “but T.CP is static, and static is bad for testing”
chant. T.CP is a getter/setter and, in fact I find it beneficial to be able to set
different security contexts in unit tests before calling in some logic. And, in case
you have wondered – T.CP is indeed thread static (and the name comes from a time where
a logical operation was bound to a thread – which is not true anymore). But all thread
creation APIs in .NET actually copy T.CP to the new thread they create. This is the
case since .NET 2.0 and is certainly an improvement compared to how Win32 does things.
So to sum it up:
The host plumbing copies the host client identity to WebAPI (this is not perfect yet,
but will surely be improved).
or in other words: The current WebAPI bits don’t ship with any authentication plumbing,
but solely use whatever authentication (and thus client identity) is set up by the
host.
WebAPI developers can retrieve the client identity from the HttpRequestMessage.
Hopefully my proposed changes around T.CP and the User property on ApiController will
be added.
In the next post, I will detail how to add WebAPI specific authentication support,
e.g. for Basic Authentication and tokens. This includes integrating the notion of
claims based identity. After that we will look at the built-in authorization bits
and how to improve them as well.
Stay tuned.