Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/management/tests/unit/management-client-fetch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ManagementClient } from "../../wrapper/ManagementClient.js";

describe("ManagementClient custom fetch option", () => {
// Regression for https://github.com/auth0/node-auth0/issues/1330
// The wrapper used to `delete (_options as any).fetch`, which silently
// dropped any caller-provided custom fetch implementation even though the
// underlying `fetcherImpl` honours `fetchFn`. These tests verify the
// wrapper now forwards `fetch` all the way through to the transport.
it("should route requests through the provided custom fetch implementation", async () => {
const calls: { url: string; init?: RequestInit }[] = [];
const customFetch: typeof fetch = (async (input, init) => {
const url = typeof input === "string" ? input : (input as URL | Request).toString();
calls.push({ url, init });
return new Response(JSON.stringify({ rules: [{ id: "rul_test", name: "Test Rule", enabled: true }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}) as typeof fetch;

const client = new ManagementClient({
domain: "tenant.auth0.com",
token: "test-token",
fetch: customFetch,
});

const page = await client.rules.list();

// The custom fetch was actually used for the request...
expect(calls.length).toBeGreaterThanOrEqual(1);
expect(calls[0].url).toContain("tenant.auth0.com/api/v2/rules");
// ...and the response it returned is what the client parsed and handed back.
expect(page.data).toEqual([{ id: "rul_test", name: "Test Rule", enabled: true }]);
});

it("should not throw when the `fetch` option is omitted (defaults to global fetch)", () => {
expect(
() =>
new ManagementClient({
domain: "tenant.auth0.com",
token: "test-token",
}),
).not.toThrow();
});
});
36 changes: 29 additions & 7 deletions src/management/wrapper/ManagementClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,28 @@ export declare namespace ManagementClient {
* @public
*/
export interface ManagementClientOptions
extends Omit<FernClient.Options, "token" | "environment" | "fetcher" | "baseUrl" | "fetch"> {
extends Omit<FernClient.Options, "token" | "environment" | "fetcher" | "baseUrl"> {
/** Auth0 domain (e.g., 'your-tenant.auth0.com') */
domain: string;
/**
* Custom `fetch` implementation used for all HTTP requests.
*
* Provide your own transport when you need to control how requests are
* made — for example to route through a proxy or corporate egress, add
* retry middleware, attach OpenTelemetry instrumentation, or run on
* platforms where the global `fetch` must be swapped (Cloudflare
* Workers, Bun). Defaults to the platform's global `fetch`.
*
* @example
* ```typescript
* const client = new ManagementClient({
* domain: 'your-tenant.auth0.com',
* token: 'your-static-token',
* fetch: myInstrumentedFetch,
* });
* ```
*/
fetch?: typeof fetch;
/**
* API audience. Defaults to https://{domain}/api/v2/
* @defaultValue `https://{domain}/api/v2/`
Expand Down Expand Up @@ -180,16 +199,16 @@ export declare namespace ManagementClient {
* });
* ```
*
* @example Using custom fetcher with custom domain header (they work together)
* @example Using a custom fetch with custom domain header (they work together)
* ```typescript
* const client = new ManagementClient({
* domain: 'your-tenant.auth0.com',
* clientId: 'your-client-id',
* clientSecret: 'your-client-secret',
* withCustomDomainHeader: 'auth.example.com', // Custom domain header logic
* fetcher: async (args) => {
* console.log('Making request:', args.url); // Custom logging
* return fetch(args.url, { ...args }); // Custom fetch implementation
* fetch: (input, init) => {
* console.log('Making request:', input); // Custom logging
* return fetch(input, init); // Custom fetch implementation
* }
* });
* ```
Expand All @@ -206,9 +225,12 @@ export class ManagementClient extends FernClient {
const headers = createTelemetryHeaders(_options);
const token = createTokenSupplier(_options);

// Temporarily remove fetcher from options to avoid people passing it for now
// The underlying `fetcher` type is a Fern implementation detail and is
// intentionally kept off the public surface, so strip it here. `fetch`,
// by contrast, is supported all the way down through `fetcherImpl` and
// is a legitimate transport-customization hook, so it is left in place.
// https://github.com/auth0/node-auth0/issues/1330
delete (_options as any).fetcher;
delete (_options as any).fetch;

// Prepare the base client options
let clientOptions: any = {
Expand Down
Loading