skip to Main Content

We have built a microservices application which consists of 3 main services. We needed to have an Api Gateway for routing and mainly for authentication and authorizations purposes as 2 of the 3 services need to have an authenticated user to make the request. The routing works fine but when I try to add the authentication and test it using postman it fails to send the request with a response 401 Unauthorized. Here is what I have done so far:

Ocelot.json

{
"Routes": [
{
  "DownstreamPathTemplate": "/api/courses/{everything}",
  "DownstreamScheme": "https",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 7123
    }
  ],
  "UpstreamPathTemplate": "/api/courses/{everything}",
  "UpstreamHttpMethod": [ "POST", "PUT", "GET", "DELETE" ],
  "AuthenticationOptions": {
    "AuthenticationProviderKey": "Bearer",
    "AllowedScopes": []
  }
},
{
  "DownstreamPathTemplate": "/api/users/{everything}",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 8080
    }
  ],
  "UpstreamPathTemplate": "/api/users/{everything}",
  "UpstreamHttpMethod": [ "POST", "PUT", "GET", "DELETE" ]
},
{
  "DownstreamPathTemplate": "/api/exam/{everything}",
  "DownstreamScheme": "http",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 8888
    }
  ],
  "UpstreamPathTemplate": "/api/exam/{everything}",
  "UpstreamHttpMethod": [ "POST", "PUT", "GET", "DELETE" ],
  "AuthenticationOptions": {
    "AuthenticationProviderKey": "Bearer",
    "AllowedScopes": []
  }
}
],
"GlobalConfiguration": {}
}

I thought the service that generates the token could be the problem, so I have generated an online token but still the same issue

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NTcwMjg2MTcsImV4cCI6MTY1NzQ2MDYxNywic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSJ9.nLAHN2vlpwd4seqCaxuqpBNgYuEeyKUmfoLW0CFsHTI

Program.cs File

using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using  Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("Ocelot.dev.json");

builder.Services.AddEndpointsApiExplorer();

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
{
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("AUTH_SECRET_KEY", EnvironmentVariableTarget.Process)!)),
    ValidateIssuerSigningKey = true,
    ValidateIssuer = false,
    ValidateAudience = false,
};
});

builder.Services.AddOcelot();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseOcelot().Wait();

app.UseAuthorization();

app.Run();

Secret key im using is "secret"

Error message from console:

warn: Ocelot.Authentication.Middleware.AuthenticationMiddleware[0]
      requestId: 0HMIUJ2BDCV3D:00000002, previousRequestId: no previous request id, message: Client has NOT been authenticated for /api/courses/create and pipeline error set. Request for
 authenticated route /api/courses/create by  was unauthenticated
warn: Ocelot.Responder.Middleware.ResponderMiddleware[0]
      requestId: 0HMIUJ2BDCV3D:00000002, previousRequestId: no previous request id, message: Error Code: UnauthenticatedError Message: Request for authenticated route /api/courses/create
 by  was unauthenticated errors found in ResponderMiddleware. Setting error response for request path:/api/courses/create, request method: POST

Can anyone spot the mistake i’m making?

2

Answers


  1. Try this, in startup

     var configuration = new OcelotPipelineConfiguration
            {
                AuthenticationMiddleware = async (cpt, est) =>
                {
                    await est.Invoke();
                }
            };
            app.UseOcelot(configuration).Wait();
    
    Login or Signup to reply.
  2. I had the same problem, to solve it you need to ensure that your JWT signature is valid, review the way how you are creating the JWT signature and then change the SignatureValidator.

    Method to create token:

    public async Task<UserToken> BuildTokenAsync(ApplicationUser userInfo)
        {
            var identity = await GenerateClaimsAsync(userInfo);
    
            var tokenHandler = new JwtSecurityTokenHandler();
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = identity,
                Expires = DateTime.UtcNow.AddHours(2),
                SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
    
            return new UserToken()
            {
                Token = tokenHandler.WriteToken(token),
                Expiration = tokenDescriptor.Expires.Value
            };
        }
    

    On Ocelot now you can configure the Authentication schema and signature validation.

    {
    "Routes": [{
        "DownstreamPathTemplate": "/api/v{version}/",
        "AuthenticationOptions": {
            "AuthenticationProviderKey": "BearerAuth",
            "AllowedScopes": ["Seller"]
    
        },
        "UpstreamPathTemplate": "/v{version}/",
        "UpstreamHttpMethod": ["Get"]
    }]}
    

    ConfigureService:

    private static void ConfigureServices(IConfiguration configuration, IServiceCollection services)
    {
        services.AddOcelot(configuration);
    
        var JWT_KEY = Environment.GetEnvironmentVariable("JWT_KEY");
    
        services.AddAuthentication()
                .AddJwtBearer("BearerAuth", options =>
                {
                    options.SaveToken = true;
                    options.RequireHttpsMetadata = false;
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        ValidateAudience = false,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = false,
                        SignatureValidator = (token, parameters) =>
                        {
                            var jwt = new JwtSecurityToken(token);
                            
                            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JWT_KEY));
                            var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
    
    
                            if(signingCredentials.Key is SymmetricSecurityKey signKey)
                            {
                                var encodedData = jwt.EncodedHeader + "." + jwt.EncodedPayload;
                                var compiledSignature = Encode(encodedData, signKey.Key);
    
                                if (compiledSignature == jwt.RawSignature)
                                    return jwt;
                            }
    
                            throw new Exception("Token signature validation failed.");
                        },
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JWT_KEY)),
                        ClockSkew = TimeSpan.Zero
                    };
                });
    }
    
    public static string Encode(string input, byte[] key)
    {
        HMACSHA256 myhmacsha = new HMACSHA256(key);
        byte[] byteArray = Encoding.UTF8.GetBytes(input);
        MemoryStream stream = new MemoryStream(byteArray);
        byte[] hashValue = myhmacsha.ComputeHash(stream);
        return Base64UrlEncoder.Encode(hashValue);
    } 
    

    And finally the Configuration method:

    private static async void Configure(WebApplication app)
    {
    
        // Configure the HTTP request pipeline.
        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            //app.UseHsts();
        }
    
        app.UseStaticFiles();
    
        app.UseAuthentication();
    
        await app.UseOcelot();
    
    
        app.Run();
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search