Ocelot: [HELPED] How to fordward claims from Ocelot to internal services

Created on 12 Jun 2018  路  9Comments  路  Source: ThreeMammals/Ocelot

Actual Behavior

Currently, I have Identity Server 4 running with one instance in a Standalone Service Fabric Cluster with 1 instance of Ocelot in the same cluster and 5 instances of a service that only returns an array of values but in this last service I need to read or get some Claims of the users that were authenticated in Ocelot.

How to send the claims from Ocelot to that internal service?

Greetings

Specifications

  • Version: Ocelot 7.0.4
  • Platform: Visual Studio 2017 / WinPro10
  • Subsystem: ASP.NET Core 2 / .NET Core 2.0 / Identity Server 4
question

Most helpful comment

Thanks @TomPallister for your help, finally I have the complete solution to generate, get, transform and send the claims with Ocelot to the internal Service Fabric (Microservices) as follow:

  1. First, in Identity Server 4 project, I made this code refactor in the ApiResource and Client definition:
public static IEnumerable<ApiResource> ApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("socialnetwork", "Social Network")
        {
            // Define the claims that will use the Resource and generate in the Token
            UserClaims = new List<string> { "City", "State" }
        }
    };
}

public static IEnumerable<Client> Clients()
{
    return new[]
    {
        new Client
        {
            ClientId = "socialnetworkclient",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            ClientSecrets = { new Secret("secret".Sha256()) },
            AllowedScopes = { "socialnetwork" },
            AccessTokenType = AccessTokenType.Jwt,
            // Specify that I want to include the claims in the same Token
            AlwaysSendClientClaims = true
        }
    };
}

public static IEnumerable<TestUser> Users()
{
    return new[]
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "[email protected]",
            Password = "*******",
            Claims = new List<Claim>
            {
                new Claim("City", "Mexicali"),
                new Claim("State", "Baja California")
            }
        }
    };
}
  1. So, when I authenticate to the IdentityServer4 url endpoint http://+/connect/token I confirm that the claims was returned in the Token, decoded with the help of https://jwt.io website as follow:

image

  1. Then I just modified the configuration.json Ocelot file with the specification as the docs says here with the following json code:
{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/{route}",
            "UpstreamPathTemplate": "/servicea/{route}",
            "UpstreamHttpMethod": [ "Get", "Post", "Put", "Patch", "Delete", "Options" ],
            "DownstreamScheme": "http",
            "ServiceName": "ServiceFabricApplication/ServiceA",
            "UseServiceDiscovery": true,
            "AuthenticationOptions": {
                "AuthenticationProviderKey": "TestKey",
                "AllowedScopes": []
            },
            // Here it is the instruction for the conversion of Claims
            // to request Headers
            "AddHeadersToRequest": {
                "claims_City": "Claims[City] > value > |",
                "claims_State": "Claims[State] > value > |"
            }
        },
        {
            "DownstreamPathTemplate": "/{route}",
            "UpstreamPathTemplate": "/serviceoauth/{route}",
            "UpstreamHttpMethod": [ "Get", "Post", "Put", "Patch", "Delete", "Options" ],
            "DownstreamScheme": "http",
            "ServiceName": "ServiceFabricApplication/ServiceOAuth",
            "UseServiceDiscovery": true
        }
    ],
    "GlobalConfiguration": {
        "RequestIdKey": "OcRequestId",
        "AdministrationPath": "/administration",
        "ServiceDiscoveryProvider": {
            "Host": "localhost",
            "Port": 19081,
            "Type": "ServiceFabric"
        }
    }
}
  1. And finally, when I send a request to the Ocelot service pointing to the microservice that needs authentication I received those claims in the headers of the request in the microservice like so:

image

That's all ... thanks again @TomPallister

Here it is the complete source code sample that I want to share for everyone GitHub Repository

All 9 comments

@licjapodaca I think you have two options, Ocelot will forward the authorization header to your services so you can parse the token again and get the claim value or you can use the claims tranformation feauture, the docs are here

Thanks @TomPallister for your help, finally I have the complete solution to generate, get, transform and send the claims with Ocelot to the internal Service Fabric (Microservices) as follow:

  1. First, in Identity Server 4 project, I made this code refactor in the ApiResource and Client definition:
public static IEnumerable<ApiResource> ApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("socialnetwork", "Social Network")
        {
            // Define the claims that will use the Resource and generate in the Token
            UserClaims = new List<string> { "City", "State" }
        }
    };
}

public static IEnumerable<Client> Clients()
{
    return new[]
    {
        new Client
        {
            ClientId = "socialnetworkclient",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            ClientSecrets = { new Secret("secret".Sha256()) },
            AllowedScopes = { "socialnetwork" },
            AccessTokenType = AccessTokenType.Jwt,
            // Specify that I want to include the claims in the same Token
            AlwaysSendClientClaims = true
        }
    };
}

public static IEnumerable<TestUser> Users()
{
    return new[]
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "[email protected]",
            Password = "*******",
            Claims = new List<Claim>
            {
                new Claim("City", "Mexicali"),
                new Claim("State", "Baja California")
            }
        }
    };
}
  1. So, when I authenticate to the IdentityServer4 url endpoint http://+/connect/token I confirm that the claims was returned in the Token, decoded with the help of https://jwt.io website as follow:

image

  1. Then I just modified the configuration.json Ocelot file with the specification as the docs says here with the following json code:
{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/{route}",
            "UpstreamPathTemplate": "/servicea/{route}",
            "UpstreamHttpMethod": [ "Get", "Post", "Put", "Patch", "Delete", "Options" ],
            "DownstreamScheme": "http",
            "ServiceName": "ServiceFabricApplication/ServiceA",
            "UseServiceDiscovery": true,
            "AuthenticationOptions": {
                "AuthenticationProviderKey": "TestKey",
                "AllowedScopes": []
            },
            // Here it is the instruction for the conversion of Claims
            // to request Headers
            "AddHeadersToRequest": {
                "claims_City": "Claims[City] > value > |",
                "claims_State": "Claims[State] > value > |"
            }
        },
        {
            "DownstreamPathTemplate": "/{route}",
            "UpstreamPathTemplate": "/serviceoauth/{route}",
            "UpstreamHttpMethod": [ "Get", "Post", "Put", "Patch", "Delete", "Options" ],
            "DownstreamScheme": "http",
            "ServiceName": "ServiceFabricApplication/ServiceOAuth",
            "UseServiceDiscovery": true
        }
    ],
    "GlobalConfiguration": {
        "RequestIdKey": "OcRequestId",
        "AdministrationPath": "/administration",
        "ServiceDiscoveryProvider": {
            "Host": "localhost",
            "Port": 19081,
            "Type": "ServiceFabric"
        }
    }
}
  1. And finally, when I send a request to the Ocelot service pointing to the microservice that needs authentication I received those claims in the headers of the request in the microservice like so:

image

That's all ... thanks again @TomPallister

Here it is the complete source code sample that I want to share for everyone GitHub Repository

@licjapodaca awesome :) thanks!!

@licjapodaca Great, man! You saved the day!

You're welcome :)

@licjapodaca I'm trying to forward some claims value to route parameter, but in the destination api service these values has the placeholder name instead of the claim value.

My ocelot.json
"ReRoutes": [ { "DownstreamPathTemplate": "/api/test/{company}/{customerCode}", "ChangeDownstreamPathTemplate": { "company": "Claims[company] > value > |", "customerCode": "Claims[customerCode] > value > |" }, "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 60700 }], "UpstreamHeaderTransform": { "user-host-ip": "{RemoteIpAddress}", "proxy-host": "{BaseUrl}" }, "UpstreamPathTemplate": "/test/template", "UpstreamHttpMethod": [ "Get", "Post" ], "AuthenticationOptions": { "AuthenticationProviderKey": "Bearer", "AllowedScopes": [] }, } ]

In the service api action "Get"
image

We have custom claims with custom schema; what's wrong with my configuration?

Thank you!

@Fabman08 Hi there, first of all, try to use the same Business Logic I used in this issue, check my configuration.json file that correspond to your ocelot.json file, and use the same properties to transform your Claims into Request Headers, be based on this resource link (Claims Transformation), and in the API service the Claims that comes in the request headers, they arrive as so, as request headers and NOT as parameters in the action method, instead, check the Collection Headers of the Request ASP.NET object as I specified at the top of this issue, and that's all.

Regards

Thanks @TomPallister for your help, finally I have the complete solution to generate, get, transform and send the claims with Ocelot to the internal Service Fabric (Microservices) as follow:

  1. First, in Identity Server 4 project, I made this code refactor in the ApiResource and Client definition:
public static IEnumerable<ApiResource> ApiResources()
{
  return new List<ApiResource>
  {
      new ApiResource("socialnetwork", "Social Network")
      {
          // Define the claims that will use the Resource and generate in the Token
          UserClaims = new List<string> { "City", "State" }
      }
  };
}

public static IEnumerable<Client> Clients()
{
  return new[]
  {
      new Client
      {
          ClientId = "socialnetworkclient",
          AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
          ClientSecrets = { new Secret("secret".Sha256()) },
          AllowedScopes = { "socialnetwork" },
          AccessTokenType = AccessTokenType.Jwt,
          // Specify that I want to include the claims in the same Token
          AlwaysSendClientClaims = true
      }
  };
}

public static IEnumerable<TestUser> Users()
{
  return new[]
  {
      new TestUser
      {
          SubjectId = "1",
          Username = "[email protected]",
          Password = "*******",
          Claims = new List<Claim>
          {
              new Claim("City", "Mexicali"),
              new Claim("State", "Baja California")
          }
      }
  };
}
  1. So, when I authenticate to the IdentityServer4 url endpoint http://+/connect/token I confirm that the claims was returned in the Token, decoded with the help of https://jwt.io website as follow:

image

  1. Then I just modified the configuration.json Ocelot file with the specification as the docs says here with the following json code:
{
  "ReRoutes": [
      {
          "DownstreamPathTemplate": "/{route}",
          "UpstreamPathTemplate": "/servicea/{route}",
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Patch", "Delete", "Options" ],
          "DownstreamScheme": "http",
          "ServiceName": "ServiceFabricApplication/ServiceA",
          "UseServiceDiscovery": true,
          "AuthenticationOptions": {
              "AuthenticationProviderKey": "TestKey",
              "AllowedScopes": []
          },
          // Here it is the instruction for the conversion of Claims
          // to request Headers
          "AddHeadersToRequest": {
              "claims_City": "Claims[City] > value > |",
              "claims_State": "Claims[State] > value > |"
          }
      },
      {
          "DownstreamPathTemplate": "/{route}",
          "UpstreamPathTemplate": "/serviceoauth/{route}",
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Patch", "Delete", "Options" ],
          "DownstreamScheme": "http",
          "ServiceName": "ServiceFabricApplication/ServiceOAuth",
          "UseServiceDiscovery": true
      }
  ],
  "GlobalConfiguration": {
      "RequestIdKey": "OcRequestId",
      "AdministrationPath": "/administration",
      "ServiceDiscoveryProvider": {
          "Host": "localhost",
          "Port": 19081,
          "Type": "ServiceFabric"
      }
  }
}
  1. And finally, when I send a request to the Ocelot service pointing to the microservice that needs authentication I received those claims in the headers of the request in the microservice like so:

image

That's all ... thanks again @TomPallister

Here it is the complete source code sample that I want to share for everyone GitHub Repository

AddHeadersToRequest add to headers, but AddClaimsToRequest is not adding to claims. see the below example

AddClaimsToRequest: {
"claims_Email": "Claims[Email] > value > |",
"claims_Name": "Claims[Name] > value > |"
}

In API project

(ClaimsIdentity)User.Identity doesn't have any claims.

@acheriya you have to parse token in order to use User.Identity with Claims.
In startup.cs of your microservice:

    //ConfigureServices
        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = CreateTokenValidationParameters();
        });

...

private TokenValidationParameters CreateTokenValidationParameters() //we ignore token validation because gateway validates it
    {
        var result = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateIssuerSigningKey = false,
            SignatureValidator = delegate (string token, TokenValidationParameters parameters)
            {
                var jwt = new JwtSecurityToken(token);
                return jwt;
            },
            RequireExpirationTime = true,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero,
        };
        result.RequireSignedTokens = false;
        return result;
    }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

FerAguilarR93 picture FerAguilarR93  路  3Comments

piyey picture piyey  路  6Comments

ioritzalberdi picture ioritzalberdi  路  5Comments

nagybalint001 picture nagybalint001  路  4Comments

jefferson picture jefferson  路  3Comments