How to build middleware for next level authentication in Episerver
Episerver continuously improves all aspects of our platform. One area that definitely – and unfortunately – lacks attention, for native support, is the authentication part, which out of the box relies on the old ASP.NET Membership framework.
Let me be honest, I don’t like ASP.NET Membership. ASP.NET Identity and the OWIN specification, via Katana, is, in my humble opinion, the way to go regarding identity management and I am very pleased to see the countless examples of how OWIN and ASP.NET Identity is leveraged through the Episerver community.
In order to maximize your value of reading this post, I recommend you to have a fundamental understanding of the objective behind the OWIN specification and also some level of familiarity with the Katana implementation.
Let me start with a statement; Katana is for more than authentication against common identity providers such as Facebook. Individuals working on enterprise solutions tend to agree. They recognize the need for challenging an identity provider – e.g. via OAuth2.0 – that goes beyond what’s already provided by the community supporting the Katana project. Indeed, there are a multitude of options, but you will, at some point, be required to define a custom authentication middleware that’s tailored to the clients implementation of a recognized authentication protocols such as Auth2.0.
Create custom OAuth Middleware for an OWIN powered Episerver solution
Let’s start with a small recap of the OAuth2.0 protocol. It allows users to prove their identity, when using a web platform, without having to provide credentials. Users – e.g. site administrators – can use the the site without worrying about their credentials being compromised. At a very high level, the protocol is based on a authorization server (here after referred to as identity provider) issuing access tokens that is used to grant access to resources – such as profile information – via a resource server. What we are about to build is the authentication mechanism in the third party application recognized as the client.
That authentication mechanism is Middleware. Middleware are modules written to work with Katana. It’s executed at a low level, as part of the OWIN pipeline, and are used to respond to HTTP requests.
An example of Middleware is the code that provides authenticating against Facebook. Most people recognizes it via it’s registration:
Each authentication Middleware provides an abstraction for providning middleware options – it’s flexibility – and registration in the OWIN pipeline. The UseFacebookAuthentication extension is syntactic candy designed to provide an easy setup.
Let’s investigate what’s inside Use[YourAuthProvider]Authentication to prepare you for a customized implementation
The OAuth Process
Managing a challenge against a OAuth Identity Provider requires several interactions with the browser, the middleware and the identity provider. It’s all managed via HTTP requests and would, in the good old IE6 days (with refresh sounds), play a symphony of clicking sounds.
It mainly consists of these 5 steps:
- A HTTP request that requires authentication is caught by Middleware due to HTTP 401 response
- End users browser is redirected to identity provider
- User provides credentials or accepts the information claimed by client application
- Identity provider redirects back to client application and the incoming request is caught by Middleware
- The application builds an external Claims Identity that can be used to create an user profile and login
Creating a custom authentication mechanism, via Middleware, requires us to manage all the subprocesses in above steps.
The process begins when Episerver requires a user to authenticate – e.g. due to access control list or an MVC [Authorize] attribute. Our client application can, when authentication is required, redirect the user to a login page outlining the options for providing an identity.
When the user clicks on an option – e.g. “Sign in with Loyalty Account”, the client application will request a authentication challenge to the provider type “LoyaltyProgramIdp”, through the OwinContex, followed by a HTTP 401. At this point, the
ApplyResponseChallengeAsync method is invoked in the Middleware, with the matching provider type, and it can intercept the response to redirect to appropriate authorize endpoint.
Once the user has reached the identity provider, outside of our Episerver application, he/she will be prompted to provide credentials (as an example to provide his/her identity). When the user finishes, the identity provider will redirect the browser to the applications callback path that were detailed by the Middleware. The callback path is a reserved path, such as /signin-loyaltyprogramidp, and is a path used by the Middleware to identify a request coming back from a third party service. Please remember, that the middleware is invoked before Episerver routing occurs.
So, as soon as the request reaches the client application, the Middleware will, if the callback path is correct, intercept the request. Intercept happens because it has to build an user identity. Valuable information, being especially the authorization code (often referred to as code), has been forwarded with the request. Immediately after obtaining the authorization code, the Middleware will, via
AuthenticateAsync, contact the identity providers token-endpoint to request an access token.
When the Middleware is in possession of the access token, the resource server can be requested to retrieve necessary information about the user. A Claims Identity can be constructed as soon as the user’s information has been collected. This Claims Identity can, after a internal redirect to an application controller – e.g. /login/callback, be used to create an user profile and login.
Combine this with the inner parts defining a Middleware
Middleware consists of several parts that manages the sub processes outlined above. Below elements – mainly classes – are used to register the Middleware and handle all authentication requests.
Similar to the UseFacebookAuthentication, an authentication extensions is used to abstract the registration of Middleware. A static class named
AuthenticationExtensions would be the obvious for our example. It’s intended to extend the
IAppBuilder by providing a
Options details the flexbility in the Middleware. It exposes configuration options that affects the authentication process.
Options has to inherits from
AuthenticationOptions and often provides these basic configuration options:
- Authority: endpoint for identity provider that often varies across environments
- Scope: user information – e.g. email and profile – client requests access too
- Provider: provider enabling developer to intercept callback and authentication flows
- CallbackPath: reserved path that triggers Middleware authentication
Please be aware that the provider type, being “LoyaltyProgramIdp”, is set as part of the base class constructor.
LoyaltyProgramAuthenticationMiddleware is rather sparse. It mainly registers the the
LoyaltyProgramAuthenticationHandler is responsible for the largest part of the authentication process. It inherits from
and often requires three methods:
- ApplyResponseChallengeAsync: manages the redirect when client application requests a challenge against identity provider.
- InvokeAsync: manages the callback, by checking if path matches /signin-loyaltyprogramidp, and requests authentication
- AuthenticateCoreAsync: authenticates user based on authorization code issued by identity provider
AuthenticationProvider specifies delegates that are called during challenge and authentication:
- Authenticated: is invoked when a user is successfully authenticated and is often used to add additional claims.
- ReturnEndPoint: is invoked after the
ClaimsIdentityis constructed and before it’s signed in.
Understanding the processes and elements involved in authentication using Middleware is a prerequisite when building custom Middleware for any OAuth2.0 identity provider. Examples of a large variety of identity providers can be accessed via the OwinOAuthProviders Github project.