# node-auth0: the Management API client, split for smaller bundles

> auth0's node-auth0 package is the Node.js client for the Auth0 platform, and at version 7 it no longer ships the authentication layer at all. What remains is the Management API, and the interesting engineering in this release is the per-resource entry points and a shared token helper that let a serverless runtime import only the two clients it actually uses.

**auth0/node-auth0** — Node.js client library for the Auth0 platform.

- Repository: https://github.com/auth0/node-auth0
- Stars: 682 · Forks: 318
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/auth0-node-auth0

## v7 removed the authentication layer from the main entry point

The most consequential fact about this package is stated in the configuration section rather than in a changelog headline: as of v7, node-auth0 no longer ships AuthenticationClient in its main entrypoint. The authentication layer has been separated into a dedicated package.

The practical effect is that the two jobs this library used to do are now two dependencies. Authentication operations, which the README lists as OAuth flows, token management and user sign-up, now live in @auth0/auth0-auth-js, and the SDK documentation points at that repository for the full API reference. The code to create the client looks like this:

```js
import { AuthClient } from "@auth0/auth0-auth-js";

const auth = new AuthClient({
    domain: "{YOUR_TENANT_AND REGION}.auth0.com",
    clientId: "{YOUR_CLIENT_ID}",
    clientSecret: "{OPTIONAL_CLIENT_SECRET}",
});
```

What is left in node-auth0 is the Management API, and the boundary the README draws around it is worth quoting in spirit: the Management API is meant to be used by back-end servers or trusted parties performing administrative tasks, and generally speaking anything that can be done through the Auth0 dashboard, and more, can also be done through this API.

That is a narrower and more dangerous surface than the old combined package. There are three separate migration guides in the repository root, v5_MIGRATION_GUIDE.md, v6_MIGRATION_GUIDE.md and v7_MIGRATION_GUIDE.md, and an AUTH_MIGRATION_GUIDE.md at the top level for moving the authentication layer specifically. Four guides for a package that is now one API plus a pointer elsewhere tells you that the upgrade path has been the hard part of this library's recent life.

## The Management client, with a token or with client credentials

The Management client is initialised with a domain and a token:

```js
import { ManagementClient } from "auth0";

const management = new ManagementClient({
    domain: "{YOUR_TENANT_AND REGION}.auth0.com",
    token: "{YOUR_API_V2_TOKEN}",
});
```

Or it is initialised with client credentials, which is the version that handles token acquisition for you:

```js
import { ManagementClient } from "auth0";

const management = new ManagementClient({
    domain: "{YOUR_TENANT_AND REGION}.auth0.com",
    clientId: "{YOUR_CLIENT_ID}",
    clientSecret: "{YOUR_CLIENT_SECRET}",
    withCustomDomainHeader: "auth.example.com", // Optional: Auto-applies to whitelisted endpoints
});
```

The last option is the one to understand properly. withCustomDomainHeader is an optional setting that auto-applies to whitelisted endpoints, which means a tenant served on a custom domain like auth.example.com can be told to send the right Host header to the subset of endpoints that support it, without you having to special-case each call.

The static token form is the one to be careful with. Passing a token as a string means the token does not refresh, so a long-running back-end service that keeps a static token will start failing when the token expires, and a serverless function that reads a token from a secret store has the same problem on a cold start. The client credentials form avoids that by doing the grant for you, which is why it is the shape most production code should use.

## Per-resource entry points and a shared token helper

The feature that distinguishes this release is the ability to import a single Management resource instead of the whole client. Each resource has its own entry point, for example auth0/clients, auth0/users and auth0/connections, so a bundler ships only the resources you use. The README names the target explicitly: this keeps bundles small on size-constrained runtimes such as Cloudflare Workers.

The problem this solves is easy to underestimate. A generated client for a large REST API contains every resource, every request type and every response type, and a serverless runtime that bundles the whole thing pays for all of it in cold-start time and image size. If your function only needs to list users, importing everything to get one method is a poor trade.

The awkward part of per-resource imports is that each one needs authentication, and repeating credential handling in every call site is exactly what you do not want. The answer is createManagementAuth, exported from auth0/management. It handles the token once, either fetching and refreshing it via client credentials or accepting a static token, and returns an options object you spread into any sub-client. The token is cached and shared, so you are not re-authenticating per client:

```js
import { createManagementAuth } from "auth0/management";
import { ClientsClient } from "auth0/clients";
import { UsersClient } from "auth0/users";

// Configure auth once and reuse it across clients.
const auth = createManagementAuth({
    domain: "{YOUR_TENANT_AND_REGION}.auth0.com",
    clientId: "{YOUR_CLIENT_ID}",
    clientSecret: "{YOUR_CLIENT_SECRET}",
});

// Create each sub-client once and reuse the instances throughout your app.
export const clients = new ClientsClient(auth.clientOptions);
export const users = new UsersClient(auth.clientOptions);

await users.list({ page: 0, per_page: 10 });
```

The createManagementAuth function also accepts a static token, in which case the shape is the same minus the credential fields, so switching between the two forms does not change how the clients are constructed.

## TokenProvider when you want control over the token lifecycle

For lower-level control over the token lifecycle, TokenProvider is also exported from auth0/management. It performs the client credentials grant and caches the token until shortly before it expires:

```js
import { TokenProvider } from "auth0/management";
import { ClientsClient } from "auth0/clients";

const tokenProvider = new TokenProvider({
    domain: "{YOUR_TENANT_AND_REGION}.auth0.com",
    clientId: "{YOUR_CLIENT_ID}",
    clientSecret: "{YOUR_CLIENT_SECRET}",
    audience: "https://{YOUR_TENANT_AND_REGION}.auth0.com/api/v2/",
});

const clients = new ClientsClient({
    baseUrl: "https://{YOUR_TENANT_AND_REGION}.auth0.com/api/v2",
    token: () => tokenProvider.getAccessToken(),
});
```

The difference from createManagementAuth is where the token function lives. Here the sub-client is given a callback, token, that returns the current access token, and the provider decides when to refresh and when to hand back a cached one. The audience field is explicit, which matters when your tenant has a custom domain, because the audience that the token is issued for and the base URL the request goes to are both things you are stating rather than deriving.

Caching until shortly before expiry is the detail worth internalising. Refresh at the moment of expiry means a request that arrives after the token died fails; refreshing a little early costs one extra grant and removes that whole class of intermittent failure. Any wrapper you write around this client should keep that margin.

There is also a subtlety in how the client is wired: the token is a function, not a string. A client holding a static string cannot refresh. A client holding a function can, and the function is called per request, so the provider stays in control of the lifetime.

## Types live in a namespace so they cost nothing in the bundle

Request and response types for the sub-clients live under a shared Management namespace and are imported separately with import type:

```ts
import type { Management } from "auth0";

const body: Management.CreateClientRequestContent = { name: "My App" };
const created: Management.CreateClientResponseContent = await clients.create(body);
```

The point of the type-only import is stated in the README with the reason that matters for bundle size: because these are TypeScript interfaces, import type is erased at compile time, so importing types from the root auth0 entry adds nothing to your bundle and does not pull in the full ManagementClient.

That is a small detail with a large effect on the ergonomics of the whole approach. The temptation with per-resource imports is to avoid importing from the root entirely, and the root is where the types live. TypeScript's import type makes the root import free at runtime, so you get complete type coverage and a small bundle at the same time.

The recommendation the README gives for small bundles is to import each client as a value from its own entry point, auth0/clients, auth0/users and so on, rather than from the root. The value import is the one that costs bundle size, and the type import is the one that does not, so keeping those two categories separate is what makes the per-resource scheme pay off.

## Two release channels, and a Node version floor

Two operational details from the getting-started section will affect a production deployment more than any API shape.

The first is the release channel. Endpoints move through three maturity tiers, Generally Available, Early Access and Beta, and the package is published on two channels. The stable channel, which is what npm install auth0 gives you, includes the Generally Available and Early Access endpoints. The beta channel, npm install auth0@beta, is a superset that includes all three tiers. The consequence is stated plainly: beta prereleases can introduce breaking changes between versions, so pin an exact version if you need reproducible installs.

That is a real risk, because the beta channel is where new endpoints appear first, and a breaking change arriving in a beta version will land in your lockfile when a routine install updates it. Pinning an exact version in the beta channel, or staying on stable until the endpoints you need graduate, avoids the surprise.

The second is the runtime floor. The supported tooling versions are Node.js ^20.19.0, ^22.12.0, ^24.0.0 or ^26.0.0, which is a list of even-numbered lines with patch-level minimums. A project on Node 18 cannot use this package, and a project on Node 20 needs at least 20.19.0.

The repository reflects a generated SDK. There is a .fern/ directory and a .fernignore, and the README carries a badge saying the library is built with Fern, which is a code generation tool for API clients. The .version file, the .shiprc and the scripts/ directory are the release pipeline, and there are separate tsconfig files for the CommonJS and ES module builds. The package publishes both, with the exports map in package.json pointing at dist/cjs and dist/esm depending on whether the import or require condition matches.

## Conclusion

Adopt node-auth0 v7 if you administer an Auth0 tenant from a Node.js back end, especially on a size-constrained runtime such as Cloudflare Workers where importing the full ManagementClient is not acceptable. Do not adopt it for login flows, since the authentication layer was moved out to @auth0/auth0-auth-js and @auth0/auth0-server-js as of v7, and do not mix the stable and beta channels in one project without pinning, because beta prereleases can carry breaking changes between versions. Before you commit, read the v7 migration guide, check that your Node version satisfies the supported range, and decide between the stable channel and the beta channel based on whether the endpoints you need are Generally Available or still Early Access.

## FAQ

### Does node-auth0 still include the authentication client in v7?

No. As of v7 the package no longer ships AuthenticationClient in its main entrypoint. Authentication operations such as OAuth flows, token management and user sign-up moved to the separate @auth0/auth0-auth-js package, and node-auth0 now covers the Management API.

### How do I install the Auth0 Node.js client?

Run npm install auth0 in your project directory. The supported Node.js versions are ^20.19.0, ^22.12.0, ^24.0.0 and ^26.0.0, and there is also a beta channel installed with npm install auth0@beta.

### What is the difference between the stable and beta release channels for node-auth0?

The stable channel includes the Generally Available and Early Access endpoints, while the beta channel is a superset that also includes the Beta tier. The README warns that beta prereleases can introduce breaking changes between versions, so pin an exact version if you need reproducible installs.

### How do I keep a node-auth0 serverless bundle small?

Import each Management resource from its own entry point, such as auth0/clients and auth0/users, rather than the root, and use createManagementAuth from auth0/management to handle the token once and share it. Request and response types can still be imported from the root with import type, which is erased at compile time and adds nothing to the bundle.

### What is TokenProvider used for in node-auth0?

It is for lower-level control over the token lifecycle. It performs the client credentials grant and caches the token until shortly before it expires, and you hand the sub-client a token function that calls getAccessToken, so the provider stays in control of refreshing.

## Sources

- [auth0/node-auth0 on GitHub](https://github.com/auth0/node-auth0)
- [Issues](https://github.com/auth0/node-auth0/issues)
- [License: MIT](https://github.com/auth0/node-auth0/blob/master/LICENSE)
- [README](https://github.com/auth0/node-auth0/blob/master/README.md)
- [Releases](https://github.com/auth0/node-auth0/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/auth0-node-auth0
