The Web API v2 Individual Accounts template shows off some quite popular application scenarios, e.g. username/password authentication with local accounts (including create account, change/set password) as well as third party authentication using Facebook, Google etc – including linking those external accounts with local ones. All that is done using an OAuth2 authorization server – using both the resource owner password flow for local accounts and the implicit flow for external accounts.
To make all that happen the template combines quite a bit of new stuff together: OWIN, Katana authentication middleware, ASP.NET identity, OAuth2 and a bunch of new authentication related attributes…and I must admit figuring out exactly what’s going on was a bit of a challenge. Two quotes constantly came to mind while digging through the source code and writing down my notes. One was: complexity is the natural enemy of security – and the other one was: shit’s hard. So enjoy.
In this post I want to focus on the general setup of the Katana authentication middleware, the following posts will deal with the local account features and the external authentication.
In Katana, every authentication middleware “registers” itself with the system. For that it needs a “name” – or technically speaking an AuthenticationType. Using that name, some code like a framework can call into the authentication component. This is done using the IAuthenticationManager interface which hangs off the Authentication property on the OwinContext. It features methods like SignIn, SignOut, AuthenticateAsync or Challenge. Each of these methods require an AuthenticationType as a hint which middleware will do the actual work.
One built-in mechanism that uses the authentication manager is the new HostAuthenticationFilter in Web API v2 – will come to that later. Let’s first have a look which authentication middleware gets actually wired up (see also Startup.Auth.cs).
For the implicit flow and the interaction with Google and friends, “browser tech” is needed (think web views in native apps, or the browser itself for JS) – this is where cookies come in:
This call adds supports for classic cookie based authentication. The authentication type is simply called Cookies or in code the middleware is referenced using CookieAuthenticationDefaults.AuthenticationType.
The second cookie middleware registers itself as ExternalCookie (or DefaultAuthenticationTypes.ExternalCookie). This cookie is used to temporarily store information about a user logging in with a third party login provider
Further there is one authentication middleware registered for every external login provider you want to support (authentication types: Google, Facebook, Twitter and Microsoft):
OK – next up is all the plumbing to support token-based authentication – we need a token producer and consumer. This is all hidden behind the following line of code:
This extension method actually registers three middlewares behind the covers:
- OAuth2 authorization server to deal with resource owner flow and implicit flow token requests. Application specific logic is encapsulated in the ApplicationOAuthProvider class which we’ll have a closer look in the next post.
- Token-based authentication for local accounts using an authentication type of Bearer (or OAuthDefault.AuthenticationType). This middleware only accepts claims where the issuer has been set to LOCAL AUTHORITY.
- Token-based authentication for external accounts (resulting from an authentication handshake with an external login provider). It uses an authentication type of ExternalBearer (or DefaultAuthenticationTypes.ExternalBearer) and only accepts claims where the issuer is not LOCAL AUTHORITY (important technical detail – keep that in the back of your mind).
With that setup you can now control which authentication type is required to access which parts of the API surface – let me give you some examples:
In general Web API requires token-based authentication using local accounts (Bearer). This is why you find the following two lines of code in WebApiConfig.cs:
Let’s say you’d want to also accept tokens resulting from external authentication – but require an authenticated principal, the following would work (e.g. on a controller or action):
If you want to override the global setting and only accept an application cookie if present (a technique used in the account controller – more on that in the next post) – you could do this:
OK – now you know how the template is “configured” in terms of security. In the next posts we’ll have a look how all this works together to authenticate local accounts using username and password.