Support async functions for tokenParams and providerParams
- Dominant language
- JavaScript
- Stars
- 616
- Forks
- 210
- Avg merge
- 3m
- Merged PRs (30d)
- 3
Description
### 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](https://datatracker.ietf.org/doc/html/rfc7523).
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.
Contributor guide
Assessment
This issue has not been assessed yet.