Part Two - Building API Gateway Using Ocelot In ASP.NET Core - Authentication

In this article, I will continue with the topic of Building API Gateway In ASP.NET Core and will show you something about authentication later.

Introduction

In my last article, Building API Gateway Using Ocelot In ASP.NET Core, I introduced how we can use Ocelot to build our API Gateway with the simplest demo. In this article, I will continue with the topic of Building API Gateway In ASP.NET Core and will show you something about authentication later.

As all we know, API services are protected resources. We should secure them as far as we can!

Normally, we will follow Security to handle the security of our projects. This repository contains the security and authorization middleware for ASP.NET Core.  It always makes things easier.

Do we need to make a big change when we import API Gateway into our project? The answer is - No! We only make some imperceptible changes. It means that everything will be easy.

Let's look at the below screenshot first.

ASP.NET Core

This screenshot shows that when we want to access our services, the authentication module of API Gateway should be visited at first. We need to visit the auth service to get the access token at first so that we can visit the protected services with the access_token.

Maybe some of you will put the auth service alone,= which means that the clients should visit auth service to get access_token straight at first, then send a request to API Gateway to do something later. There is no doubt that thing will be all right but I recommend that you put your auth service together with other services.

API Gateway is the entry of all our services; we just make auth service can be accessed by the clients without authentication.

OK, let's begin to do it.

I will use something the same as last article's demo here. I think it may help you to understand more quickly.

Step 1

Create four projects here.

Project NameProject TypeDescription
APIGatewayASP.NET Core Emptyentry of this demo
CustomersAPIServicesASP.NET Core Web APIAPI Service that handles something about customers
AuthServerASP.NET Core Web APIAPI Service that handles something about auth
ClientAppConsole AppThis is a Console App that simulates client.

Make APIGateway and CustomerAPIServices project the same as in the last demo. You can find them in  APIGatewayBasicDemo.

Step 2

Finish AuthServer. This service is mainly for generating access_token for the request. What we need to do is add a method to handle this.

  1. [HttpGet]  
  2. public IActionResult Get(string name, string pwd)  
  3. {  
  4.     //just hard code here.  
  5.     if (name == "catcher" && pwd == "123")  
  6.     {  
  7.         var now = DateTime.UtcNow;  
  8.   
  9.         var claims = new Claim[]  
  10.         {  
  11.             new Claim(JwtRegisteredClaimNames.Sub, name),  
  12.             new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),  
  13.             new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)  
  14.         };  
  15.   
  16.         var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret));  
  17.         var tokenValidationParameters = new TokenValidationParameters  
  18.         {  
  19.             ValidateIssuerSigningKey = true,  
  20.             IssuerSigningKey = signingKey,  
  21.             ValidateIssuer = true,  
  22.             ValidIssuer = _settings.Value.Iss,  
  23.             ValidateAudience = true,  
  24.             ValidAudience = _settings.Value.Aud,  
  25.             ValidateLifetime = true,  
  26.             ClockSkew = TimeSpan.Zero,  
  27.             RequireExpirationTime = true,  
  28.   
  29.         };  
  30.   
  31.         var jwt = new JwtSecurityToken(  
  32.             issuer: _settings.Value.Iss,  
  33.             audience: _settings.Value.Aud,  
  34.             claims: claims,  
  35.             notBefore: now,  
  36.             expires: now.Add(TimeSpan.FromMinutes(2)),  
  37.             signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)  
  38.         );  
  39.         var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);  
  40.         var responseJson = new  
  41.         {  
  42.             access_token = encodedJwt,  
  43.             expires_in = (int)TimeSpan.FromMinutes(2).TotalSeconds  
  44.         };  
  45.   
  46.         return Json(responseJson);  
  47.     }  
  48.     else  
  49.     {  
  50.         return Json("");  
  51.     }  
  52. }  

When verifying the users, I just use the hard code here because it's not an important thing to implement it in this article's topic!

We have finished auth service. We can run it up now.

ASP.NET Core

Step 3

Turn to CustomerAPIServices project, we should secure this service!!

Edit the Startup class so that we can enable the authentication. I use JwtBearer here and I set the default authentication scheme to TestKey which I will mention in the next step.

  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     var audienceConfig = Configuration.GetSection("Audience");  
  4.   
  5.     var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));  
  6.     var tokenValidationParameters = new TokenValidationParameters  
  7.     {  
  8.         ValidateIssuerSigningKey = true,  
  9.         IssuerSigningKey = signingKey,  
  10.         ValidateIssuer = true,  
  11.         ValidIssuer = audienceConfig["Iss"],  
  12.         ValidateAudience = true,  
  13.         ValidAudience = audienceConfig["Aud"],  
  14.         ValidateLifetime = true,  
  15.         ClockSkew = TimeSpan.Zero,  
  16.         RequireExpirationTime = true,  
  17.     };  
  18.   
  19.     services.AddAuthentication()  
  20.             .AddJwtBearer("TestKey", x =>  
  21.              {  
  22.                  x.RequireHttpsMetadata = false;  
  23.                  x.TokenValidationParameters = tokenValidationParameters;  
  24.              });  
  25.   
  26.     services.AddMvc();  
  27. }  
  28. public void Configure(IApplicationBuilder app)  
  29. {              
  30.     app.UseAuthentication();  
  31.     app.UseMvc();  
  32. }  

And then, we should use Authorize attribute in our method.

  1. [Authorize]  
  2. [HttpGet]  
  3. public IEnumerable<string> Get()  
  4. {  
  5.     return new string[] { "Catcher Wong""James Li" };  
  6. }  

Here, I just authorize of them because we need to show the not protected resources for comparison.

Note

This project is based on ASP.NET Core 2.0. If you are working with 1.x, you may find some difference here. You can follow Migrating Authentication and Identity to ASP.NET Core 2.0 to migrate.

Run it up too!

ASP.NET Core

Step 4

The most important step is now arriving. Configure the authentication in your API Gateway.

  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     var audienceConfig = Configuration.GetSection("Audience");  
  4.   
  5.     var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));  
  6.     var tokenValidationParameters = new TokenValidationParameters  
  7.     {  
  8.         ValidateIssuerSigningKey = true,  
  9.         IssuerSigningKey = signingKey,  
  10.         ValidateIssuer = true,  
  11.         ValidIssuer = audienceConfig["Iss"],  
  12.         ValidateAudience = true,  
  13.         ValidAudience = audienceConfig["Aud"],  
  14.         ValidateLifetime = true,  
  15.         ClockSkew = TimeSpan.Zero,  
  16.         RequireExpirationTime = true,  
  17.     };  
  18.   
  19.     services.AddAuthentication()  
  20.             .AddJwtBearer("TestKey", x =>  
  21.              {  
  22.                  x.RequireHttpsMetadata = false;  
  23.                  x.TokenValidationParameters = tokenValidationParameters;  
  24.              });  
  25.   
  26.     services.AddOcelot(Configuration);  
  27. }  

You will find that most of the code is the same as Customer Service.

When Ocelot runs, it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey and check that there is an Authentication Provider registered with the given key. If there isn't, then Ocelot will not start up. If there is, then the ReRoute will use that provider when it executes.

So, we also should modify the configuration.json file. We should add a new node named AuthenticationOptions and make the AuthenticationProviderKey the same as what we defined in the Startup class.

  1. {  
  2.     "DownstreamPathTemplate""/api/customers",  
  3.     "DownstreamScheme""http",  
  4.     "DownstreamHost""localhost",  
  5.     "DownstreamPort": 9001,  
  6.     "UpstreamPathTemplate""/customers",  
  7.     "UpstreamHttpMethod": [ "Get" ],  
  8.     "AuthenticationOptions": {  
  9.         "AuthenticationProviderKey""TestKey",  
  10.         "AllowedScopes": []  
  11.     }  
  12. }  

We also run it up now.

ASP.NET Core

Note
If you meet the following error when you run this project, you need to check your code whether the AddJwtBearer method specifies the authentication scheme.

Unhandled Exception: System.InvalidOperationException: Scheme already exists: Bearer

Step 5

We have done all preparation work here. Now, we should use our client app to simulate some request of the API Gateway.

First of all, we need to add a method to get the assess_token.

  1. private static string GetJwt()  
  2. {  
  3.     HttpClient client = new HttpClient();  
  4.     client.BaseAddress = new Uri( "http://localhost:9000");  
  5.     client.DefaultRequestHeaders.Clear();  
  6.     var res2 = client.GetAsync("/api/auth?name=catcher&pwd=123").Result;  
  7.     dynamic jwt = JsonConvert.DeserializeObject(res2.Content.ReadAsStringAsync().Result);  
  8.     return jwt.access_token;  
  9. }  

Then, write some code to visit the customer services via API Gateway.

  1. static void Main(string[] args)  
  2. {  
  3.     HttpClient client = new HttpClient();  
  4.       
  5.     client.DefaultRequestHeaders.Clear();  
  6.     client.BaseAddress = new Uri("http://localhost:9000");  
  7.       
  8.     // 1. without access_token will not access the service  
  9.     //    and return 401 .  
  10.     var resWithoutToken = client.GetAsync("/customers").Result;  
  11.       
  12.     //print something here   
  13.       
  14.     //2. with access_token will access the service  
  15.     //   and return result.  
  16.     client.DefaultRequestHeaders.Clear();  
  17.     var jwt = GetJwt();  
  18.       
  19.     client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");  
  20.     var resWithToken = client.GetAsync("/customers").Result;  
  21.       
  22.     //print something here   
  23.       
  24.     //3. visit no auth service   
  25.     client.DefaultRequestHeaders.Clear();  
  26.     var res = client.GetAsync("/customers/1").Result;  
  27.       
  28.     //print something here   
  29.       
  30.     Console.Read();  
  31. }  

When you run this project, you may find the results as follows.

ASP.NET Core

So far, we have done the job.

Here is the source code you can find on my GitHub page.

Summary

This article introduced how to configure authentication when you use Ocelot as your API Gateway. Hope this will help you.

Read next part here >>