Repository: auth0/node-auth0
Pull request #793 (2023-03-14 11:45:53)

feat: allow to pass a method as token
### Changes

This allow to have custom management of the token. For example when scaling an application horizontally we might want to share the tokens between all the application instances. 
This allow to do so with the following : 
```ts
const auth0ManagementClient = new ManagementClient({
  token: () => Promise.resolve('my-token')
  clientId,
  domain,
  audience,
  scope,
});
``` 

### References

Following this issue : https://github.com/auth0/node-auth0/issues/792

### Testing

Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors. 

- [X] This change adds unit test coverage
- [ ] This change adds integration test coverage

### Checklist

- [X] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
- [X] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
- [X] All existing and new tests complete without errors



Patch:
diff --git a/src/management/index.js b/src/management/index.js
index e38e12852..9bacac36a 100644
--- a/src/management/index.js
+++ b/src/management/index.js
@@ -95,7 +95,7 @@ class ManagementClient {
    *          If no token is provided domain, clientId and clientSecret (or clientAssertionSigningKey)
    *          are required.
    * @param   {string}  options.domain                              ManagementClient server domain.
-   * @param   {string}  [options.token]                             API access token.
+   * @param   {string|Function}  [options.token]       API access token.
    * @param   {string}  [options.clientId]                          Management API Non Interactive Client Id.
    * @param   {string}  [options.clientSecret]                      Management API Non Interactive Client Secret.
    * @param   {string}  [options.clientAssertionSigningKey]         Private key used to sign the client assertion JWT.
@@ -144,15 +144,19 @@ class ManagementClient {
       }
 
       this.tokenProvider = new ManagementTokenProvider(config);
-    } else if (typeof options.token !== 'string' || options.token.length === 0) {
+    } else if (
+      typeof options.token !== 'function' &&
+      (typeof options.token !== 'string' || options.token.length === 0)
+    ) {
       throw new ArgumentError('Must provide a token');
     } else {
       this.tokenProvider = {
         getAccessToken() {
-          return Promise.resolve(options.token);
+          return typeof options.token === 'function'
+            ? options.token()
+            : Promise.resolve(options.token);
         },
       };
-      managerOptions.headers['Authorization'] = `Bearer ${options.token}`;
     }
 
     managerOptions.tokenProvider = this.tokenProvider;


Test patch:
diff --git a/test/management/management-client.tests.js b/test/management/management-client.tests.js
index 8b38001af..33cc89362 100644
--- a/test/management/management-client.tests.js
+++ b/test/management/management-client.tests.js
@@ -37,10 +37,21 @@ describe('ManagementClient', () => {
     token: 'fake-token',
   };
 
+  const withTokenFunctionConfig = {
+    domain: 'auth0-node-sdk.auth0.com',
+    token: () => Promise.resolve('fake-function-token'),
+  };
+
   it('should expose an instance of ManagementClient when withTokenConfig is passed', () => {
     expect(new ManagementClient(withTokenConfig)).to.exist.to.be.an.instanceOf(ManagementClient);
   });
 
+  it('should expose an instance of ManagementClient when withTokenFunctionConfig is passed', () => {
+    expect(new ManagementClient(withTokenFunctionConfig)).to.exist.to.be.an.instanceOf(
+      ManagementClient
+    );
+  });
+
   it('should expose an instance of ManagementClient when withTokenProviderConfig is passed', () => {
     expect(new ManagementClient(withTokenProviderConfig)).to.exist.to.be.an.instanceOf(
       ManagementClient
@@ -964,5 +975,22 @@ describe('ManagementClient', () => {
       expect(headers).to.deep.equal({ 'content-type': 'application/json' });
       nock.cleanAll();
     });
+
+    it('should include the header Authorization with the token returned by the injected function', async function () {
+      const config = Object.assign({}, withTokenFunctionConfig);
+      this.client = new ManagementClient(config);
+
+      nock('https://auth0-node-sdk.auth0.com', {
+        reqheaders: {
+          Authorization: (value) => value === 'Bearer fake-function-token',
+        },
+      })
+        .get(`/api/v2/users`)
+        .reply(200, { data: 'value' });
+
+      const { data } = await this.client.getUsers();
+      expect(data).to.deep.equal('value');
+      nock.cleanAll();
+    });
   });
 });