Search code examples
c#apijwtmicroservicesocelot

Bypassing Ocelot API Gateway


I have an API Gateway, in this instance is called Gateway.Api. Within the Startup class I have the following:

public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOcelot(Configuration);
        services.AddMvc();

        var appSettingSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(appSettingSection);

        var appSettings = appSettingSection.Get<AppSettings>();
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAuthentication();
        app.UseOcelot().Wait();
        app.UseMvc();
    }

As you can see it defines the authentication scheme.

Using Ocelot I have the following configuration file for my Gateway.Api:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/customer",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 50366
        }
      ],
      "UpstreamPathTemplate": "/api/customer",
      "UpstreamHttpMethod": [ "Get" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Bearer",
        "AllowedScopes": []
      }
    },
    {
      "DownstreamPathTemplate": "/api/user/authenticate",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 50353
        }
      ],
      "UpstreamPathTemplate": "/api/user/authenticate",
      "UpstreamHttpMethod": [ "Post" ]
    }
  ],
  "GlobalConfiguration": {
    "UseServiceDiscovery": false
  }
}

When I try to access http://localhost:50333/api/customer (Gateway.Api has a port of 50333) without a token I get a 401 response which proves that the configuration file as well as the authentication works.

As well as the Customer microservice, I also have an Identity microservice which allows users to authenticate using a valid username and password which will then issue a token. Using this token to then call the Customer service I get a successful response (200 OK).

Now for some reason, if I access the Customer service directly without using the Gateway (so http://localhost:50366/api/customer) I am able to get a successful response without a token.

Below is the Customer microservice:

[Route("api/[controller]")]
public class CustomerController : Controller
{
    [HttpGet]
    public IEnumerable<string> Get()
    {
        var customers = new string[] {
            "test",
            "test"
        };

        return customers;
    }
}

Does this mean that I have to add an authentication scheme to every microservice Startup class? If so, isn't this overkill?

What I did try is using a [Authorize] attribute on the actions within the Customer microservice however this throws an exception that their is no default authentication scheme.


Solution

  • This is development environment so you can access the url directly . Your Customer service have no idea of gateway. In actual production environment you generally will only expose API gateway and rest of the services sit behind firewall (private subnet). Only API gateway has access to them. The only way to access the services is to pass through the gateway. If however you want to expose the services publicly then individual service authentication is must.

    Regardless It's always good idea to add authentication to services that you want to secure.