OAuth2 client_assertion

22 views
Skip to first unread message

Tommy Gilligan

unread,
May 21, 2024, 12:58:25 AMMay 21
to KrakenD Community
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.html
We 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-bearer


Notably, 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:
  1. Generate client_assertion in a Lua script.  This would require 3rd party-libraries and therefore is not really a viable workaround.
  2. 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?
  3. 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,
  );
}

Albert Lombarte

unread,
May 21, 2024, 4:50:09 AMMay 21
to KrakenD Community, Tommy Gilligan
Hi Tommy,

I am unfamiliar with the internals of client credentials, but someone with better knowledge can jump in. However, allow me to comment on something that might help.

When configuring the client credentials, you pass a token URL like http://localhost:8012/services/rest/auth/oauth2/v1/token. Maybe this URL could be an internal KrakenD endpoint that performs any additional modifications you need. This is a common practice that helps you overcome many issues. So, you would have the client credentials flow asking the token to KrakenD, which would add any necessary bits, and then ask Netsuite, and the final token saved on KrakenD and having the full functionality.

Let me know if this workaround works for you

El dia dimarts, 21 de maig del 2024 a les 6:58:25 UTC+2, Tommy Gilligan va escriure:
Reply all
Reply to author
Forward
0 new messages