Hello KrakenD community!
We are using KrakenD as a proxy for Netsuite. If I'm reading the documentation correctly: KrakenD can be configured to handle the OAuth2 client_credentials flow used by Netsuite. This seems very convenient and we would love to make use of this feature.
Netsuite provides some sample Python code for building the token request at:
https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/article_0907012935.htmlWe have recreated this in Typescript and verified that requesting an access token in this way from Netsuite succeeds. The request looks like this:
POST /services/rest/auth/oauth2/v1/token HTTP/2
Host: localhost:8012
User-Agent: curl/8.4.0
Accept: */*
Content-Length: 1070
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_assertion_type=urn:ietf:par
ams:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJh
bGciOiJ[REDACTED]Using "auth/client-credentials", I've configured KrakenD for Netsuite OAuth2 like so:
{
"host": [ "https://accountid.connect.api.netsuite.com" ],
"url_pattern": "/protected-resource",
"extra_config": {
"auth/client-credentials": {
"client_id": "[CLIENT_ID]",
"client_secret": "[CLIENT_SECRET]",
"token_url": "http://localhost:8012/services/rest/auth/oauth2/v1/token",
"scopes": "restlets,rest_webservices",
"endpoint_params": {
"client_assertion_type": ["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"],
"client_id": ["[REDACTED]"],
"client_secret": ["[REDACTED]"]
}
}
}
}KrakenD then generates a token request that looks like this:
POST /services/rest/auth/oauth2/v1/token HTTP/1.1
Host: localhost:8012
User-Agent: Go-http-client/1.1
Content-Length: 2877
Authorization: Basic YTAwYm[REDACTED]
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
client_id=[REDACTED]&client_secret=[REDACTED]&grant_type=client_credentials&scope=restlets+rest_webservicesk&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearerNotably, this doesn't include client_assertion needed by Netsuite. What we would really like to know: is there a way to get KrakenD to generate a client_assertion and include this in the OAuth2 token request body?
Inspecting KrakenD source code, it uses
golang.org/x/oauth2 to provide OAuth2 functionality. This library has some outstanding issues that look to be related to what we are trying to do:
This would imply that auth/client-credentials cannot be used for generating client_assertion. Thus, we have been considering some workarounds:
- Generate client_assertion in a Lua script. This would require 3rd party-libraries and therefore is not really a viable workaround.
- Generate client_assertion in clients, passing it to auth/client-credentials via custom header. It nearly seems like endpoint_params could be used for this except that only static values can be used. Is it possible for endpoint_params to reference request headers?
- Point token_url at a separate KrakenD endpoint responsible for generating the client_assertion. This could be implemented as a Go extension.
It is not the end of the world if we cannot get KrakenD to do OAuth2 for us. We can let our KrakenD clients assume this responsibility if we really need to but thought it would be good to check that there isn't something obvious we're missing.
Can KrakenD be used for a client credentials OAuth2 flow that includes a client_assertion request body parameter? How would you configure it for this?
Kindly,
Tommy Gilligan
PS. Typescript code for generating Netsuite client_assertion:
import { KJUR } from "jsrsasign";
exports.getClientAssertion = function getClientAssertion(
aud: string,
iss: string,
kid: string,
privateKey: string,
): string {
const now = Date.now() / 1000;
const alg = "PS256";
return KJUR.jws.JWS.sign(
alg,
JSON.stringify({ alg, typ: "JWT", kid }),
{
iss,
scope: ["restlets", "rest_webservices"],
iat: now,
exp: now + 3600 - 1,
aud,
},
privateKey,
);
}