Skip to content

Commit 4b99afd

Browse files
accept a function as token (#1069)
1 parent fbe605c commit 4b99afd

File tree

5 files changed

+105
-3
lines changed

5 files changed

+105
-3
lines changed

src/management/management-client-options.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export interface ManagementClientOptions extends ClientOptions {
66
}
77

88
export interface ManagementClientOptionsWithToken extends ManagementClientOptions {
9-
token: string;
9+
token: string | (() => Promise<string>) | (() => string);
1010
}
1111

1212
export interface ManagementClientOptionsWithClientSecret extends ManagementClientOptions {

src/management/token-provider-middleware.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import {
77
} from './management-client-options.js';
88
import { TokenProvider } from './token-provider.js';
99

10+
import { resolveValueToPromise } from '../utils.js';
11+
1012
export class TokenProviderMiddleware implements Middleware {
11-
private tokenProvider: { getAccessToken: () => Promise<string> };
13+
private readonly tokenProvider: { getAccessToken: () => Promise<string> };
1214
constructor(
1315
options: ManagementClientOptionsWithToken | ManagementClientOptionsWithClientCredentials
1416
) {
1517
if ('token' in options) {
1618
this.tokenProvider = {
17-
getAccessToken: () => Promise.resolve(options.token),
19+
getAccessToken: () => resolveValueToPromise<string>(options.token),
1820
};
1921
} else {
2022
this.tokenProvider = new TokenProvider({

src/utils.ts

+22
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,25 @@ export const generateClientInfo = () => ({
1515
* @private
1616
*/
1717
export const mtlsPrefix = 'mtls';
18+
19+
type SyncGetter<T> = () => T;
20+
type AsyncGetter<T> = () => Promise<T>;
21+
/**
22+
* Resolves a value that can be a static value, a synchronous function, or an asynchronous function.
23+
*
24+
* @template T - The type of the value to be resolved.
25+
* @param {T | SyncGetter<T> | AsyncGetter<T>} value - The value to be resolved. It can be:
26+
* - A static value of type T.
27+
* - A synchronous function that returns a value of type T.
28+
* - An asynchronous function that returns a Promise of type T.
29+
* @returns {Promise<T>} A promise that resolves to the value of type T.
30+
*/
31+
export const resolveValueToPromise = async <T>(
32+
value: T | SyncGetter<T> | AsyncGetter<T>
33+
): Promise<T> => {
34+
if (typeof value === 'function') {
35+
const result = (value as SyncGetter<T> | AsyncGetter<T>)(); // Call the function
36+
return result instanceof Promise ? result : Promise.resolve(result); // Handle sync/async
37+
}
38+
return Promise.resolve(value); // Static value
39+
};

test/lib/utils.test.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { resolveValueToPromise } from '../../src/utils.js';
2+
3+
describe('resolveValueToPromise', () => {
4+
it('should resolve a static string value', async () => {
5+
const value = 'staticValue';
6+
const result = await resolveValueToPromise(value);
7+
expect(result).toBe(value);
8+
});
9+
10+
it('should resolve a synchronous function returning a string', async () => {
11+
const value = 'syncValue';
12+
const syncFunction = () => value;
13+
const result = await resolveValueToPromise(syncFunction);
14+
expect(result).toBe(value);
15+
});
16+
17+
it('should resolve an asynchronous function returning a string', async () => {
18+
const value = 'asyncValue';
19+
const asyncFunction = async () => value;
20+
const result = await resolveValueToPromise(asyncFunction);
21+
expect(result).toBe(value);
22+
});
23+
});

test/management/token-provider-middleware.test.ts

+55
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,59 @@ describe('TokenProviderMiddleware', () => {
9999
).resolves.toMatchObject({});
100100
expect(customFetch).toHaveBeenCalled();
101101
});
102+
103+
it('should use provided access token as a string', async () => {
104+
await expect(tokenClient.testRequest({ path: '/foo', method: 'GET' })).resolves.toMatchObject(
105+
{}
106+
);
107+
expect(spy).toHaveBeenCalledWith(
108+
expect.objectContaining({
109+
authorization: 'Bearer token',
110+
})
111+
);
112+
});
113+
114+
it('should use provided access token as a sync function', async () => {
115+
const syncTokenClient = new TestClient({
116+
...opts,
117+
middleware: [
118+
new TokenProviderMiddleware({
119+
...opts,
120+
domain,
121+
token: () => 'sync-token',
122+
}),
123+
],
124+
});
125+
126+
await expect(
127+
syncTokenClient.testRequest({ path: '/foo', method: 'GET' })
128+
).resolves.toMatchObject({});
129+
expect(spy).toHaveBeenCalledWith(
130+
expect.objectContaining({
131+
authorization: 'Bearer sync-token',
132+
})
133+
);
134+
});
135+
136+
it('should use provided access token as an async function', async () => {
137+
const asyncTokenClient = new TestClient({
138+
...opts,
139+
middleware: [
140+
new TokenProviderMiddleware({
141+
...opts,
142+
domain,
143+
token: async () => 'async-token',
144+
}),
145+
],
146+
});
147+
148+
await expect(
149+
asyncTokenClient.testRequest({ path: '/foo', method: 'GET' })
150+
).resolves.toMatchObject({});
151+
expect(spy).toHaveBeenCalledWith(
152+
expect.objectContaining({
153+
authorization: 'Bearer async-token',
154+
})
155+
);
156+
});
102157
});

0 commit comments

Comments
 (0)