Be Sociable, Share!

Tweet



Email

WhatsApp



Recently I’ve received lot of comments and emails asking how we can decouple the OWIN Authorization Server we’ve built in the previous posts from the resources we are protecting. If you are following the posts mentioned below you will notice that we’ve only one software component (API) which plays both roles: Authorization Server and Resource Server.

There is nothing wrong with the the current implementation because OAuth 2.0 specifications never forces the separation between the Authorization Server and the Resource Server, but in many cases and especially if you have huge resources and huge number of endpoints you want to secure; then it is better from architecture stand point to decouple the servers. So you will end up having a stand alone Authorization Server, and another stand alone Resource Server which contains all protected resources, as well this Resource Server will understand only the access tokens issued from the Authorization Server.

You can check the source code on Github.

Update Oct-27 New post: better approach to Decouple Authorization Server from Resource Server by using JSON Web Tokens (JWT).

OAuth 2.0 Roles: Authorization Server, Resource Server, Client, and Resource Owner

Before we jump into implementation and how we can decouple the servers I would like to emphasis on OAuth 2.0 roles defined in the specifications; so we’ll have better understanding of the responsibility of each role, by looking at the image below you will notice that there are 4 roles involved:

Resource Owner:

The entity or person (user) that owns the protected Resource.

Resource Server:

The server which hosts the protected resources, this server should be able to accept the access tokens issued by the Authorization Server and respond with the protected resource if the the access token is valid.

Client Applications:

The application or the (software) requesting access to the protected resources on the Resource Server, this client (on some OAuth flows) can request access token on behalf of the Resource Owner.

Authorization Server:

The server which is responsible for managing authorizations and issuing access tokens to the clients after validating the Resource Owner identity.

Note: In our architecture we can have single Authorization Server which is responsible to issue access token which can be consumed by multiple Resource Servers.

Decoupling OAuth 2.0 OWIN Authorization Server from Resource Server

In the previous posts we’ve built the Authorization Server and the Resource Server using a single software component (API), this API can be accessed on (http://ngauthenticationAPI.azurewebsites.net), in this demo I will guide you on how to create new standalone Resource API and host it on different machine other than the Authorization Server machine, then configure the Resource API to accept only access tokens issued from our Authorization Server. This new Resource Server is hosted on Azure can be accessed on (http://ngauthenticationResourcesAPI.azurewebsites.net).

Note: As you noticed from the previous posts we’ve built a protected resource (OrdersController) which can be accessed by issuing HTTP GET to the end point(http://ngauthenticationAPI.azurewebsites.net/api/orders), this end point is considered as a resource and should be moved to the Resource Server we’ll build now, but for the sake of keeping all the posts on this series functioning correctly; I’ll keep this end point in our Authorization Server, so please consider the API we’ve built previously as our standalone Authorization Server even it has a protected resource in it.

Steps to build the Resource Server

In the steps below, we’ll build another Web API which will contain our protected resources, for the sake of keeping it simple, I’ll add only one secured end point named “ProtectedController”, any authorized request to this end point should contain valid bearer token issued from our Authorization Server, so let’s start implementing this:

Step 1: Creating the Resource Web API Project

We need to add new ASP.NET Web application named “AngularJSAuthentication.ResourceServer” to our existing solution named “AngularJSAuthentication”, the selected template for the new project will be “Empty” template with no core dependencies, check the image below:

Step 2: Install the needed NuGet Packages

This project is empty so we need to install the NuGet packages needed to setup our OWIN Resource Server, configure ASP.NET Web API to be hosted within an OWIN server, and configure it to only uses OAuth 2.0 bearer tokens as authorization middle ware; so open NuGet Package Manager Console and install the below packages:

Install-Package Microsoft.AspNet.WebApi -Version 5.2.2 Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.2.2 Install-Package Microsoft.Owin.Host.SystemWeb -Version 3.0.0 Install-Package Microsoft.Owin.Cors -Version 3.0.0 Install-Package Microsoft.Owin.Security.OAuth -Version 2.1.0 1 2 3 4 5 Install-Package Microsoft . AspNet . WebApi -Version 5 . 2 . 2 Install-Package Microsoft . AspNet . WebApi . Owin -Version 5 . 2 . 2 Install-Package Microsoft . Owin . Host . SystemWeb -Version 3 . 0 . 0 Install-Package Microsoft . Owin . Cors -Version 3 . 0 . 0 Install-Package Microsoft . Owin . Security . OAuth -Version 2 . 1 . 0

The usage for each package has been covered on the previews posts, feel free to check this post to know the rational of using each package is used for.

Important note: In the initial post I was using package “Microsoft.Owin.Security.OAuth” version “3.0.0” which differs from the version used in the Authorization Server version “2.1.0”, there was a bug in my solution when I was using 2 different versions, basically the bug happens when we send an expired token to the Resource Server, the result for this that Resource Server accepts this token even if it is expired. I’ve noticed that the properties for the Authentication ticket are null and there is no expiry date. To fix this issue we need to unify the assembly version “Microsoft.Owin.Security.OAuth” between the Authorization Server and the Resource Server, I believe there is breaking changes between those 2 versions that’s why ticket properties are not de-crypted and de-serialized correctly. Thanks for Ashish Verma for notifying me about this.

Step 3: Add OWIN “Startup” Class

Now we want to add new class named “Startup”. It will contain the code below:

[assembly: OwinStartup(typeof(AngularJSAuthentication.ResourceServer.Startup))] namespace AngularJSAuthentication.ResourceServer { public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); ConfigureOAuth(app); WebApiConfig.Register(config); app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); } private void ConfigureOAuth(IAppBuilder app) { //Token Consumption app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions { }); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [ assembly : OwinStartup ( typeof ( AngularJSAuthentication . ResourceServer . Startup ) ) ] namespace AngularJSAuthentication . ResourceServer { public class Startup { public void Configuration ( IAppBuilder app ) { HttpConfiguration config = new HttpConfiguration ( ) ; ConfigureOAuth ( app ) ; WebApiConfig . Register ( config ) ; app . UseCors ( Microsoft . Owin . Cors . CorsOptions . AllowAll ) ; app . UseWebApi ( config ) ; } private void ConfigureOAuth ( IAppBuilder app ) { //Token Consumption app . UseOAuthBearerAuthentication ( new OAuthBearerAuthenticationOptions { } ) ; } } }

As we’ve covered in previous posts, this class will be fired once our Resource Server starts, as you noticed I’ve enabled CORS so this resource server can accept XHR requests coming from any origin.

What worth noting here is the implementation for method “ConfigureOAuth”, inside this method we are configuring the Resource Server to accept (consume only) tokens with bearer scheme, if you forget this one; then your Resource Server won’t understand the bearer tokens sent to it.

Step 3: Add “WebApiConfig” Class

As usual we need to add the WebApiConfig class under folder “App_Start” which contains the code below:

public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API routes config.MapHttpAttributeRoutes(); var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First(); jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } } 1 2 3 4 5 6 7 8 9 10 11 public static class WebApiConfig { public static void Register ( HttpConfiguration config ) { // Web API routes config . MapHttpAttributeRoutes ( ) ; var jsonFormatter = config . Formatters . OfType < JsonMediaTypeFormatter > ( ) . First ( ) ; jsonFormatter . SerializerSettings . ContractResolver = new CamelCasePropertyNamesContractResolver ( ) ; } }

This class is responsible to enable routing attributes on our controller.

Step 4: Add new protected (secured) controller

Now we want to add a controller which will serve as our protected resource, this controller will return list of claims for the authorized user, those claims for sure are encoded within the access token we’ve obtained from the Authorization Server. So add new controller named “ProtectedController” under “Controllers” folder and paste the code below:

[Authorize] [RoutePrefix("api/protected")] public class ProtectedController : ApiController { [Route("")] public IEnumerable<object> Get() { var identity = User.Identity as ClaimsIdentity; return identity.Claims.Select(c => new { Type = c.Type, Value = c.Value }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [ Authorize ] [ RoutePrefix ( "api/protected" ) ] public class ProtectedController : ApiController { [ Route ( "" ) ] public IEnumerable < object > Get ( ) { var identity = User . Identity as ClaimsIdentity ; return identity . Claims . Select ( c = > new { Type = c . Type , Value = c . Value } ) ; } }

Notice how we attribute the controller with [Authorize] attribute which will protect this resource and only will allow HTTP GET requests containing a valid bearer access token sent in the authorization header to pass, in more details when the request is received the OAuth bearer authentication middle ware will perform the following:

Extracting the access token which is sent in the “Authorization header” with the “Bearer” scheme. Extracting the authentication ticket from access token, this ticket will contain claims identity and any additional authentication properties. Checking the validity period of the authentication ticket.

Step 5: Hosting the Authorization Server and Resource Server on different machines

Until this step and if you are developing locally on your dev machine, if you tried to obtain an access token from your Authorization Server i.e. (http://AuthServer/token) and then send this access token to the secured end point in our Resource Server i.e. (http://ResServer/api/protected) the result for this request will pass and you will have access to the protected resource, how is this happening?

One your dev machine once you request an access token from your Authorization Server; the OAuth middleware will use the default data protection provider in your Authorization Server, so it will use the “validationKey” value in machineKey node stored in machine.config file to issue the access token and protect it. The same case applies when you send the access token to your Resource Server, it will use the same machineKey to decrypt the access token and extract the authentication ticket from it.

The same applies if you are planing on your production environment to host your Authorization Server and your Resource Server on the same machine.

But in our case I’m hosting my Authorization Server on Azure website using West-US region, and my Resource Server is hosted on Azure websites on East-US region so both are totally on different machines and they share different machine.config files. For sure I can’t change anything on machine.config file because it is used by different sites on the Azure VMs I’m hosting my APIs on it.

So to solve this case we need to override the machineKey node for both APIs (Authorization Server and Resource Server) and share the same machineKey between both web.config files.

To do so we need to generate a new machineKey using this online tool, the result after using the tool will be as the below: As advised by Barry Dorrans it is not recommend to use an online tool to generate your machineKey because you do not know if this online tool will store the value for your machineKey in some secret database, it is better to generate the machineKey by your self.

To do so I’ll follow the steps mentioned in article KB 2915218, Appendix A which uses Power Shell commands to generate machineKey, so after you open Power Shell and run the right command as the image below you will receive new machineKey



<machineKey validationKey="A970D0E3C36AA17C43C5DB225C778B3392BAED4D7089C6AAF76E3D4243E64FD797BD17611868E85D2E4E1C8B6F1FB684B0C8DBA0C39E20284B7FCA73E0927B20" decryptionKey="88274072DD5AC1FB6CED8281B34CDC6E79DD7223243A527D46C09CF6CA58DB68" validation="SHA1" decryption="AES" /> 1 <machineKey validationKey = "A970D0E3C36AA17C43C5DB225C778B3392BAED4D7089C6AAF76E3D4243E64FD797BD17611868E85D2E4E1C8B6F1FB684B0C8DBA0C39E20284B7FCA73E0927B20" decryptionKey = "88274072DD5AC1FB6CED8281B34CDC6E79DD7223243A527D46C09CF6CA58DB68" validation = "SHA1" decryption = "AES" />

Note: Do not use those for production and generate a new machineKey for your Servers using Power Shell.

After you generate it open the web.config file for both the Authorization Server (AngularJSAuthentication.API project) and for the Resource Server (AngularJSAuthentication.ResourceServer) and paste the machineKey node inside the <system.web> node as the below:

<system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <machineKey validationKey="VALUE GOES HERE" decryptionKey="VALUE GOES HERE" validation="SHA1" decryption="AES"/> </system.web> 1 2 3 4 5 6 7 8 <system.web> <compilation debug = "true" targetFramework = "4.5" /> <httpRuntime targetFramework = "4.5" /> <machineKey validationKey = "VALUE GOES HERE" decryptionKey = "VALUE GOES HERE" validation = "SHA1" decryption = "AES" /> </system.web>

By doing this we’ve unified the machineKey for both Authorization Server and Resource Server (Only this API in case of Azure shared hosting or any other shared host) and we are ready now to test our implementation.

Step 6: Testing the Authorization Server and Resource Server

Now we need to obtain an access token from our Authorization Server as we did before in the previous posts, so we can issue HTTP POST to the end point (http://ngauthenticationapi.azurewebsites.net/token) including the resource owner username and password as the image below:

If the request succeeded we’ll receive an access token, then we’ll use this access token to send HTTP GET request to our new Resource Server using the protected end point (http://ngauthenticationresourcesapi.azurewebsites.net/api/protected), for sure we need to send the access token in the Authorization header using bearer scheme, the request will be as the image below:

As you notice we’re now able to access the protected resource in our new Resource Server and the claims for this identity which obtained from the Authorization Server are extracted.

That’s it for now folks, hopefully this short walk through helped in understanding how we can decouple the Authorization Server from the Resource Server.

If you have any comment or question please drop me a comment

You can use the following link to check the demo application.

You can use the following link to test the Authorization Server (http://ngauthenticationAPI.azurewebsites.net),

You can use the following link to test the Resource Server (http://ngauthenticationResourcesAPI.azurewebsites.net)

You can check the source code on Github.

Follow me on Twitter @tjoudeh

References