Runtime
Node.js
Runtime version
24
Module version
13
Used with
Hapi application
Any other relevant information
No response
What problem are you trying to solve?
I'm building a Node.js service that authenticates users via Microsoft Entra ID using Bell.
The service runs on AWS and I want to use Federated Identity Credentials.
Federated identity means Entra accepts a short-lived JWT assertion that the app fetches from AWS STS, rather than a long-lived shared secret. Full details in RFC 7523.
Bell almost supports this as is, but requires two workarounds to get it working.
tokenParams does not support asynchronous functions
tokenParams is resolved synchronously by internals.resolveProviderParams. This means a function passed as tokenParams cannot be async - it cannot await a call to AWS STS to fetch the assertion token.
The workaround is to pre-fetch the token before the server starts up and store it in memory. I then have a background refresh loop to keep it current when the synchronous tokenParams needs to reference it.
That's obviously not ideal as it means I'm frequently fetching a fresh token even if no user needs it at that point.
clientSecret is required
I'm not sure if this is a workaround or intended usage, but I couldn't find it documented anywhere. As I don't have a clientSecret, to bypass Bell sending the client secret in the request, I have to pass the value as an empty object.
So combining the two workarounds above, I have config like this.
server.auth.strategy('entra', 'bell', {
clientSecret: {}, // object prevents Bell adding client_secret to the request
tokenParams: (_request) => getClientCredentialParams() // must be synchronous so call a function that returns current in memory token as `client_assertion` and `client_assetion_type` of `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
})
Do you have a new or modified API suggestion to solve the problem?
Make internals.resolveProviderParams async and await it's result on each call.
That allows me to remove the background refreshing of the token entirely.
server.auth.strategy('entra', 'bell', {
clientSecret: {},
tokenParams: async (_request) => {
const result = await stsClient.send(new GetWebIdentityTokenCommand({ ... }))
return {
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion: result.IdentityToken
}
}
})
I have opened a pull request for consideration with this change.
It would also be good to clarify whether passing an empty object for clientSecret is intended behaviour and won't stop working as a result of any refactoring.
Runtime
Node.js
Runtime version
24
Module version
13
Used with
Hapi application
Any other relevant information
No response
What problem are you trying to solve?
I'm building a Node.js service that authenticates users via Microsoft Entra ID using Bell.
The service runs on AWS and I want to use Federated Identity Credentials.
Federated identity means Entra accepts a short-lived JWT assertion that the app fetches from AWS STS, rather than a long-lived shared secret. Full details in RFC 7523.
Bell almost supports this as is, but requires two workarounds to get it working.
tokenParamsdoes not support asynchronous functionstokenParamsis resolved synchronously byinternals.resolveProviderParams. This means a function passed astokenParamscannot be async - it cannotawaita call to AWS STS to fetch the assertion token.The workaround is to pre-fetch the token before the server starts up and store it in memory. I then have a background refresh loop to keep it current when the synchronous
tokenParamsneeds to reference it.That's obviously not ideal as it means I'm frequently fetching a fresh token even if no user needs it at that point.
clientSecretis requiredI'm not sure if this is a workaround or intended usage, but I couldn't find it documented anywhere. As I don't have a
clientSecret, to bypass Bell sending the client secret in the request, I have to pass the value as an empty object.So combining the two workarounds above, I have config like this.
Do you have a new or modified API suggestion to solve the problem?
Make
internals.resolveProviderParamsasyncandawaitit's result on each call.That allows me to remove the background refreshing of the token entirely.
I have opened a pull request for consideration with this change.
It would also be good to clarify whether passing an empty object for
clientSecretis intended behaviour and won't stop working as a result of any refactoring.