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.
Related
(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())"
}
}
}
I am using a kickstart.json file to setup FusionAuth in developer environments. Everything is automated except I still need to manually go and get the client secret from the fusion auth instance.
Is there anyway I can predefine the client secret in the kickstart file so I can pre-configure it in my app?
you should absolutely be able to set the client secret from kickstart.json. Any API call should work from within Kickstart.
https://fusionauth.io/docs/v1/tech/apis/applications#create-an-application indicates you can POST an application including the client secret.
So a kickstart file like this should work:
{
"variables": {
"defaultTenantId": "30663132-6464-6665-3032-326466613934"
},
"apiKeys": [
{
"key": "mykey",
"description": "API key"
}
],
"requests": [
{
"method": "POST",
"url": "/api/application/85a03867-dccf-4882-adde-1a79aeec50df",
"body": {
"application": {
"name": "Pied Piper",
"roles": [
{
"name": "dev"
},
{
"name": "ceo"
},
{
"name": "intern"
}
],
"oauthConfiguration" : {
"clientSecret": "shhh-your-desired-secret"
}
}
}
}
]
}
I haven't tested that, but don't see any reason why it would not work. (Note that 1.37, the most recent version, has an issue with kickstart as documented here: https://github.com/FusionAuth/fusionauth-issues/issues/1816 but that should be fixed soon.)
If this doesn't work for you, please share the error message and a scrubbed kickstart file.
I am creating a serverless API using AWS SAM template and ASP.Net Core.
I wanted to know if it is possible to call a common lamnda function from multiple lambda functions?
I have 2 APIs for user authentication.
/user/authenticate
/admin/authenticate
Now when the user calls these API endpoints I want to call a common lambda function which will look like following:
public AuthResponse Authenticate(AuthInfo info, int role);
I get a user role based on which API endpoint is called. For example if /user/authetication is called then role=1 otherwise role=0.
And then I want Authenticate() lambda to perform user authentication based on the AuthInfo + Role.
I want to do this because all my users are stored in the same table and I would like to cross verify if user has the correct role to access the feature.
I will also share a portion of serverless.template used for above APIs.
/admin/authenticate
"Handler": "Server::API.Admin::Authenticate",
"Description" : "Allows admin to authenticate",
"Runtime": "dotnetcore2.1",
"CodeUri": "",
"MemorySize": 256,
"Timeout" : 300,
"Role": {"Fn::GetAtt" : [ "LambdaExecutionRole", "Arn"]},
"FunctionName" : "AdminAuthenticate",
"Events":
{
"PutResource":
{
"Type": "Api",
"Properties":
{
"Path": "/v1/admin/authenticate",
"Method": "POST"
}
}
}
}
}
/user/authenticate
"Handler": "Server::API.User::Authenticate",
"Description" : "Allows user to authenticate",
"Runtime": "dotnetcore2.1",
"CodeUri": "",
"MemorySize": 256,
"Timeout" : 300,
"Role": {"Fn::GetAtt" : [ "LambdaExecutionRole", "Arn"]},
"FunctionName" : "UserAuthenticate",
"Events":
{
"PutResource":
{
"Type": "Api",
"Properties":
{
"Path": "/v1/user/authenticate",
"Method": "GET"
}
}
}
}
}
As you can see above, 2 lambda functions are created AdminAuthenticate and UserAuthentication. I want these lambda functions to share the common code.
Does anyone has any idea how to do it?
Thanks and Regards.
I can think about 2 options to achieve your goal. In the first option, you use multiple Lambda functions, one for each endpoint, both pointing to your same codebase. In the second option, you have a single Lambda function that handles all authentication needs.
Single codebase, multiple functions
In this case, you can define your template file with 2 functions but use the CodeUri property to point to the same codebase.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Resources": {
"AdminFunction": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "Server::API.Admin::Authenticate",
"Description": "Allows admin to authenticate",
"Runtime": "dotnetcore2.1",
"CodeUri": "./codebase_path/",
"MemorySize": 256,
"Timeout": 300,
"FunctionName": "AdminAuthenticate",
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/v1/admin/authenticate",
"Method": "POST"
}
}
}
}
},
"UserFunction": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "Server::API.User::Authenticate",
"Description": "Allows user to authenticate",
"Runtime": "dotnetcore2.1",
"CodeUri": "./codebase_path/",
"MemorySize": 256,
"Timeout": 300,
"FunctionName": "UserAuthenticate",
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/v1/user/authenticate",
"Method": "POST"
}
}
}
}
}
}
}
Single codebase, single function
In this case, you will expose 2 endpoints on API Gateway, but they will be directed to the same handler on your function. Therefore, you will need to write some logic on your code to handle the login properly. The event object passed to your Lambda function will have information on the original URL in the path property (reference -- even though this is for Lambda proxy, still applies).
The template file in this case would be similar to (note I replaced Admin/User terms to "Any", since this will handle any form of authentication):
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Resources": {
"AnyFunction": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "Server::API.Any::Authenticate",
"Description": "Allows any to authenticate",
"Runtime": "dotnetcore2.1",
"CodeUri": "./hello_world/",
"MemorySize": 256,
"Timeout": 300,
"Events": {
"UserEndpoint": {
"Type": "Api",
"Properties": {
"Path": "/v1/user/authenticate",
"Method": "POST"
}
},
"AdminEndpoint": {
"Type": "Api",
"Properties": {
"Path": "/v1/admin/authenticate",
"Method": "POST"
}
}
}
}
}
}
}
You can invoke lambda functions through any lambda function, using aws sdk in your chosen language and there you have invoke function defined.
For a reference here is the link to boto3 invoke definition.
Also
The approach you are using for authentication using common codebase is not the right one.
If you need a lambda function to check or authentication particular request, you can setup custom authorizer for that, which in your terms i can say, share lambda code or call it first before invoking the lambda you setup for the particular endpoint with the possibility to pass the custom data if you want.
A Lambda authorizer (or as a custom authorizer) is an API Gateway feature that uses a Lambda function to control access to your API.
If still this doesn't solve your problem and you want common codebase, you can point out as many as api endpoint's to same lambda function.
Then you have to handle event['resources'] inside your codebase.
This is not the recommended way, but you can use it.
You can refer aws samples to setup custom authorizer or the documentation is fair enough to clear all your doubts.
Background
I am using the newer Yodlee Aggregation API that differs from what I was previously building off of.
I am currently using this endpoint in the account registration flow to put the MFA response:
PUT /{cobrandName}/v1/providers/{providerAccountId}
My request looks like this:
{
url: `${this.rest}providers/${providerAccountId}`,
headers: {
'Authorization': `cobSession=${self.appToken}, userSession=${token}`
},
form: {
'MFAChallenge': JSON.stringify(newMfa)
}
}
where this.rest is my personal rest url, providerAccountId is the appropriate providerAccountId for the refresh, self.appToken is the current cobrand session token, token is the current user's login token, and newMfa is a JSON object being stringified that matches this profile:
{
"loginForm": {
"mfaTimeout": 94650,
"formType": "questionAndAnswer",
"row": [
{
"id": "SQandA--QUESTION_1--Row--1",
"fieldRowChoice": "0001",
"form": "0001",
"label": "What is the name of your state?",
"field": [
{
"id": "SQandA_QUESTION_1_1",
"name": "QUESTION_1",
"isOptional": false,
"value": "Enter the answer",
"valueEditable": true,
"type": "text"
}
]
},
{
"id": "SQandA--QUESTION_2--Row--2",
"fieldRowChoice": "0001",
"form": "0001",
"label": "What is the name of your first school",
"field": [
{
"id": "SQandA_QUESTION_2_2",
"name": "QUESTION_2",
"isOptional": false,
"value": "Enter the answer",
"valueEditable": true,
"type": "text"
}
]
}
]
}
with the exeption being the value fields of the field object, which have been encrypted with PKI as per instructions.
Issue
However, when I carry out this PUT request, I get this error from Yodlee:
{ errorCode: 'Y803',
errorMessage: 'MFAChallenge or providerParam required',
referenceCode: 'p1460412835654A4Q24t' }
though I clearly have an MFAChallenge parameter in my form. Any ideas on why I could be getting this error if the MFAChallenge is present (and note that it is the only info that is passed through the PUT request other than through headers or url parameters)? I tried putting it through as body data, but that doesn't seem to work, and very few of the API endpoints actually seem to use body over form encoded strings, though there was at least one.
You have to send this information MFAChallenge as part of URL, see below example
https://developer.api.yodlee.com/ysl/restserver/v1/providers/10114184?MFAChallenge=%7B%20%22loginForm%22%3A%20%7B%20%20%20%20%20%22mfaTimeout%22%3A%2094650%2C%20%20%20%20%20%22formType%22%3A%20%22questionAndAnswer%22%2C%20%20%20%20%20%22row%22%3A%20%5B%20%20%20%20%20%7B%20%20%20%20%20%20%20%20%20%22id%22%3A%20%22SQandA--QUESTION_1--Row--1%22%2C%20%20%20%20%20%20%20%20%20%22fieldRowChoice%22%3A%20%220001%22%2C%20%20%20%20%20%20%20%20%20%22form%22%3A%20%220001%22%2C%20%20%20%20%20%20%20%20%20%22label%22%3A%20%22What%20is%20the%20name%20of%20your%20state%3F%22%2C%20%20%20%20%20%20%20%20%20%22field%22%3A%20%5B%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22id%22%3A%20%22SQandA_QUESTION_1_1%22%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%3A%20%22QUESTION_1%22%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22isOptional%22%3A%20false%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%22Enter%20the%20answer%22%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22valueEditable%22%3A%20true%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22text%22%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20%20%20%20%20%20%20%20%20%5D%20%20%20%20%20%7D%2C%20%20%20%20%20%7B%20%20%20%20%20%20%20%20%20%22id%22%3A%20%22SQandA--QUESTION_2--Row--2%22%2C%20%20%20%20%20%20%20%20%20%22fieldRowChoice%22%3A%20%220001%22%2C%20%20%20%20%20%20%20%20%20%22form%22%3A%20%220001%22%2C%20%20%20%20%20%20%20%20%20%22label%22%3A%20%22What%20is%20the%20name%20of%20your%20first%20school%22%2C%20%20%20%20%20%20%20%20%20%22field%22%3A%20%5B%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22id%22%3A%20%22SQandA_QUESTION_2_2%22%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%3A%20%22QUESTION_2%22%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22isOptional%22%3A%20false%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22value%22%3A%20%22Enter%20the%20answer%22%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22valueEditable%22%3A%20true%2C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22type%22%3A%20%22text%22%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20%20%20%20%20%20%20%20%20%5D%20%20%20%20%20%7D%20%5D%20%7D
I'm attempting to customize the oauth2orize all-grants example for my use. I can run the all-grants as-is and everything works (as you would expect), but when I run my customized version, I always end up with this error:
Error: Unable to issue redirect for OAuth 2.0 transaction
at Object.response [as handle] (C:\Dev\Expy\api\node_modules\oauth2orize\lib\grant\code.js:122:41)
I've been digging into this a bit and it seems there is a property of the txn variable within that function that should be named redirectURI and should be populated with the redirect_uri from the query string of the initial request to the /dialog/authorize page. For some reason this doesn't happen on my example app. Is this caused by an express version difference? That is the biggest difference that I see between the example code and my customizations. The all-grants uses express 2.* and my app will use express 4.*.
If it isn't an express version issue, where should I start looking in my code for the issue?
For reference, this is what I see in my app for the txn object:
txn: {
"transactionID": "evlUd2q4",
"client": { ... },
"req": {
"type": "code",
"clientID": "5C3B4438-433F-11E5-A532-74653C701F13"
},
"user": { ... },
"res": {
"allow": true
}
}
and this is what I see in that same object with the example (note the presence of the redirectURI in req and in the txn itself):
txn: {
"transactionID": "EEcYp3Uj",
"client": { ... },
"redirectURI": "http://localhost:3000/api/userinfo",
"req": {
"type": "code",
"clientID": "abc123",
"redirectURI": "http://localhost:3000/api/userinfo"
},
"user": { ... },
"res": {
"allow": true
}
}