Skip to content

Commit

Permalink
Ensure to pass-trough options to TokenProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
frederikprijck committed Sep 15, 2023
1 parent 3c4ac31 commit 46698ce
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/management/management-client-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface ManagementClientOptionsWithClientSecret extends ManagementClien
export interface ManagementClientOptionsWithClientAssertion extends ManagementClientOptions {
clientId: string;
clientAssertionSigningKey: string;
clientAssertionSigningAlg?: string;
}

export type ManagementClientOptionsWithClientCredentials =
Expand Down
32 changes: 13 additions & 19 deletions src/management/token-provider.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import { AuthenticationClient } from '../auth/index.js';
import { TokenSet } from '../auth/oauth.js';
import { JSONApiResponse } from '../lib/models.js';
import {
ManagementClientOptionsWithClientAssertion,
ManagementClientOptionsWithClientCredentials,
ManagementClientOptionsWithClientSecret,
} from './management-client-options.js';

const LEEWAY = 10 * 1000;

interface BaseOptions {
domain: string;
audience: string;
clientId: string;
enableCache?: boolean;
}

export class TokenProvider {
private authenticationClient: AuthenticationClient;
private expiresAt = 0;
private accessToken = '';
private pending: Promise<JSONApiResponse<TokenSet>> | undefined;

constructor(options: BaseOptions & { clientSecret: string });
constructor(options: BaseOptions & { clientAssertionSigningKey: string });
constructor(private options: any) {
this.authenticationClient = new AuthenticationClient({
clientId: options.clientId,
domain: options.domain,
clientSecret: options.clientSecret,
clientAssertionSigningKey: options.clientAssertionSigningKey,
});
constructor(options: ManagementClientOptionsWithClientSecret & { audience: string });
constructor(options: ManagementClientOptionsWithClientAssertion & { audience: string });
constructor(
private options: ManagementClientOptionsWithClientCredentials & { audience: string }
) {
this.authenticationClient = new AuthenticationClient(options);
}

public async getAccessToken() {
const disableCache = this.options.enableCache === false;
if (disableCache || !this.accessToken || Date.now() > this.expiresAt - LEEWAY) {
if (!this.accessToken || Date.now() > this.expiresAt - LEEWAY) {
this.pending =
(!disableCache && this.pending) ||
this.pending ||
this.authenticationClient.oauth.clientCredentialsGrant({
audience: this.options.audience,
});
Expand Down
24 changes: 11 additions & 13 deletions test/management/token-provider.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import nock from 'nock';
import { jest } from '@jest/globals';
import { TokenProvider } from '../../src/management/token-provider.js';
import { FetchAPI } from '../../src/lib/models.js';

const opts = {
domain: 'test-domain.auth0.com',
Expand Down Expand Up @@ -47,13 +48,6 @@ describe('TokenProvider', () => {
expect(spy).toHaveBeenCalledTimes(1);
});

it('should get a new access token if caching disabled', async () => {
const tp = new TokenProvider({ ...opts, enableCache: false });
expect(await tp.getAccessToken()).toBe('my-access-token');
expect(await tp.getAccessToken()).toBe('my-access-token');
expect(spy).toHaveBeenCalledTimes(2);
});

it('should get a new access token if token expires', async () => {
jest.useFakeTimers({ doNotFake: ['nextTick'] });
const tp = new TokenProvider(opts);
Expand Down Expand Up @@ -93,12 +87,16 @@ describe('TokenProvider', () => {
expect(spy).toHaveBeenCalledTimes(1);
});

it('should not cache concurrent requests when caching disabled', async () => {
const tp = new TokenProvider({ ...opts, enableCache: false });
expect(
await Promise.all([tp.getAccessToken(), tp.getAccessToken(), tp.getAccessToken()])
).toEqual(['my-access-token', 'my-access-token', 'my-access-token']);
it('should use a custom fetch', async () => {
const customFetch = jest
.fn<FetchAPI>()
.mockImplementation((url: URL | RequestInfo, init?: RequestInit) => fetch(url, init));

expect(spy).toHaveBeenCalledTimes(3);
const tp = new TokenProvider({
...opts,
fetch: customFetch as any,
});
expect(await tp.getAccessToken()).toBe('my-access-token');
expect(customFetch).toHaveBeenCalled();
});
});
2 changes: 1 addition & 1 deletion v4_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Some method names have been changed to better align with the documentation.
The following configuration options have changed:

- `scope` - The Management Client uses the Client Credentials grant which gets all the scopes granted to the application. So this is redundant and has been removed.
- `tokenProvider.enableCache` - Use the `enableCache` option of the Management Client.
- `tokenProvider.enableCache` - Each instance of the Management Client has its own cache, if you want a new cache you want to instantiate a new Management Client.
- `tokenProvider.cacheTTLInSeconds` - Each instance of the Management Client only stores a single access token, so this functionality has been removed.
- `proxy` - You should now provide an [HttpsAgent](https://nodejs.org/api/https.html#class-httpsagent) as the `agent` config.
- `includeResponseHeaders` - This has been removed, all return types include the response headers by default.
Expand Down

0 comments on commit 46698ce

Please sign in to comment.