Ocelot - adding multiple AuthenticationProviderKeys for one Downstream - authentication

I have 2 authentication methods (2 different login pages) for my project that return the JWT token. Some microservices are supposed to accept only one of the two methods, but others should be accessible and authorized by both methods (either one or the other). Essentially my problem is in the AuthenticationOptions part of the Ocelot configuration:
{
"DownstreamPathTemplate": "/FM",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5285
}
],
"AuthenticationOptions": {
"AuthenticationProviderKey": "firstauthenticationmethod",
"AllowedScopes": []
},
"UpstreamPathTemplate": "/API/GetFMData",
"UpstreamHttpMethod": [ "Get" ]
},
Here I can provide only one AuthenticationProviderKey for the FM microservice. However this one I would like to be authorized whether the user is giving JWT token from the first authentication method or from the second one. I can't supply an array of strings to this property like this for example:
"AuthenticationOptions": {
"AuthenticationProviderKey": ["firstauthenticationmethod", "secondauthenticationmethod"],
"AllowedScopes": []
}
nor provide an array of AuthenticationOptions like this for example:
"AuthenticationOptions": [{
"AuthenticationProviderKey": "firstauthenticationmethod",
"AllowedScopes": []
},
{
"AuthenticationProviderKey": "secondauthenticationmethod",
"AllowedScopes": []
}],
Both of these are not allowed in the Ocelot config file. Is there a way to make this microservice configurable in Ocelot to allow either of the authentication methods?

Related

Adding two jwt validators to api endpoint through krakenD

I have two authentication mechanisms that I need to enable through proxy using krakenD. Each authentication has their own jwk-url to validate the keys of the token. I am using krakenD community edition 1.3 and following the krakenD documentation for jwt validation here https://www.krakend.io/docs/v1.3/authorization/jwt-validation/. Is there a way to add two jwt validators to the same endpoint?
{
"endpoint": "/paas/hydra/test/ab/projects/{project}",
"method": "GET",
"output_encoding": "no-op",
"headers_to_pass": ["Authorization"],
"extra_config": {
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
"jwk-url": "{{ .jwk.host }}/oauth2/v1/keys",
"cache": true,
"cache_duration": 1800,
"disable_jwk_security": true
},
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
"jwk-url": "https://authbluetokens.aexp.com/v2/app2app/tokens/keys",
"cache": true,
"cache_duration": 1800,
"disable_jwk_security": true
}
},
"backend": [
{
"method": "GET",
"encoding": "no-op",
"host": ["http://localhost:8080"],
"url_pattern": "/v6/workspaces/{project}",
"extra_config": {
"github.com/devopsfaith/krakend/transport/http/client/executor": {
"name": "bomoktacustom",
"audienceid": "0oasx1emolGCrwnht0x7,0oasx1nyh6ytqb96z0x7,0oaohi3lo9nr9lMXu0x7,0oarc7drtoyMIbAic0x7,0oaqnh8rpuaCUFhD70x7,0oaqctxj5rYvoUVuy0x7,0oappvfm4hre783rH0x7,0oawdujgrqi4DPyBz0x7,*.aexp.com"
},
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 6,
"capacity": 6
},
"github.com/devopsfaith/krakend-martian": {
"header.Modifier": {
"scope": ["request", "response"],
"name": "Content-Type",
"value": "application/json"
}
}
}
}
]
},
As shown in the code, I have tried adding two krakend-jose/validator to the same endpoint. The current behavior of this implementation is krakenD ignores the first validator and only utilizes the second one. When using a token that requires the first validator krakenD returns Error #01: no Keys has been found. But using a token with the second validator works. The behavior I need is krakenD allowing to validate both type of tokens. Any help would be much appreciated!!
The functionality you are looking for works only in the enterprise edition: https://www.krakend.io/docs/enterprise/authentication/multiple-identity-providers/ (it was also available on EE v1.3)
The community version expects 1 identity provider per endpoint only. Duplicating the keys or any other approach is not going to have the desired outcome.

Is there a way to push the message to two backends in the async agent?

(As context, I am using RabbitMQ as the message broker, integrated by KrakenD. The APIs are using Nestjs.)
I understand that the async agent in KrakenD can push the data consumed to multiple backends:
KrakenD contacts the defined backend(s) list passing the event data when a new message kicks in.
However, passing two different backends here result to logger indicating a context exceeded for both of the APIs. If I just put a single backend in the list, it returns what's expected.
Here's the working code:
"backend": [
{
"url_pattern": "/newOrder",
"method": "POST",
"host": [ "http://127.0.0.1:3300" ],
"disable_host_sanitize": false,
"extra_config": {
"modifier/martian": {
"header.Modifier": {
"scope": [
"request"
],
"name": "Content-Type",
"value": "application/json"
}
}
}
},
{
"url_pattern": "/newOrderNotification",
"method": "POST",
"host": [ "http://127.0.0.1:3200" ],
"disable_host_sanitize": false,
"extra_config": {
"modifier/martian": {
"header.Modifier": {
"scope": [
"request"
],
"name": "Content-Type",
"value": "application/json"
}
}
}
}
],
Hope I can receive any advice on this. Thanks!
You can connect a single async agent to several backends but KrakenD does not support distributed transactions, so no more than one non-safe backend request (as defined at RFC 2616, section 9) is allowed per pipe. From the documentation (https://www.krakend.io/docs/backends/):
Even though you can use several backends in one endpoint, KrakenD does not allow you to define multiple non-safe (write) backends. This is a (sometimes controversial) design decision to disable the gateway to handle transactions.
If you need to have a write method (POST, PUT, DELETE, PATCH) together with other GET methods, use the sequential proxy and place a maximum of 1 write method at the end of the sequence.
If you want to send a secondary non-safe request, you can add a minimal lua snippet using the http_response helper (https://www.krakend.io/docs/endpoints/lua/#making-additional-requests-http_response) just like this:
{
"extra_config": {
"modifier/lua-proxy": {
"pre": "local r = request.load(); http_response.new('http://127.0.0.1:3200/newOrderNotification', "POST", r:body())"
}
}
}

Ocelot API Gateway Optional Parameter

Is there a way to tell Ocelot that a parameter is optional?
Let's say the query param below is optional:
"DownstreamPathTemplate": "/api/SearchAPI/?query={query}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5000
}
],
"UpstreamPathTemplate": "/api/SearchAPI/?query={query}",
"UpstreamHttpMethod": [ "GET" ],
"FileCacheOptions": {
"TtlSeconds": 60
}
If I send a request to /SearchAPI/?query= I get a 404 as response. I need to duplicate the Upstream to not use the param to fix. Is there another way to fix that?
You can use {everything} like
"DownstreamPathTemplate": "/api/SearchAPI/{everything}"

IdentityServer4 read client claims from appsettings.json

how can I read client claims from the appsettings.json file?
I have this appsettings.json:
"IdentityServer": {
"Clients": [
{
"Enabled": true,
"ClientId": "client1",
"AlwaysSendClientClaims": true,
"Claims": [
{
"Type": "custom_claim1",
"Value": "value1"
},
{
"Type": "custom_claim2",
"Value": "value2"
}
]
}
]
}
And, I load the clients config like the docs says:
var builder = services.AddIdentityServer(opts =>
{
/// Opts removed for simplicity
})
.AddInMemoryClients(Configuration.GetSection("IdentityServer:Clients"));
All is working fine, except for the client claims. I can not see them in Jwt.io decode tool.
There is a problem binding the Claims collection in the Clients[] from appSettings.json due to the fact that the current implementation of the Claim object can not be deserialized from json.
https://github.com/IdentityServer/IdentityServer4/issues/2573
and here
https://github.com/IdentityServer/IdentityServer4/pull/3887/files/ed14abc204960b2d5ca3418a868882a698e54d90

Loopback Connector REST API

How to create an external API on Loopback?
I want to get the external API data and use it on my loopback application, and also pass the input from my loopback to external API and return result or response.
Loopback has the concept of non-database connectors, including a REST connector. From the docs:
LoopBack supports a number of connectors to backend systems beyond
databases.
These types of connectors often implement specific methods depending
on the underlying system. For example, the REST connector delegates
calls to REST APIs while the Push connector integrates with iOS and
Android push notification services.
If you post details on the API call(s) you want to call then I can add some more specific code samples for you. In the mean time, this is also from the documentation:
datasources.json
MyModel": {
"name": "MyModel",
"connector": "rest",
"debug": false,
"options": {
"headers": {
"accept": "application/json",
"content-type": "application/json"
},
"strictSSL": false
},
"operations": [
{
"template": {
"method": "GET",
"url": "http://maps.googleapis.com/maps/api/geocode/{format=json}",
"query": {
"address": "{street},{city},{zipcode}",
"sensor": "{sensor=false}"
},
"options": {
"strictSSL": true,
"useQuerystring": true
},
"responsePath": "$.results[0].geometry.location"
},
"functions": {
"geocode": ["street", "city", "zipcode"]
}
}
]
}
You could then call this api from code with:
app.dataSources.MyModel.geocode('107 S B St', 'San Mateo', '94401', processResponse);
You gonna need https module for calling external module inside loopback.
Suppose you want to use the external API with any model script file. Let the model name be Customer
Inside your loopback folder. Type this command and install https module.
$npm install https --save
common/models/customer.js
var https = require('https');
Customer.externalApiProcessing = function(number, callback){
var data = "https://rest.xyz.com/api/1";
https.get(
data,
function(res) {
res.on('data', function(data) {
// all done! handle the data as you need to
/*
DO SOME PROCESSING ON THE `DATA` HERE
*/
enter code here
//Finally return the data. the return type should be an object.
callback(null, data);
});
}
).on('error', function(err) {
console.log("Error getting data from the server.");
// handle errors somewhow
callback(err, null);
});
}
//Now registering the method
Customer.remoteMethod(
'extenalApiProcessing',
{
accepts: {arg: 'number', type: 'string', required:true},
returns: {arg: 'myResponse', type: 'object'},
description: "A test for processing on external Api and then sending back the response to /externalApiProcessing route"
}
)
common/models/customer.json
....
....
//Now add this line in the ACL property.
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "extenalApiProcessing"
}
]
Now explore the api at /api/modelName/extenalApiProcessing
By default its a post method.
For more info. Loopback Remote Methods