From 216dc4b85ea2761b5fd232705a36607bae33db53 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 11:47:28 +0000 Subject: [PATCH 1/2] [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-typescript-sdk: 3.72.5 --- .shiprc | 6 - AUTH_MIGRATION_GUIDE.md | 14 - auth-migration/authentication-flows.md | 234 ------ auth-migration/index.md | 732 ------------------ auth-migration/server-side-sessions.md | 163 ---- auth-migration/troubleshooting.md | 32 - src/management/api/requests/requests.ts | 5 + src/management/api/types/types.ts | 70 ++ .../management-client-fetch-option.test.ts | 201 ----- .../tests/unit/token-provider.test.ts | 541 ------------- src/management/tests/wire/clients.test.ts | 8 + .../wire/organizations/connections.test.ts | 4 + .../tests/wire/resourceServers.test.ts | 3 + .../tests/wire/tenants/settings.test.ts | 2 + v6_MIGRATION_GUIDE.md | 158 ---- v7_MIGRATION_GUIDE.md | 146 ---- yarn.lock | 101 +-- 17 files changed, 144 insertions(+), 2276 deletions(-) delete mode 100644 .shiprc delete mode 100644 AUTH_MIGRATION_GUIDE.md delete mode 100644 auth-migration/authentication-flows.md delete mode 100644 auth-migration/index.md delete mode 100644 auth-migration/server-side-sessions.md delete mode 100644 auth-migration/troubleshooting.md delete mode 100644 src/management/tests/unit/management-client-fetch-option.test.ts delete mode 100644 src/management/tests/unit/token-provider.test.ts delete mode 100644 v6_MIGRATION_GUIDE.md delete mode 100644 v7_MIGRATION_GUIDE.md diff --git a/.shiprc b/.shiprc deleted file mode 100644 index d1791e98e5..0000000000 --- a/.shiprc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "files": { - ".version": [], - "src/management/version.ts": [] - } -} diff --git a/AUTH_MIGRATION_GUIDE.md b/AUTH_MIGRATION_GUIDE.md deleted file mode 100644 index 64626f8067..0000000000 --- a/AUTH_MIGRATION_GUIDE.md +++ /dev/null @@ -1,14 +0,0 @@ -# Authentication Migration Guide - -This guide lives in the [`auth-migration/`](./auth-migration/) directory. - -**→ Start here: [`auth-migration/index.md`](./auth-migration/index.md)**: migrate your authentication code off the `auth0` package to [`@auth0/auth0-auth-js`](https://github.com/auth0/auth0-auth-js) (stateless token grants) or [`@auth0/auth0-server-js`](https://github.com/auth0/auth0-auth-js/tree/main/packages/auth0-server-js) (server-managed sessions). - -The directory contains: - -- [`auth-migration/index.md`](./auth-migration/index.md): the main guide covering OIDC token grants and the four cross-cutting breaking changes. -- [`auth-migration/authentication-flows.md`](./auth-migration/authentication-flows.md): database, passwordless, backchannel (CIBA), token exchange, and `UserInfoClient`. -- [`auth-migration/server-side-sessions.md`](./auth-migration/server-side-sessions.md): the `@auth0/auth0-server-js` session layer. -- [`auth-migration/troubleshooting.md`](./auth-migration/troubleshooting.md): FAQ and gotchas. - -> **Migrating with an AI agent?** Point it at the Auth0 migration skill: the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`. diff --git a/auth-migration/authentication-flows.md b/auth-migration/authentication-flows.md deleted file mode 100644 index 6f7bace2a1..0000000000 --- a/auth-migration/authentication-flows.md +++ /dev/null @@ -1,234 +0,0 @@ -# Migrating the other authentication flows - -This is the incremental part of the [Authentication Migration Guide](./index.md). Start with the guide's [OIDC token grants](./index.md#oidc-token-grants) and cross-cutting breaking changes before you touch anything here. Everything below builds on those changes, so apply them to every rewrite on this page too. - -> **Migrating with an AI agent?** Point it at the Auth0 migration skill (the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`). - -Migrate one flow at a time. Only the flows your app actually uses need attention; skip the rest. - -- [Database connections](#database-connections) -- [Passwordless](#passwordless) -- [Backchannel authentication (CIBA)](#backchannel-authentication-ciba) -- [Token exchange (RFC 8693)](#token-exchange-rfc-8693) -- [UserInfoClient](#userinfoclient) -- [Quick lookup table](#quick-lookup-table) - -Unless a row routes explicitly to `@auth0/auth0-server-js`, the replacement lives on the `@auth0/auth0-auth-js` `AuthClient` (or a sub-client: `authClient.database`, `authClient.passwordless`, `authClient.mfa`, `authClient.passkey`). - -## Database connections - -Database connection operations move to the `authClient.database` sub-client. Names and required parameters stay the same; only casing and return shape change. - -### `database.signUp` → `authClient.database.signUp` - -```ts -// before -const resp = await auth0.database.signUp({ - email, - password, - connection: "Username-Password-Authentication", - given_name: "Ada", - family_name: "Lovelace", - user_metadata: { plan: "free" }, -}); -const userId = resp.data.id; -// after -const result = await authClient.database.signUp({ - email, - password, - connection: "Username-Password-Authentication", - givenName: "Ada", - familyName: "Lovelace", - userMetadata: { plan: "free" }, -}); -const userId = result.id; -``` - -> ID normalization is preserved: node-auth0 mapped the server's `_id | user_id | id` onto a single `id`. The new SDK does the same, so `result.id` is always present. Do not add your own `_id` fallback. - -### `database.changePassword` → `authClient.database.changePassword` - -node-auth0 returned a `TextApiResponse` (read via `.data`); the new SDK returns the plain `string` directly. - -```ts -// before -const resp = await auth0.database.changePassword({ email, connection: "Username-Password-Authentication" }); -const message = resp.data; // plain-text confirmation -// after -const message = await authClient.database.changePassword({ email, connection: "Username-Password-Authentication" }); -``` - -> `changePassword` requires `connection` plus at least one of `email` or `username`: either identifier is accepted, not `email` alone. - -## Passwordless - -node-auth0 lumped "start" (send the code or link) and "login" (redeem the code) onto one sub-client. The new SDK splits them: starting stays on `authClient.passwordless`; redeeming a code becomes a top-level grant method on `AuthClient`. - -### `passwordless.sendEmail` → `authClient.passwordless.sendEmail` - -```ts -// before -await auth0.passwordless.sendEmail({ email, send: "code" }); -// after -await authClient.passwordless.sendEmail({ email, send: "code" }); -``` - -> Default changed: node-auth0 defaulted `send` to `'link'` (magic link). The new SDK defaults `send` to `'code'` (one-time password). If you relied on the implicit default to send magic links, set `send: 'link'` explicitly. - -### `passwordless.sendSMS` → `authClient.passwordless.sendSms` - -Note the casing change: `sendSMS` → `sendSms`, and `phone_number` → `phoneNumber`. - -```ts -// before -await auth0.passwordless.sendSMS({ phone_number: "+15551234567" }); -// after -await authClient.passwordless.sendSms({ phoneNumber: "+15551234567" }); -``` - -### `passwordless.loginWithEmail` → `getTokenByPasswordlessEmail` - -Redeeming the one-time password is now a grant method on `AuthClient`, not on the passwordless sub-client. - -```ts -// before -const resp = await auth0.passwordless.loginWithEmail({ email, code, audience, scope }); -const token = resp.data.access_token; -// after -const tokens = await authClient.getTokenByPasswordlessEmail({ email, code, audience, scope }); -const token = tokens.accessToken; -``` - -### `passwordless.loginWithSMS` → `getTokenByPasswordlessSms` - -```ts -// before -const resp = await auth0.passwordless.loginWithSMS({ phone_number, code }); -// after -const tokens = await authClient.getTokenByPasswordlessSms({ phoneNumber, code }); -``` - -> Session apps: `@auth0/auth0-server-js` exposes `startPasswordless` / `completePasswordless` / `completePasswordlessMagicLink`, which both send the code and establish a session. Use those instead of the two-step auth-js flow when the SDK owns the session. See [Migrating session apps](./server-side-sessions.md). - -## Backchannel authentication (CIBA) - -CIBA is Client-Initiated Backchannel Authentication. - -### `backchannel.authorize` → `initiateBackchannelAuthentication` - -```ts -// before -const resp = await auth0.backchannel.authorize({ - binding_message: "ABC123", - scope: "openid", - userId: "auth0|123", -}); -const authReqId = resp.auth_req_id; -// after -const { authReqId, expiresIn, interval } = await authClient.initiateBackchannelAuthentication({ - bindingMessage: "ABC123", - loginHint: { sub: "auth0|123" }, // login_hint is an object with `sub`, not a bare string - authorizationParams: { scope: "openid" }, // scope goes here, NOT as a top-level key -}); -``` - -### `backchannel.backchannelGrant` → `backchannelAuthenticationGrant` - -```ts -// before -const resp = await auth0.backchannel.backchannelGrant({ auth_req_id: authReqId }); -// after -const tokens = await authClient.backchannelAuthenticationGrant({ authReqId }); -``` - -> One-shot convenience: `authClient.backchannelAuthentication({ ... })` initiates and polls to completion, returning a `TokenResponse`. Use it if your code did the initiate-then-poll loop by hand. -> -> Session apps: for CIBA that also establishes a session, see [Migrating session apps](./server-side-sessions.md). - -## Token exchange (RFC 8693) - -```ts -// before -const resp = await auth0.tokenExchange.exchangeToken({ - subject_token_type: "urn:example:custom", - subject_token: token, - audience: "https://api.example.com", - scope: "read", -}); -// after -const tokens = await authClient.exchangeToken({ - subjectTokenType: "urn:example:custom", - subjectToken: token, - audience: "https://api.example.com", - scope: "read", -}); -``` - -> `exchangeToken` is overloaded: a custom-exchange profile shape (`subjectTokenType` + `subjectToken` + `audience`) and a Token Vault shape (`connection` present). Presence of `connection` routes to the vault path. The custom-exchange profile is the RFC 8693 replacement for `tokenExchange.exchangeToken`. -> -> Session apps: `@auth0/auth0-server-js` exposes `loginWithCustomTokenExchange` (exchange, then establish a session) and `customTokenExchange` (exchange, then return tokens with no session). - -## UserInfoClient - -The standalone `UserInfoClient` from node-auth0 does not exist in the new SDK. Choose the replacement based on what the app needs: - -| Your intent | Replacement | -| --- | --- | -| Wanted user profile claims right after login | Read `TokenResponse.claims` from the grant result; the SDK already decodes the ID token. No extra `/userinfo` round-trip needed. **Preferred.** | -| Wanted a live `/userinfo` response for an arbitrary access token | `await authClient.getUserInfo({ accessToken })`, a direct method on `AuthClient`. | -| Wanted the profile in a server-rendered app with a session | `await serverClient.getUser()` returns the stored user claims from the session. | - -**Before (node-auth0):** - -```ts -import { UserInfoClient } from "auth0"; -const userInfo = new UserInfoClient({ domain }); -const resp = await userInfo.getUserInfo(accessToken); -const profile = resp.data; // { sub, name, email, ... } -``` - -**After (preferred): use the claims you already have:** - -```ts -const tokens = await authClient.getTokenByCode(callbackUrl, {}); -const profile = tokens.claims; // { sub, name, email, ... } decoded from the id_token -``` - -**After (direct method):** for when you only have an access token: - -```ts -// Takes an options object: { accessToken, expectedSubject? } -const profile = await authClient.getUserInfo({ accessToken }); -``` - -> Prefer reading `claims` over any `/userinfo` call: it avoids a network round-trip and the claims are already validated by the SDK. - -## Quick lookup table - -The complete node-auth0 → new SDK map, including the OIDC methods covered in the main guide. - -| node-auth0 | new SDK equivalent | Layer | -| --- | --- | --- | -| `oauth.authorizationCodeGrant` | `authClient.getTokenByCode(url, opts)` | auth-js | -| `oauth.authorizationCodeGrantWithPKCE` | `authClient.getTokenByCode(url, { codeVerifier })` | auth-js | -| `oauth.refreshTokenGrant` | `authClient.getTokenByRefreshToken({ refreshToken })` | auth-js | -| `oauth.passwordGrant` | `authClient.getTokenByPassword({ ... })` | auth-js | -| `oauth.clientCredentialsGrant` | `authClient.getTokenByClientCredentials({ audience })` | auth-js | -| `oauth.revokeRefreshToken` | `authClient.revokeToken({ token })` / `serverClient.revokeRefreshToken()` | auth-js / server-js | -| `oauth.tokenForConnection` | `authClient.exchangeToken({ connection, ... })` | auth-js | -| `oauth.pushedAuthorization` | `authClient.buildAuthorizationUrl({ pushedAuthorizationRequests: true })` | auth-js | -| `database.signUp` | `authClient.database.signUp({ ... })` | auth-js | -| `database.changePassword` | `authClient.database.changePassword({ ... })` | auth-js | -| `passwordless.sendEmail` | `authClient.passwordless.sendEmail({ ... })` | auth-js | -| `passwordless.sendSMS` | `authClient.passwordless.sendSms({ phoneNumber })` | auth-js | -| `passwordless.loginWithEmail` | `authClient.getTokenByPasswordlessEmail({ ... })` | auth-js | -| `passwordless.loginWithSMS` | `authClient.getTokenByPasswordlessSms({ ... })` | auth-js | -| `backchannel.authorize` | `authClient.initiateBackchannelAuthentication({ ... })` | auth-js | -| `backchannel.backchannelGrant` | `authClient.backchannelAuthenticationGrant({ authReqId })` | auth-js | -| `tokenExchange.exchangeToken` | `authClient.exchangeToken({ subjectTokenType, subjectToken, audience })` | auth-js | -| `UserInfoClient.getUserInfo` | `TokenResponse.claims` (preferred) / `authClient.getUserInfo({ accessToken })` / `serverClient.getUser()` | auth-js / server-js | -| (no equivalent): build `/authorize` URL | `authClient.buildAuthorizationUrl({ ... })` | auth-js | -| (no equivalent): build `/v2/logout` URL | `authClient.buildLogoutUrl({ returnTo })` | auth-js | -| `ManagementClient.*` | **not migrated, stays on `auth0`** | n/a | - -When you finish a flow, return to the [verification checklist](./index.md#verification-checklist) and confirm the four cross-cutting changes for every call site you touched. diff --git a/auth-migration/index.md b/auth-migration/index.md deleted file mode 100644 index 85a11c45c7..0000000000 --- a/auth-migration/index.md +++ /dev/null @@ -1,732 +0,0 @@ -# Authentication Migration Guide - -A guide to migrating your authentication code off the `auth0` package (node-auth0) to the modern Auth0 server SDKs: [`@auth0/auth0-auth-js`](https://github.com/auth0/auth0-auth-js) for stateless token grants, and [`@auth0/auth0-server-js`](https://github.com/auth0/auth0-auth-js/tree/main/packages/auth0-server-js) for server-managed sessions. - -> **Migrating with an AI agent?** Point it at the Auth0 migration skill first. The skill lives in [`auth0/agent-skills`](https://github.com/auth0/agent-skills) as the `auth0` skill (migration intent: `migrate-node-auth0`). It encodes the target-SDK routing, the four cross-cutting breaking changes, the method-by-method mapping, and a build-until-green verify loop. - -## Contents - -- [How to use this guide](#how-to-use-this-guide) -- [Overview](#overview) - - [Who this is for](#who-this-is-for) - - [Scope](#scope) -- [Choosing your target SDK](#choosing-your-target-sdk) -- [Prerequisites](#prerequisites) -- [Installation and constructor mapping](#installation-and-constructor-mapping) -- [OIDC token grants](#oidc-token-grants) - - [Optional: migrate only OIDC while staying on v6](#optional-migrate-only-oidc-while-staying-on-v6) -- [Cross-cutting breaking changes](#cross-cutting-breaking-changes) - - [1. Return shape](#1-return-shape) - - [2. Casing](#2-casing) - - [3. Token expiry](#3-token-expiry) - - [4. Error model](#4-error-model) -- [Verification checklist](#verification-checklist) -- [Continue the migration](#continue-the-migration) - - [Other authentication flows](#other-authentication-flows) - - [Server-side sessions](#server-side-sessions) - - [Troubleshooting](#troubleshooting) - -## How to use this guide - -This is a reference, not a linear read. You do not have to work through it top to bottom; migrate only the flows your app actually uses, in whatever order suits you. Most apps finish after the [OIDC token grants](#oidc-token-grants) section. - -The work falls into three phases: - -| Phase | What you do | Where | -| --- | --- | --- | -| **Before**: orient and set up | Pick your target SDK, check prerequisites, install the package, map constructor options. | [Choosing your target SDK](#choosing-your-target-sdk), [Prerequisites](#prerequisites), [Installation and constructor mapping](#installation-and-constructor-mapping) | -| **During**: rewrite call sites | Rewrite the OIDC token grants (in this file), then the other flows and the session layer as needed. Apply the four cross-cutting breaking changes to every call site. | [OIDC token grants](#oidc-token-grants), [Cross-cutting breaking changes](#cross-cutting-breaking-changes), [`authentication-flows.md`](./authentication-flows.md), [`server-side-sessions.md`](./server-side-sessions.md) | -| **After**: verify | Run the build-until-green checklist; confirm no residue and that `ManagementClient` code is untouched. | [Verification checklist](#verification-checklist) | - -Suggested order: start with the OIDC grants and cross-cutting changes (the whole job for most apps), then the [other flows](./authentication-flows.md) you actually use, then [session apps](./server-side-sessions.md) if you want the SDK to own sessions. Stuck? See [`troubleshooting.md`](./troubleshooting.md). - -## Overview - -node-auth0's `AuthenticationClient` is a stateless HTTP client. Every method is a single call to an Auth0 Authentication API endpoint that returns a response object. It has no notion of a logged-in user, no session, no cookie, no token store, and no automatic refresh. Anything stateful in a node-auth0 app (persisting tokens, deciding when to refresh, tracking the login across requests) was written by you *around* node-auth0. - -The modern stack splits those two concerns into two packages: - -- `@auth0/auth0-auth-js` is the stateless token layer. It is the direct successor to `AuthenticationClient`: the same "one method equals one API call equals one result" model, with modern ergonomics (camelCase, typed errors, direct return values, per-request options). -- `@auth0/auth0-server-js` is a stateful session layer built on top of auth0-auth-js. It owns the login redirect flow, a pluggable state/transaction store, cookie handling, automatic token refresh, and logout. It is the successor to the *session code you hand-rolled*, not to `AuthenticationClient` itself. - -### Who this is for - -You are running a Node.js backend that imports the `auth0` package and calls `AuthenticationClient` (or `UserInfoClient`) to perform token grants, database signup, passwordless, CIBA, token exchange, or userinfo lookups. You want to move that code to the current first-party server SDKs. This is a surgical rewrite of the authentication layer: routes, controllers, business logic, data access, and framework wiring stay as they are. You touch the smallest possible surface: the files that import and call node-auth0's Authentication API. - -### Scope - -In scope: - -- `AuthenticationClient` and its sub-clients: `.oauth`, `.database`, `.passwordless`, `.backchannel`, `.tokenExchange` -- `UserInfoClient` -- The auth error types (`AuthApiError`) and token-validation types (`IDTokenValidateOptions`, `IdTokenValidatorError`) - -Out of scope, do not touch: - -- `ManagementClient` (Management API v2). It is not being migrated and stays on the `auth0` package. -- Application routes, view/controller logic, database code, and any non-auth use of the `auth0` package. - -> If a file uses `ManagementClient`, leave that code alone. Only rewrite the `AuthenticationClient` / `UserInfoClient` parts. - -## Choosing your target SDK - -The routing question is: do you want to keep owning your session, or hand that responsibility to the SDK? - -### Decision table - -| If your code… | Migrate to | Why | -| --- | --- | --- | -| Only performs token grants / DB signup / passwordless / userinfo and manages its own session (or is a machine-to-machine service backend) | `@auth0/auth0-auth-js` | Direct, near 1:1 replacement for `AuthenticationClient`. Same stateless model. | -| Wants the SDK to own the login redirect flow, session storage, cookies, token refresh, and logout (a server-rendered web app) | `@auth0/auth0-server-js` | Adds a session layer node-auth0 never had. This is a rewrite of the session handling, not a method-for-method port. | - -**Default recommendation:** start with `@auth0/auth0-auth-js` for a faithful parity migration. Choose `@auth0/auth0-server-js` only when you currently hand-roll session/cookie/refresh logic around node-auth0 and would benefit from the SDK owning it. - -### Signals - -Signals that point to auth0-auth-js: - -- Predominant use is `clientCredentialsGrant` (machine-to-machine). There is no user, so there is no session to own. -- The app already has a session framework it is happy with and only calls node-auth0 for token grants. -- The app is an API, worker, or CLI, not a browser-facing web server. -- You want the smallest, most mechanical, lowest-risk migration. - -Signals that point to auth0-server-js: - -- The app performs a browser redirect login and reads `req.session.user` (or equivalent) on later requests. -- You wrote refresh-on-expiry logic, a token cache, or logout-with-revocation by hand. -- You use `express-openid-connect` today and want a first-party, framework-agnostic replacement. -- You are on a server framework (Express, Fastify, Hono, Next.js) and want the SDK to manage cookies. - -### Mixing both - -A single app can use both: auth0-server-js for the user-facing login/session, and auth0-auth-js directly for a separate machine-to-machine `clientCredentialsGrant` to call another API. `ServerClient` even exposes the underlying `AuthClient` via `serverClient.authClient` for occasional low-level needs. Do not force everything onto one package. - -## Prerequisites - -### Node.js version - -Both target SDKs need Node.js 20 LTS or newer. Verify the project's runtime before installing. - -### SDK versions - -- `@auth0/auth0-auth-js` >= `1.13.0` -- `@auth0/auth0-server-js` >= `1.13.0` - -Both are published on npm; install the current `latest`. `1.13.0` is the floor for the full API surface used in this guide (`getUserInfo`, per-request `RequestOptions`, and `fullResponse`). - -## Installation and constructor mapping - -Add the target package: - -```bash -# auth-js target (stateless token grants) -npm install @auth0/auth0-auth-js - -# server-js target (server-managed sessions), pulls in auth0-auth-js transitively -npm install @auth0/auth0-server-js -``` - -Keep the `auth0` package installed if the app still uses `ManagementClient`. - -### Imports - -```ts -// before -import { AuthenticationClient, UserInfoClient, AuthApiError } from "auth0"; - -// after: auth-js target -import { AuthClient, TokenByCodeError, isMfaRequiredError } from "@auth0/auth0-auth-js"; - -// after: server-js target -import { ServerClient } from "@auth0/auth0-server-js"; -``` - -> Keep the `auth0` import if the file also uses `ManagementClient`. It is correct for a file to import both `auth0` (for `ManagementClient`) and `@auth0/auth0-auth-js` (for authentication). Only remove the `auth0` import from files where it was used *solely* for `AuthenticationClient` / `UserInfoClient`. - -### AuthClient options - -The constructor options mostly carry over with camelCase names. A few are renamed or dropped. - -**Before (node-auth0):** - -```ts -new AuthenticationClient({ - domain: "tenant.us.auth0.com", - clientId: "...", - clientSecret: "...", // OR clientAssertionSigningKey - clientAssertionSigningKey: "...", - clientAssertionSigningAlg: "RS256", - idTokenSigningAlg: "RS256", // for manual id_token validation - clockTolerance: 60, // seconds, for validation - useMTLS: false, - telemetry: true, - headers: { "X-Custom": "..." }, // sent on every request - timeoutDuration: 10000, // ms - retry: { - /* ... */ - }, - agent: undiciDispatcher, - fetch: customFetch, - middleware: [ - /* ... */ - ], -}); -``` - -**After (auth0-auth-js):** - -```ts -import { AuthClient } from "@auth0/auth0-auth-js"; - -new AuthClient({ - domain: "tenant.us.auth0.com", // same (no scheme) - clientId: "...", // same - clientSecret: "...", // same - clientAssertionSigningKey: "...", // same (string | CryptoKey) - clientAssertionSigningAlg: "RS256", // same - authorizationParams: { - // NEW: default scope/audience/redirect_uri for URL builders - scope: "openid profile email", - audience: "https://api.example.com", - redirect_uri: "https://app.example.com/callback", - }, - useMtls: false, // RENAMED from useMTLS (lowercase tls) - customFetch: fetch, // RENAMED from fetch - telemetry: { - /* ... */ - }, // structured TelemetryConfig - discoveryCache: { ttl, maxEntries }, // NEW: OIDC discovery / JWKS cache -}); -``` - -Option-by-option: - -| node-auth0 | auth0-auth-js | Notes | -| --- | --- | --- | -| `domain` | `domain` | Unchanged. No `https://` scheme. | -| `clientId` | `clientId` | Unchanged. | -| `clientSecret` | `clientSecret` | Unchanged. | -| `clientAssertionSigningKey` | `clientAssertionSigningKey` | Unchanged. Now also accepts a `CryptoKey`. | -| `clientAssertionSigningAlg` | `clientAssertionSigningAlg` | Unchanged. | -| `useMTLS` | `useMtls` | Renamed (casing). | -| `fetch` | `customFetch` | Renamed. | -| `telemetry: boolean` | `telemetry: TelemetryConfig` | Now a structured object. | -| `headers` (global) | per-request `RequestOptions.headers` | Moved to per-request options; set per call site rather than globally. | -| `timeoutDuration` | per-request `RequestOptions.signal` | Use an `AbortSignal.timeout(ms)` on the call. | -| `retry` | configure via `customFetch` | Wrap your fetch with retry if needed. | -| `agent` | configure via `customFetch` | Set the dispatcher inside your custom fetch. | -| `middleware` | `customFetch` | Compose behavior in the fetch wrapper. | -| `idTokenSigningAlg` | (internal) | ID-token validation is internal; read `TokenResponse.claims`. | -| `clockTolerance` | (internal) | Handled internally during validation. | - -### ServerClient options - -`ServerClient` wraps an `AuthClient` and adds the session machinery. It shares the auth options and adds required stores. This constructor and the stores it needs are covered in [`server-side-sessions.md`](./server-side-sessions.md); reach for it only when you route to server-js. - -### Global config to per-request options - -node-auth0's global constructor options for `headers`, `timeoutDuration`, `agent`, `retry`, and `middleware` have no direct constructor equivalents in auth0-auth-js. Instead, the new SDK's methods accept a trailing `RequestOptions` parameter: - -```ts -import type { RequestOptions } from "@auth0/auth0-server-js"; // or '@auth0/auth0-auth-js' - -const tokens = await authClient.getTokenByClientCredentials( - { audience: "https://api.example.com" }, - { - headers: { "X-Custom": "value" }, - signal: AbortSignal.timeout(5000), // timeout in ms - } satisfies RequestOptions, -); -``` - -`@auth0/auth0-server-js` re-exports `RequestOptions`, `ApiResponse`, and `FullResponseOption` from `@auth0/auth0-auth-js`, so you can import any of them from either package. - -Arity rule: MFA methods (`authClient.mfa.*`) take `requestOptions` as the 2nd argument; store-first methods (session-owning methods on `serverClient`) take it as the 3rd argument after the store context; cache hits ignore it entirely. - -Common patterns: - -- Global headers: apply via `RequestOptions.headers` on each call that needs it, or wrap `customFetch` once to inject it everywhere. -- Timeout: replace `timeoutDuration: 10000` with `signal: AbortSignal.timeout(10000)` on the call. -- Agent (Node.js dispatcher): wrap `customFetch` to inject the agent into the underlying HTTP transport. -- Retry / middleware: compose behavior in a `customFetch` wrapper passed either at construction or per request. - -## OIDC token grants - -This is the core of the migration and, for most apps, the whole of it. These are the `AuthenticationClient.oauth.*` grants that drive OpenID Connect login and machine-to-machine token acquisition. All of them move onto the `AuthClient` instance directly (not a sub-client). - -Before you touch any method, internalize the four [cross-cutting breaking changes](#cross-cutting-breaking-changes); they apply to *every* rewrite here and on the incremental pages. - -Naming conventions used throughout: - -| node-auth0 | new SDKs | -| --- | --- | -| Params and response fields use the snake_case wire shape: `client_id`, `refresh_token`, `access_token`, `expires_in`, `phone_number` | camelCase: `clientId`, `refreshToken`, `accessToken`, `expiresAt`, `phoneNumber` | -| Methods take a `bodyParameters` object (+ optional `initOverrides`) | Methods take a single `options` object (+ optional trailing `RequestOptions` for per-request `signal`, `headers`, `customFetch`) | -| Every method returns a `JSONApiResponse` / `VoidApiResponse` / `TextApiResponse` wrapper | Methods return the domain object directly (`TokenResponse`, `SignUpResult`, `string`, `void`) | - -### `oauth.authorizationCodeGrant` → `getTokenByCode` - -The single most important semantic change in the whole migration. In node-auth0 you pass the raw authorization `code` (and `redirect_uri`) that you extracted from the callback query string yourself. In auth0-auth-js you pass the entire callback `URL`; the SDK extracts `code` and enforces PKCE, and `redirect_uri` comes from the `AuthClient` config / `authorizationParams`. The stateless `AuthClient` does **not** validate OAuth `state` — that is your responsibility (or use `@auth0/auth0-server-js` `completeInteractiveLogin`, which owns a transaction store and validates `state` for you). - -**Before (node-auth0):** - -```ts -import { AuthenticationClient } from "auth0"; - -const auth0 = new AuthenticationClient({ domain, clientId, clientSecret }); - -// You parsed `code` out of the callback URL yourself. -const resp = await auth0.oauth.authorizationCodeGrant({ - code, - redirect_uri: "https://app.example.com/callback", -}); -const accessToken = resp.data.access_token; -const expiresIn = resp.data.expires_in; // relative seconds -const reqId = resp.headers.get("x-request-id"); // metadata on success -``` - -**After (auth0-auth-js):** - -```ts -import { AuthClient } from "@auth0/auth0-auth-js"; - -const authClient = new AuthClient({ domain, clientId, clientSecret }); - -// `url` is a URL object for the full incoming request URL, -// e.g. new URL(req.url, `https://${req.headers.host}`) -const tokens = await authClient.getTokenByCode(url, { - // options; e.g. codeVerifier (PKCE) or organization -}); -const accessToken = tokens.accessToken; -const expiresAt = tokens.expiresAt; // absolute Unix seconds -``` - -> If your code manually parses `req.query.code`, that parsing is now the SDK's job. Delete it and hand the SDK the full URL. The SDK reads `code` from the URL and validates the PKCE verifier; it does **not** validate OAuth `state`. **Keep your existing `state` check** (compare the `state` query parameter against what you stored before the redirect) — or migrate to `@auth0/auth0-server-js` `completeInteractiveLogin`, which handles `state` validation automatically. (`getTokenByCode` options are `codeVerifier` and `organization`.) If the node-auth0 code read `resp.headers.get(...)` on success, see [Reading HTTP response metadata](#reading-http-response-metadata-fullresponse). Error-path metadata remains accessible on the typed error. - -> **Warning:** Do not delete your `state`/CSRF check when migrating to `AuthClient.getTokenByCode`. The stateless client does not validate `state`. Removing the check silently disables CSRF protection on the authorization-code flow. - -### `oauth.authorizationCodeGrantWithPKCE` → `getTokenByCode` (with verifier) - -PKCE (Proof Key for Code Exchange) is folded into the same method; supply the code verifier via options. Typically the verifier was produced earlier by `buildAuthorizationUrl` (below), which returns a `codeVerifier` for you to persist. - -```ts -// before -const resp = await auth0.oauth.authorizationCodeGrantWithPKCE({ - code, - code_verifier: verifier, - redirect_uri: "https://app.example.com/callback", -}); - -// after -const tokens = await authClient.getTokenByCode(url, { - codeVerifier: verifier, -}); -``` - -> If you build the authorization URL yourself today, prefer switching to `authClient.buildAuthorizationUrl()` (below) so the SDK generates and returns the `codeVerifier`, then persist it and pass it back to `getTokenByCode`. - -### `oauth.refreshTokenGrant` → `getTokenByRefreshToken` - -```ts -// before -const resp = await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); -// after -const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); -``` - -### `oauth.passwordGrant` → `getTokenByPassword` - -```ts -// before -const resp = await auth0.oauth.passwordGrant({ - username, - password, - realm: "Username-Password-Authentication", - audience, - scope, -}); -// after -const tokens = await authClient.getTokenByPassword({ - username, - password, - realm: "Username-Password-Authentication", - audience, - scope, -}); -``` - -### `oauth.clientCredentialsGrant` → `getTokenByClientCredentials` - -The canonical machine-to-machine grant. This is the most common reason to stay on auth0-auth-js rather than adopt server-js: there is no user session involved. - -```ts -// before -const resp = await auth0.oauth.clientCredentialsGrant({ audience: "https://api.example.com" }); -const token = resp.data.access_token; -// after -const tokens = await authClient.getTokenByClientCredentials({ audience: "https://api.example.com" }); -const token = tokens.accessToken; -``` - -### `oauth.revokeRefreshToken` → `revokeToken` - -Renamed, and simplified return (was `VoidApiResponse`, now `void`). - -```ts -// before -await auth0.oauth.revokeRefreshToken({ token: rt }); -// after -await authClient.revokeToken({ token: rt }); -``` - -> **Session apps:** if you are migrating to server-js and this revoke was part of logout, use `serverClient.revokeRefreshToken()` instead of the low-level `revokeToken`. By default it reads the refresh token from the session; you can also pass an explicit `{ token }` in its options. - -### Build the authorization and logout URLs - -node-auth0 left `/authorize` URL construction to the caller (or to `express-openid-connect`). The new SDK gives you `buildAuthorizationUrl()` and `buildLogoutUrl()`. When migrating a redirect login, replace hand-built `/authorize` and `/v2/logout` URLs with these: - -```ts -const { authorizationUrl, codeVerifier } = await authClient.buildAuthorizationUrl({ - authorizationParams: { redirect_uri, scope: "openid profile email", audience }, -}); -// ... later, on logout: -const logoutUrl = await authClient.buildLogoutUrl({ returnTo: "https://app.example.com" }); -``` - -> Pushed Authorization Requests (PAR): there is no standalone PAR method. Pass `pushedAuthorizationRequests: true` to `buildAuthorizationUrl`: the SDK performs the PAR POST and returns an authorization URL that references the resulting `request_uri`. Requires the tenant to expose a `pushed_authorization_request_endpoint`; the SDK throws if PAR is requested but unsupported. This replaces node-auth0's `oauth.pushedAuthorization`. - -Once the OIDC grants are rewritten and the [cross-cutting breaking changes](#cross-cutting-breaking-changes) are applied, run the [verification checklist](#verification-checklist). If your app also uses database, passwordless, CIBA, token exchange, or `UserInfoClient`, continue with [`authentication-flows.md`](./authentication-flows.md). If you want the SDK to own sessions, see [`server-side-sessions.md`](./server-side-sessions.md). - -### Optional: migrate only OIDC while staying on v6 - -You do not have to migrate everything at once, and you do not have to wait for v7. node-auth0 v6 still ships `AuthenticationClient` alongside `ManagementClient`, so you can move your OIDC login and token grant code off `AuthenticationClient` to `@auth0/auth0-auth-js` now, incrementally, while the rest of the app keeps using `auth0` v6 unchanged. - -A common and fully supported end state: - -- OIDC / token grant code: migrated to `@auth0/auth0-auth-js` (the grants covered in this section). -- Other auth flows you have not gotten to yet: still on `AuthenticationClient` from `auth0` v6. -- Management API: still on `ManagementClient` from `auth0` (never migrates). - -The OIDC grants above are a complete, shippable step on their own; finishing them is a valid stopping point even if you migrate nothing else. Move on to [`authentication-flows.md`](./authentication-flows.md) and [`server-side-sessions.md`](./server-side-sessions.md) later, at your own pace. When you eventually upgrade to v7 (which removes the Authentication API from the main entrypoint; see the [v7 Migration Guide](../v7_MIGRATION_GUIDE.md)), the OIDC work is already done. - -## Cross-cutting breaking changes - -Every call-site rewrite in this guide and on the incremental pages is subject to four changes that cut across all methods. They cause the overwhelming majority of migration defects, and three of the four are *silent*: the code compiles and often runs, but produces wrong behavior at runtime. Apply each one deliberately. - -1. [Return shape: `JSONApiResponse` → domain object](#1-return-shape) -2. [Casing: snake_case wire shape → camelCase](#2-casing) -3. [Token expiry: `expires_in` (relative) → `expiresAt` (absolute)](#3-token-expiry), most dangerous -4. [Error model: `AuthApiError` → typed per-operation errors](#4-error-model) - -### 1. Return shape - -node-auth0 wraps most Authentication API results in a response envelope: - -- `JSONApiResponse`: has `.data` (the payload), `.status` (number), `.statusText`, `.headers` (a `Headers` object). -- `VoidApiResponse`: same envelope, `.data` is `undefined` (used by `sendEmail`, `revokeRefreshToken`, …). -- `TextApiResponse`: `.data` is a `string` (used by `database.changePassword`). - -Exception: `backchannel.authorize`, `backchannel.backchannelGrant`, and `tokenExchange.exchangeToken` return domain objects directly (no `.data` wrapper) in node-auth0. - -The new SDKs drop the envelope and return the domain object directly: - -- Token grants return a `TokenResponse` instance. -- `database.signUp` returns a `SignUpResult` object. -- `database.changePassword` returns a `string`. -- `sendEmail` / `sendSms` / `revokeToken` return `void`. - -HTTP metadata (status code, response headers such as `x-request-id`, `retry-after`, rate-limit headers) is available through the typed error objects on failure paths. On success paths, metadata is available via the opt-in `fullResponse` envelope (see [Reading HTTP response metadata](#reading-http-response-metadata-fullresponse)). It is no longer on the bare success value by default. - -The rewrite: delete `.data` indirection on every success path: - -```ts -// before -const resp = await auth0.oauth.clientCredentialsGrant({ audience }); -const token = resp.data.access_token; -const status = resp.status; - -// after -const tokens = await authClient.getTokenByClientCredentials({ audience }); -const token = tokens.accessToken; -``` - -```ts -// before: changePassword returned TextApiResponse -const resp = await auth0.database.changePassword({ email, connection }); -console.log(resp.data); - -// after: returns the string directly -const message = await authClient.database.changePassword({ email, connection }); -console.log(message); -``` - -> `changePassword` requires `connection` plus at least one of `email` or `username`: either identifier is accepted, not `email` alone. - -#### Reading HTTP response metadata (fullResponse) - -When your node-auth0 code reads HTTP response metadata (status, headers) on a success path, migrate to the opt-in envelope rather than dropping the read. This is most common when you track rate limits, log request IDs, or check retry-after headers for dashboard telemetry. - -```ts -// before (node-auth0): metadata on the success envelope -const resp = await auth0.oauth.clientCredentialsGrant({ audience }); -const remaining = resp.headers.get("x-ratelimit-remaining"); -const token = resp.data.access_token; - -// after: opt in to the envelope, read the native Response -const { data, response } = await authClient.getTokenByClientCredentials({ audience, fullResponse: true }); -const remaining = response.headers.get("x-ratelimit-remaining"); -const token = data.accessToken; -``` - -The same opt-in covers the non-token Authentication API methods that node-auth0 wrapped in a `JSONApiResponse` / `TextApiResponse` / `VoidApiResponse`: - -| Method | Bare return | `fullResponse: true` return | -| --- | --- | --- | -| `database.signUp` | `SignUpResult` | `ApiResponse` | -| `database.changePassword` | `string` | `ApiResponse` | -| `passwordless.sendEmail` | `void` | `ApiResponse` (`data` is `undefined`) | -| `passwordless.sendSms` | `void` | `ApiResponse` (`data` is `undefined`) | - -```ts -// before (node-auth0): read the request id off the signup envelope -const resp = await auth0.database.signUp({ email, password, connection }); -const reqId = resp.headers.get("x-request-id"); - -// after: opt in to the envelope -const { data, response } = await authClient.database.signUp({ email, password, connection, fullResponse: true }); -const reqId = response.headers.get("x-request-id"); - -// void-returning methods expose the Response with an undefined `data` -const { response: sendResp } = await authClient.passwordless.sendEmail({ email, fullResponse: true }); -const rateLimit = sendResp.headers.get("x-ratelimit-remaining"); -``` - -Caveats: - -- Pass `fullResponse: true` as a literal, not a variable. Using spread (`{ ...opts, fullResponse: true }`) widens `true` to `boolean`, causing TypeScript overload resolution to fall back to the bare return type. Fix: pass `{ ...opts, fullResponse: true as const }` or include `fullResponse` as an inline literal in the options object. -- Performance: `@auth0/auth0-auth-js` does not cache tokens: every `AuthClient` grant method performs a live token-endpoint round-trip regardless of `fullResponse`, so the flag adds no extra network cost at this layer. (Token caching and reuse live in `@auth0/auth0-server-js`'s session store, not in the auth-js `AuthClient`.) The only in-memory cache in auth-js is for OIDC discovery / JWKS metadata, which is unrelated to `fullResponse`. -- Reserved headers: a caller `Authorization` header is ignored and the telemetry `Auth0-Client` header always wins; `RequestOptions.headers` cannot override them. -- Per-request `customFetch` replaces the base transport for that call but does not inherit mutual TLS (mTLS). If you rely on mTLS, the supplied fetch must itself be mTLS-capable. - -Default to the bare return type. Reach for `fullResponse` only where you actually consumed response metadata on success: rate-limit dashboards, request-id logging for support investigations, or retry-after handling. `MissingCapturedResponseError` is an internal-bug sentinel; you do not normally catch it. - -Gotchas: - -- **Void methods.** Code that did `const r = await auth0.passwordless.sendEmail(...)` and then checked `r.status === 200` must drop that check: by default the method returns `void` and throws on failure. Rely on the thrown error instead (see [Error model](#4-error-model)). -- **Header reads.** Any code reading `resp.headers.get('x-ratelimit-remaining')` on a success path needs the opt-in `fullResponse` envelope. Error paths still surface metadata on the typed error. Search your code for `.headers` on response values. -- **Do not hand-roll a compatibility shim.** Resist reintroducing a custom `{ data, status }` shape to minimize downstream diff. Let the domain object flow through; the SDK's opt-in `fullResponse` envelope is the sanctioned channel when you genuinely need the HTTP Response. - -### 2. Casing - -node-auth0's public API exposes the snake_case wire shape verbatim, on both inputs and outputs. The new SDKs use camelCase for the public API and only translate to snake_case at the HTTP boundary internally. - -Input parameters, field map: - -| node-auth0 (snake_case) | new SDK (camelCase) | -| --- | --- | -| `client_id` | `clientId` | -| `client_secret` | `clientSecret` | -| `refresh_token` | `refreshToken` | -| `redirect_uri` | (via `authorizationParams.redirect_uri` on config / builder) | -| `code_verifier` | `codeVerifier` | -| `phone_number` | `phoneNumber` | -| `auth_req_id` | `authReqId` | -| `binding_message` | `bindingMessage` | -| `subject_token` / `subject_token_type` | `subjectToken` / `subjectTokenType` | -| `given_name` / `family_name` | `givenName` / `familyName` | -| `user_metadata` | `userMetadata` | -| `login_hint` | `loginHint` | - -Output fields, `TokenResponse` field map: - -| node-auth0 `TokenSet` (snake_case) | new SDK `TokenResponse` (camelCase) | -| --- | --- | -| `access_token` | `accessToken` | -| `refresh_token` | `refreshToken` | -| `id_token` | `idToken` | -| `token_type` | `tokenType` | -| `expires_in` (relative) | `expiresAt` (absolute, see [Token expiry](#3-token-expiry)) | -| `scope` | `scope` | -| (none): had to decode id_token yourself | `claims` (already-decoded ID token claims) | -| `authorization_details` | `authorizationDetails` | - -Rename fields on both the arguments you pass in and the fields you read out: - -```ts -// before -const resp = await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); -const newRt = resp.data.refresh_token; -const idToken = resp.data.id_token; - -// after -const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); -const newRt = tokens.refreshToken; -const idToken = tokens.idToken; -``` - -> **Gotcha: keys that look renamed but are your data.** `user_metadata` → `userMetadata` is a rename of the *SDK's* parameter. The object *inside* it (e.g. `{ plan: 'free' }`) is passed through untouched. Do not rename your own metadata keys. The same applies to `authorization_details`. - -### 3. Token expiry - -**This is the highest-risk change in the migration. It is silent, it compiles, and it corrupts session lifetimes.** - -- node-auth0 `TokenSet.expires_in` = the token's lifetime in seconds relative to now (e.g. `86400` for a 24-hour token). This is the raw OAuth `expires_in` from the wire. -- new SDK `TokenResponse.expiresAt` = an absolute Unix timestamp in seconds (e.g. `1786000000`) computed by the SDK as roughly `now + expires_in`. - -Existing node-auth0 code almost always converts the relative value to an absolute deadline itself: - -```ts -// before: very common node-auth0 pattern -const resp = await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); -const expiresAtMs = Date.now() + resp.data.expires_in * 1000; // stored deadline -``` - -If you mechanically rename `expires_in` → `expiresAt` and leave the arithmetic, you get: - -```ts -// WRONG: double-counts "now" -const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); -const expiresAtMs = Date.now() + tokens.expiresAt * 1000; // ~ now + (now + lifetime) → far future -``` - -The stored deadline lands decades in the future, so the token is treated as valid long after it has actually expired. The app does not refresh it, so production 401s follow. - -The rewrite: `expiresAt` is *already* the deadline. Do not add `Date.now()`: - -```ts -// after: correct -const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); -const expiresAtMs = tokens.expiresAt * 1000; // absolute; convert s → ms only if you store ms -``` - -If downstream code genuinely needs the *relative* remaining lifetime (e.g. to set a cookie `Max-Age`), compute it from the absolute value: - -```ts -const secondsRemaining = tokens.expiresAt - Math.floor(Date.now() / 1000); -``` - -To find every instance, grep your code for these patterns and inspect each by hand: - -- `expires_in` -- `Date.now() +` near a token result -- `+ expires` / `* 1000` near a token result -- any stored field named `expiresAt`, `expires_at`, `expiry`, `tokenExpiry` fed from a grant - -Every one of these is a candidate for the double-count bug. - -> **Session apps get this for free.** If you migrate to server-js, the SDK owns expiry math inside `getAccessToken`. Delete your `Date.now() + expires_in * 1000` bookkeeping entirely. - -### 4. Error model - -node-auth0 throws a single error type for Authentication API failures: - -```ts -class AuthApiError extends Error { - name: "AuthApiError"; - error: string; // OAuth error code, e.g. 'invalid_grant' - error_description: string; - statusCode: number; - body: string; - headers: Headers; -} -``` - -The new SDKs throw typed, operation-specific error classes: `TokenByCodeError`, `TokenByRefreshTokenError`, `TokenByClientCredentialsError`, `TokenByPasswordError`, `TokenExchangeError`, `TokenRevocationError`, `PasswordlessStartError`, `PasswordlessChallengeError`, `PasswordlessDbGetTokenError`, `MfaEnrollmentError`, and so on. Each carries a structured `.cause` (the underlying OAuth2 error) rather than flat `error` / `error_description` strings. - -The rewrite: generic catch: - -```ts -// before -try { - await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); -} catch (e) { - if (e instanceof AuthApiError && e.error === "invalid_grant") { - // refresh token revoked/expired - } -} - -// after -import { TokenByRefreshTokenError } from "@auth0/auth0-auth-js"; -try { - await authClient.getTokenByRefreshToken({ refreshToken: rt }); -} catch (e) { - if (e instanceof TokenByRefreshTokenError && e.cause?.error === "invalid_grant") { - // refresh token revoked/expired - } -} -``` - -Import the specific error class for the operation you are calling. If you had one broad `catch (e instanceof AuthApiError)` around several different operations, either widen to catch each operation's error type or check the shared base behavior. Prefer the specific type per call site, since it documents which operation can fail. - -#### MFA detection: use the type guard, not the string - -Multi-factor authentication (MFA). A very common node-auth0 pattern is detecting `mfa_required` by string comparison to route the user into an MFA challenge: - -```ts -// before -try { - await auth0.oauth.passwordGrant({ username, password }); -} catch (e) { - if (e instanceof AuthApiError && e.error === "mfa_required") { - // start MFA flow using e (mfa_token is in the body) - } -} -``` - -The new SDK provides `isMfaRequiredError()`, a type guard that narrows the error and gives typed access to the MFA context (including the `mfa_token`). Use it instead of matching the string: - -```ts -// after -import { isMfaRequiredError } from "@auth0/auth0-auth-js"; -try { - await authClient.getTokenByPassword({ username, password }); -} catch (e) { - if (isMfaRequiredError(e)) { - // e is narrowed; drive the MFA challenge via authClient.mfa.* - } -} -``` - -> After detecting `mfa_required`, the MFA enroll/challenge/verify flow that node-auth0 handled ad hoc now lives on `authClient.mfa.*` (`listAuthenticators`, `enrollAuthenticator`, `challengeAuthenticator`, `verify`, and `deleteAuthenticator`). In server-js, `serverClient.mfa.verify()` also persists the resulting tokens to the session. - -#### ID-token validation types - -node-auth0 exposed `IDTokenValidateOptions` and `IdTokenValidatorError` for callers doing manual ID-token validation. The new SDK validates ID tokens internally during grants and exposes the decoded, validated result as `TokenResponse.claims`. Replace manual validation: - -- Options like `organization`, `nonce`, `maxAge` are passed to the grant call (e.g. `getTokenByCode`), and the SDK validates them and throws a typed error on mismatch, so you no longer construct a validator or catch `IdTokenValidatorError` yourself. -- Read the validated claims from `TokenResponse.claims` instead of decoding the `id_token` string. - -## Verification checklist - -The migration is not complete until every check passes in a single pass. For every node-auth0 auth call you rewrote (here or on the incremental pages), confirm all four cross-cutting changes: - -- [ ] **Return shape**: removed `.data` / `.status` / `.headers` access on the success path. -- [ ] **Casing**: renamed every snake_case field on input args and output reads to camelCase. -- [ ] **Expiry**: any code using the old `expires_in` now uses `expiresAt` as an *absolute* timestamp; no `Date.now() +` was left in front of it. -- [ ] **Errors**: `AuthApiError` catches replaced with the specific typed error (`.cause.error`); `mfa_required` string checks replaced with `isMfaRequiredError()`. - -Then run the project gates and repeat the whole loop if any step fails: - -- [ ] Grep for residue: unmigrated `from 'auth0'` auth imports, `.data.` reads on auth responses, and relative `expires_in` arithmetic. -- [ ] `tsc --noEmit`: catches structural mismatches and type errors. -- [ ] `npm test` (or the project's test command): confirms behavior is preserved. -- [ ] Run the linter if the project has one configured. -- [ ] Confirm files that use `ManagementClient` still import and call it from `auth0`; that code must be untouched. - -Do not declare the migration complete until the loop converges: all steps pass in a single iteration. - -## Continue the migration - -Once the OIDC grants and the four cross-cutting changes are in, migrate the rest at your own pace. Each area lives in its own page. - -### Other authentication flows - -Database signup, passwordless, backchannel (CIBA), token exchange, and `UserInfoClient` lookups: see [`authentication-flows.md`](./authentication-flows.md). - -### Server-side sessions - -Routing to `@auth0/auth0-server-js`, where the SDK owns the login redirect flow, session storage, cookies, token refresh, and logout: see [`server-side-sessions.md`](./server-side-sessions.md). - -### Troubleshooting - -Common questions and failure modes (tokens valid for decades, missing `resp.data`, magic-link default flip, `getUserInfo`, `mfa_required` detection, global config): see [`troubleshooting.md`](./troubleshooting.md). diff --git a/auth-migration/server-side-sessions.md b/auth-migration/server-side-sessions.md deleted file mode 100644 index d040e2986b..0000000000 --- a/auth-migration/server-side-sessions.md +++ /dev/null @@ -1,163 +0,0 @@ -# Migrating session apps to `@auth0/auth0-server-js` - -This page is part of the [Authentication Migration Guide](./index.md). Read it only when you are routing to **`@auth0/auth0-server-js`**: when you want the SDK to own the login redirect flow, session storage, cookies, token refresh, and logout, instead of hand-rolling that around node-auth0. If you only need stateless token grants, stay on the main guide and [`authentication-flows.md`](./authentication-flows.md); you do not need this page. - -> **Migrating with an AI agent?** Point it at the Auth0 migration skill (the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`). It walks the session lifecycle step by step. - -**This is a rewrite of the session handling, not a method-for-method port.** node-auth0 had no session concept, so there is nothing to translate line-for-line. Instead you *replace* your existing session code (your `express-session` wiring, your token cache, your refresh-on-expiry logic, your logout handler) with the ServerClient lifecycle. You still touch only the auth/session code; routes, views, and business logic stay put. - -- [Mental model](#mental-model) -- [Store setup](#store-setup) -- [The redirect-login lifecycle](#the-redirect-login-lifecycle) -- [Logins without a browser redirect](#logins-without-a-browser-redirect) -- [Backchannel logout](#backchannel-logout) - -## Mental model - -A ServerClient login has three durable pieces: - -1. **Transaction store**: short-lived. Holds the in-flight login: the OAuth `state` and the PKCE (Proof Key for Code Exchange) `code_verifier` between the moment you redirect the user to Auth0 and the moment they come back to your callback. Created at `startInteractiveLogin`, consumed at `completeInteractiveLogin`. -2. **State store**: long-lived. Holds the established session: the user claims plus the access / refresh / ID tokens and their absolute expiry. Read on every subsequent request via `getUser`, `getSession`, `getAccessToken`. -3. **Cookies**: how the two stores key themselves to the browser. With a *stateless* store the session data lives encrypted in the cookie itself; with a *stateful* store the cookie holds only an identifier and the data lives in your backend (Redis, database, and so on). - -node-auth0 exposed none of this; you built equivalents by hand. You are swapping your implementation for the SDK's. - -## Store setup - -`@auth0/auth0-server-js` ships store base classes and cookie-backed implementations: - -- `CookieTransactionStore`: transaction store backed entirely by a cookie. Good default. -- `StatelessStateStore`: session lives encrypted in the cookie. No server-side storage; good for serverless or horizontally-scaled deployments with small sessions. -- `StatefulStateStore`: session lives server-side; the cookie holds an id. Use for large sessions or when you need server-side revocation. -- `AbstractTransactionStore` / `AbstractStateStore`: extend these to back a store with your own storage (Redis, Postgres, and so on). These are the exported base-class names. - -All stores accept a `CookieHandler` so they can integrate with any framework's cookie API. The `storeOptions` generic (`TStoreOptions`) is how you thread per-request context (like the framework `req` / `res`) into store reads and writes; every ServerClient method takes an optional trailing `storeOptions` argument for exactly this. - -```ts -import { ServerClient, CookieTransactionStore, StatelessStateStore } from "@auth0/auth0-server-js"; - -const serverClient = new ServerClient({ - domain: process.env.AUTH0_DOMAIN!, - clientId: process.env.AUTH0_CLIENT_ID!, - clientSecret: process.env.AUTH0_CLIENT_SECRET!, - authorizationParams: { - redirect_uri: "https://app.example.com/callback", - scope: "openid profile email offline_access", // offline_access ⇒ refresh token - audience: "https://api.example.com", - }, - transactionStore: new CookieTransactionStore( - { secret: process.env.SESSION_SECRET! }, - cookieHandler, // CookieHandler implementation - ), - stateStore: new StatelessStateStore( - { secret: process.env.SESSION_SECRET! }, - cookieHandler, // CookieHandler implementation - ), -}); -``` - -## The redirect-login lifecycle - -### 1. Start login: replace the hand-built `/authorize` redirect - -Whatever you did to send the user to Auth0 (a hand-constructed `/authorize` URL, or `express-openid-connect`'s `/login`) becomes: - -```ts -// GET /login -app.get("/login", async (req, res) => { - const authorizationUrl = await serverClient.startInteractiveLogin( - { - authorizationParams: { - /* optional per-login overrides */ - }, - appState: { returnTo: req.query.returnTo || "/" }, // seed appState for round-trip - }, - { req, res }, // storeOptions: lets the transaction store write its cookie - ); - res.redirect(authorizationUrl.href); -}); -``` - -`startInteractiveLogin` generates `state` and PKCE, writes them to the transaction store, and returns the fully-formed authorization URL. - -### 2. Complete login: replace the manual code exchange - -The callback handler that used to call `oauth.authorizationCodeGrant` (or `authorizationCodeGrantWithPKCE`) and then stuff tokens into the session becomes a single call: - -```ts -// GET /callback -app.get("/callback", async (req, res) => { - const callbackUrl = new URL(req.url, `https://${req.headers.host}`); - const { appState } = await serverClient.completeInteractiveLogin(callbackUrl, { req, res }); - // Session is now established in the state store. Tokens are NOT your concern anymore. - res.redirect(appState?.returnTo ?? "/"); -}); -``` - -`completeInteractiveLogin` validates `state`, exchanges the code, validates the ID token, writes the session (user + tokens + absolute expiry) to the state store, and clears the transaction. - -### 3. Read the user or session on later requests - -Replace `req.session.user` reads: - -```ts -const user = await serverClient.getUser({ req, res }); // user claims, or undefined -const session = await serverClient.getSession({ req, res }); // full session data, or undefined -``` - -`getUser` / `getSession` return `undefined` when there is no session or it has expired (the store deletes expired sessions on read), so use that as your "not logged in" signal. - -### 4. Get an access token to call an API: refresh is automatic - -Replace your manual "is the token expired? if so refresh" block: - -```ts -const { accessToken } = await serverClient.getAccessToken({ req, res }); -// If the stored access token is expired and a refresh token exists, -// the SDK refreshes and persists the new tokens transparently. -``` - -This is where the `expires_in` → `expiresAt` hazard disappears entirely: the SDK owns expiry math. For a downstream federated connection token (Token Vault), use `serverClient.getAccessTokenForConnection({ connection }, { req, res })`. - -### 5. Logout: replace manual revoke, session clear, and `/v2/logout` redirect - -```ts -// GET /logout -app.get("/logout", async (req, res) => { - const logoutUrl = await serverClient.logout({ returnTo: "https://app.example.com" }, { req, res }); - res.redirect(logoutUrl.href); -}); -``` - -`logout` clears the session from the state store and returns the Auth0 `/v2/logout` URL. If you also revoked the refresh token on logout (via `oauth.revokeRefreshToken`), call `serverClient.revokeRefreshToken({ req, res })` before redirecting; by default it reads the refresh token from the session, so you do not handle the raw token yourself (it also accepts an explicit `{ token }` if you need to revoke a specific one). - -## Logins without a browser redirect - -Some logins do not use a browser redirect: the password grant, passwordless, CIBA, and custom token exchange. If you used node-auth0 for one of these *and* want a server-js session out of it, use the ServerClient methods that both authenticate and write the session, rather than the low-level auth-js grants: - -| Flow | ServerClient method | -| --- | --- | -| Backchannel / CIBA | `loginBackchannel({ ... }, storeOptions)` | -| Passwordless (send) | `startPasswordless({ connection, email \| phoneNumber, ... }, storeOptions)` | -| Passwordless (verify code → session) | `completePasswordless({ connection, email \| phoneNumber, verificationCode }, storeOptions)` | -| Passwordless magic link (callback → session) | `completePasswordlessMagicLink(url, storeOptions)` | -| Custom token exchange → session | `loginWithCustomTokenExchange({ ... }, storeOptions)` | -| MFA verify → session | `serverClient.mfa.verify({ ... }, storeOptions)` | - -Each of these performs the underlying grant *and* persists the resulting tokens to the state store, so the user is logged in afterward, exactly the behavior you previously wrote by hand after a node-auth0 grant. - -## Backchannel logout - -If you implemented an Auth0 back-channel logout endpoint by hand (validating the logout token, then clearing your session store), replace it with: - -```ts -// POST /backchannel-logout -app.post("/backchannel-logout", async (req, res) => { - await serverClient.handleBackchannelLogout(req.body.logout_token, { req, res }); - res.sendStatus(204); -}); -``` - -It validates the logout token and clears the corresponding session. - -When the session layer is wired, return to the [verification checklist](./index.md#verification-checklist) in the main guide. diff --git a/auth-migration/troubleshooting.md b/auth-migration/troubleshooting.md deleted file mode 100644 index 334a37a698..0000000000 --- a/auth-migration/troubleshooting.md +++ /dev/null @@ -1,32 +0,0 @@ -# Troubleshooting: FAQ and gotchas - -Common questions and failure modes when migrating off the `auth0` package's Authentication API. This page is part of the [Authentication Migration Guide](./index.md); it assumes the terms defined there. - -> **Migrating with an AI agent?** Point it at the Auth0 migration skill (the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`). - -### Do I have to migrate everything at once? -No. The OIDC / token-grant work is a complete, shippable step on its own. You can stay on `auth0` v6 and migrate only OIDC, leaving other auth flows on `AuthenticationClient` for now. See [Optional: migrate only OIDC while staying on v6](./index.md#optional-migrate-only-oidc-while-staying-on-v6). - -### Do I have to migrate the Management API too? -No. `ManagementClient` is out of scope and stays on the `auth0` package. A file importing both `auth0` (for management) and `@auth0/auth0-auth-js` (for authentication) is correct. - -### auth0-auth-js or auth0-server-js: which do I pick? -Default to auth0-auth-js for a low-risk parity migration. Pick auth0-server-js only when you want the SDK to own the login redirect flow, session storage, cookies, refresh, and logout. See [Choosing your target SDK](./index.md#choosing-your-target-sdk). - -### My tokens suddenly look valid for decades. What happened? -You almost certainly left `Date.now() +` in front of `expiresAt`. `expiresAt` is already an absolute Unix timestamp, not a relative lifetime. See [Token expiry](./index.md#3-token-expiry). - -### Where did `resp.data` go? -The new SDKs return the domain object directly. Read `tokens.accessToken`, not `resp.data.access_token`. If you truly need HTTP response metadata on a success path, opt into `fullResponse`. - -### My magic-link passwordless flow stopped sending links. -The `send` default changed from `'link'` (node-auth0) to `'code'` (new SDK). Set `send: 'link'` explicitly if you want magic links. See [Passwordless](./authentication-flows.md#passwordless). - -### Where is `getUserInfo`? -Prefer `TokenResponse.claims`; they are already decoded and validated, with no extra round-trip. For an arbitrary access token, use `authClient.getUserInfo({ accessToken })`. In a session app, use `serverClient.getUser()`. See [UserInfoClient](./authentication-flows.md#userinfoclient). - -### Can I still set a global `headers` / `timeout` / `agent` on the client? -Not on the constructor. Move them to the per-call `RequestOptions` argument (`headers`, `signal: AbortSignal.timeout(ms)`) or wrap `customFetch`. - -### How do I detect `mfa_required` now? -Use the `isMfaRequiredError()` type guard, not a string comparison. It narrows the error and exposes the `mfa_token`. Drive the challenge via `authClient.mfa.*`. See [Error model](./index.md#4-error-model). diff --git a/src/management/api/requests/requests.ts b/src/management/api/requests/requests.ts index 48677f4749..6aa4b57cad 100644 --- a/src/management/api/requests/requests.ts +++ b/src/management/api/requests/requests.ts @@ -1555,6 +1555,7 @@ export interface CreateResourceServerRequestContent { skip_consent_for_verifiable_first_party_clients?: boolean; /** Whether to enforce authorization policies (true) or to ignore them (false). */ enforce_policies?: boolean; + access_token?: Management.ResourceServerAccessToken | null; token_encryption?: Management.ResourceServerTokenEncryption | null; consent_policy?: Management.ResourceServerConsentPolicyEnum | null; authorization_details?: unknown[] | null; @@ -1630,6 +1631,7 @@ export interface UpdateResourceServerRequestContent { token_dialect?: Management.ResourceServerTokenDialectSchemaEnum; /** Whether authorization policies are enforced (true) or not enforced (false). */ enforce_policies?: boolean; + access_token?: Management.ResourceServerAccessToken | null; token_encryption?: Management.ResourceServerTokenEncryption | null; consent_policy?: Management.ResourceServerConsentPolicyEnum | null; authorization_details?: unknown[] | null; @@ -3604,6 +3606,7 @@ export interface CreateOrganizationAllConnectionRequestParameters { /** Determines whether organization signup should be enabled for this organization connection. Only applicable for database connections. Default: false. */ is_signup_enabled?: boolean; organization_access_level?: Management.OrganizationAccessLevelEnum; + organization_member_access_level?: Management.OrganizationMemberAccessLevelEnum; /** Whether the connection is enabled for the organization. */ is_enabled?: boolean; /** Connection identifier. */ @@ -3624,6 +3627,7 @@ export interface UpdateOrganizationConnectionRequestParameters { /** Determines whether organization signup should be enabled for this organization connection. Only applicable for database connections. Default: false. */ is_signup_enabled?: boolean; organization_access_level?: Management.OrganizationAccessLevelEnumWithNull | null; + organization_member_access_level?: Management.OrganizationMemberAccessLevelEnumWithNull | null; /** Whether the connection is enabled for the organization. */ is_enabled?: boolean | null; } @@ -4260,6 +4264,7 @@ export interface UpdateTenantSettingsRequestContent { default_redirection_uri?: string; /** Supported locales for the user interface */ enabled_locales?: Management.TenantSettingsSupportedLocalesEnum[]; + access_token?: Management.ResourceServerAccessToken | null; security_headers?: Management.TenantSettingsNullableSecurityHeaders | null; session_cookie?: Management.SessionCookieSchema | null; sessions?: Management.TenantSettingsSessions | null; diff --git a/src/management/api/types/types.ts b/src/management/api/types/types.ts index 8cc83e7259..99ac7edcbc 100644 --- a/src/management/api/types/types.ts +++ b/src/management/api/types/types.ts @@ -3068,6 +3068,10 @@ export interface ClientMyOrganizationPatchConfiguration { connection_deletion_behavior: Management.ClientMyOrganizationDeletionBehaviorEnum; /** The client ID this client uses while creating invitations through My Organization API. */ invitation_landing_client_id?: string | undefined; + /** When true, limits the permissions that organization admins can assign to members to only those held by the admin themselves. */ + enforce_permission_ceiling?: boolean | undefined; + /** When true, prevents organization admins from assigning permissions to themselves. */ + enforce_self_assignment_restriction?: boolean | undefined; } /** @@ -3084,6 +3088,10 @@ export interface ClientMyOrganizationPostConfiguration { connection_deletion_behavior: Management.ClientMyOrganizationDeletionBehaviorEnum; /** The client ID this client uses while creating invitations through My Organization API. */ invitation_landing_client_id?: string | undefined; + /** When true, limits the permissions that organization admins can assign to members to only those held by the admin themselves. */ + enforce_permission_ceiling?: boolean | undefined; + /** When true, prevents organization admins from assigning permissions to themselves. */ + enforce_self_assignment_restriction?: boolean | undefined; } /** @@ -3100,6 +3108,10 @@ export interface ClientMyOrganizationResponseConfiguration { connection_deletion_behavior: Management.ClientMyOrganizationDeletionBehaviorEnum; /** The client ID this client uses while creating invitations through My Organization API. */ invitation_landing_client_id?: string | undefined; + /** When true, limits the permissions that organization admins can assign to members to only those held by the admin themselves. */ + enforce_permission_ceiling?: boolean | undefined; + /** When true, prevents organization admins from assigning permissions to themselves. */ + enforce_self_assignment_restriction?: boolean | undefined; } /** @@ -10257,6 +10269,7 @@ export interface CreateOrganizationAllConnectionResponseContent { /** Determines whether organization signup should be enabled for this organization connection. Only applicable for database connections. Default: false. */ is_signup_enabled?: boolean | undefined; organization_access_level?: Management.OrganizationAccessLevelEnum | undefined; + organization_member_access_level?: Management.OrganizationMemberAccessLevelEnum | undefined; /** Whether the connection is enabled for the organization. */ is_enabled?: boolean | undefined; /** Connection identifier. */ @@ -10407,6 +10420,7 @@ export interface CreateResourceServerResponseContent { /** Expiration value (in seconds) for anonymous-session access tokens issued for this API. */ token_lifetime_for_anonymous_access_tokens?: number | undefined; token_dialect?: Management.ResourceServerTokenDialectResponseEnum | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; authorization_details?: (unknown[] | null) | undefined; @@ -32607,6 +32621,7 @@ export interface GetOrganizationAllConnectionResponseContent { /** Determines whether organization signup should be enabled for this organization connection. Only applicable for database connections. Default: false. */ is_signup_enabled?: boolean | undefined; organization_access_level?: Management.OrganizationAccessLevelEnum | undefined; + organization_member_access_level?: Management.OrganizationMemberAccessLevelEnum | undefined; /** Whether the connection is enabled for the organization. */ is_enabled?: boolean | undefined; /** Connection identifier. */ @@ -32827,6 +32842,7 @@ export interface GetResourceServerResponseContent { /** Expiration value (in seconds) for anonymous-session access tokens issued for this API. */ token_lifetime_for_anonymous_access_tokens?: number | undefined; token_dialect?: Management.ResourceServerTokenDialectResponseEnum | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; authorization_details?: (unknown[] | null) | undefined; @@ -33037,6 +33053,7 @@ export interface GetTenantSettingsResponseContent { default_redirection_uri?: string | undefined; /** Supported locales for the user interface. */ enabled_locales?: Management.SupportedLocales[] | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; security_headers?: (Management.TenantSettingsNullableSecurityHeaders | null) | undefined; session_cookie?: (Management.SessionCookieSchema | null) | undefined; sessions?: (Management.TenantSettingsSessions | null) | undefined; @@ -35062,6 +35079,7 @@ export interface OrganizationAllConnectionPost { /** Determines whether organization signup should be enabled for this organization connection. Only applicable for database connections. Default: false. */ is_signup_enabled?: boolean | undefined; organization_access_level?: Management.OrganizationAccessLevelEnum | undefined; + organization_member_access_level?: Management.OrganizationMemberAccessLevelEnum | undefined; /** Whether the connection is enabled for the organization. */ is_enabled?: boolean | undefined; /** Connection identifier. */ @@ -35256,6 +35274,26 @@ export interface OrganizationMember { roles?: Management.OrganizationMemberRole[] | undefined; } +/** Access level for the organization member (e.g., "none", "full"). */ +export const OrganizationMemberAccessLevelEnum = { + None: "none", + Readonly: "readonly", + Limited: "limited", + Full: "full", +} as const; +export type OrganizationMemberAccessLevelEnum = + (typeof OrganizationMemberAccessLevelEnum)[keyof typeof OrganizationMemberAccessLevelEnum]; + +/** Access level for the organization member (e.g., "none", "full"). */ +export const OrganizationMemberAccessLevelEnumWithNull = { + None: "none", + Readonly: "readonly", + Limited: "limited", + Full: "full", +} as const; +export type OrganizationMemberAccessLevelEnumWithNull = + (typeof OrganizationMemberAccessLevelEnumWithNull)[keyof typeof OrganizationMemberAccessLevelEnumWithNull]; + export interface OrganizationMemberEffectiveRole { /** Role ID */ id: string; @@ -36075,6 +36113,7 @@ export interface ResourceServer { /** Expiration value (in seconds) for anonymous-session access tokens issued for this API. */ token_lifetime_for_anonymous_access_tokens?: number | undefined; token_dialect?: Management.ResourceServerTokenDialectResponseEnum | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; authorization_details?: (unknown[] | null) | undefined; @@ -36085,6 +36124,33 @@ export interface ResourceServer { client_id?: string | undefined; } +/** + * Custom configuration for access tokens + */ +export interface ResourceServerAccessToken { + claims_mapping?: Management.ResourceServerAccessTokenClaimsMapping | undefined; +} + +/** + * Custom configuration for claims in access tokens + */ +export interface ResourceServerAccessTokenClaimsMapping { + custom_claims?: Management.ResourceServerAccessTokenCustomClaimsMapping | undefined; +} + +/** + * Custom claims to emit in anonymous-session access tokens. Each rule maps a value read from the anonymous-session context (via a restricted dot-path expression) onto a named access-token claim. + */ +export type ResourceServerAccessTokenCustomClaimsMapping = + Management.ResourceServerAccessTokenCustomClaimsMappingRule[]; + +export interface ResourceServerAccessTokenCustomClaimsMappingRule { + /** The access-token claim name to emit, stored with the casing you provide. Reserved OIDC/JWT claim names are not allowed (compared case-insensitively). */ + name: string; + /** Restricted dot-path expression read from the anonymous-session context (e.g. `anonymous_session.metadata.country`). */ + expression: string; +} + /** * Authorization policy for the resource server. */ @@ -36161,6 +36227,7 @@ export interface ResourceServerSearchResponse { /** Expiration value (in seconds) for anonymous-session access tokens issued for this API. */ token_lifetime_for_anonymous_access_tokens?: number | undefined; token_dialect?: Management.ResourceServerTokenDialectResponseEnum | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; authorization_details?: (unknown[] | null) | undefined; @@ -39137,6 +39204,7 @@ export interface UpdateOrganizationAllConnectionResponseContent { /** Determines whether organization signup should be enabled for this organization connection. Only applicable for database connections. Default: false. */ is_signup_enabled?: boolean | undefined; organization_access_level?: Management.OrganizationAccessLevelEnum | undefined; + organization_member_access_level?: Management.OrganizationMemberAccessLevelEnum | undefined; /** Whether the connection is enabled for the organization. */ is_enabled?: boolean | undefined; /** Connection identifier. */ @@ -39274,6 +39342,7 @@ export interface UpdateResourceServerResponseContent { /** Expiration value (in seconds) for anonymous-session access tokens issued for this API. */ token_lifetime_for_anonymous_access_tokens?: number | undefined; token_dialect?: Management.ResourceServerTokenDialectResponseEnum | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; authorization_details?: (unknown[] | null) | undefined; @@ -39443,6 +39512,7 @@ export interface UpdateTenantSettingsResponseContent { default_redirection_uri?: string | undefined; /** Supported locales for the user interface. */ enabled_locales?: Management.SupportedLocales[] | undefined; + access_token?: (Management.ResourceServerAccessToken | null) | undefined; security_headers?: (Management.TenantSettingsNullableSecurityHeaders | null) | undefined; session_cookie?: (Management.SessionCookieSchema | null) | undefined; sessions?: (Management.TenantSettingsSessions | null) | undefined; diff --git a/src/management/tests/unit/management-client-fetch-option.test.ts b/src/management/tests/unit/management-client-fetch-option.test.ts deleted file mode 100644 index d9262d8e92..0000000000 --- a/src/management/tests/unit/management-client-fetch-option.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -// Mock problematic ES modules before importing ManagementClient -jest.mock("jose", () => ({ - __esModule: true, - default: {}, - jwtVerify: jest.fn(), - SignJWT: jest.fn(), - importPKCS8: jest.fn(), - importSPKI: jest.fn(), - createRemoteJWKSet: jest.fn().mockReturnValue(jest.fn()), - base64url: { - encode: (str: string) => { - return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); - }, - decode: (str: string) => { - let paddedStr = str.replace(/-/g, "+").replace(/_/g, "/"); - while (paddedStr.length % 4) { - paddedStr += "="; - } - return Buffer.from(paddedStr, "base64").toString(); - }, - }, -})); - -jest.mock("uuid", () => ({ - v4: jest.fn(() => "test-uuid"), -})); - -// NOTE: We do NOT mock ../../core/index.js here. We supply a custom `fetch` -// implementation directly in ManagementClient options so the real Fern fetcher -// pipeline runs but our mock function is used for the actual HTTP call. - -import { ManagementClient } from "../../wrapper/ManagementClient.js"; - -const DOMAIN = "test-tenant.auth0.com"; -const TOKEN = "test-token"; - -describe("ManagementClient custom fetch option", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it("invokes the custom fetch function when making a Management API request", async () => { - const myFetchMock = jest.fn().mockResolvedValue( - new Response(JSON.stringify({ users: [], length: 0 }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - - const client = new ManagementClient({ - domain: DOMAIN, - token: TOKEN, - fetch: myFetchMock as unknown as typeof fetch, - }); - - // Call a simple GET endpoint — users.list() issues GET /api/v2/users - await client.users.list(); - - expect(myFetchMock).toHaveBeenCalled(); - }); - - it("calls the custom fetch with a URL that contains the configured domain", async () => { - const myFetchMock = jest.fn().mockResolvedValue( - new Response(JSON.stringify({ users: [], length: 0 }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - - const client = new ManagementClient({ - domain: DOMAIN, - token: TOKEN, - fetch: myFetchMock as unknown as typeof fetch, - }); - - await client.users.list(); - - // The first argument to the fetch call should be the URL string - const [calledUrl] = myFetchMock.mock.calls[0] as [string, RequestInit]; - expect(calledUrl).toContain(DOMAIN); - }); - - it("uses the supplied mTLS fetch for Management API requests when useMTLS is set", async () => { - // Client-credentials mode issues two calls through the supplied fetch: - // (1) the token request to the oauth endpoint, (2) the api/v2 request. - // Branch on URL so the token call gets a valid access_token and the - // api/v2 call gets a users payload. - const myFetchMock = jest.fn((url: string) => { - if (String(url).includes("/oauth/token")) { - return Promise.resolve( - new Response(JSON.stringify({ access_token: "mtls-token", expires_in: 3600 }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - } - return Promise.resolve( - new Response(JSON.stringify({ users: [], length: 0 }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - }); - - const client = new ManagementClient({ - domain: DOMAIN, - clientId: "test-client-id", - clientSecret: "test-client-secret", - useMTLS: true, - fetch: myFetchMock as unknown as typeof fetch, - }); - - await client.users.list(); - - // The mTLS-capable fetch must actually be invoked for the api/v2 call, - // otherwise the client certificate is never presented on the request. - const apiCall = myFetchMock.mock.calls.find(([u]) => String(u).includes("/api/v2")); - expect(apiCall).toBeDefined(); - expect(String(apiCall![0])).toContain(`${DOMAIN}/api/v2`); - - // The token request must go to the mtls. subdomain (RFC 8705 mTLS-bound token endpoint). - const oauthCall = myFetchMock.mock.calls.find(([u]) => String(u).includes("/oauth/token")); - expect(oauthCall).toBeDefined(); - expect(String(oauthCall![0])).toContain(`mtls.${DOMAIN}`); - - // The token returned by the mTLS exchange must be forwarded as the Authorization header - // on the subsequent api/v2 call, proving end-to-end token propagation. - // Fern passes a Headers instance (not a plain object) as the headers field. - const [, apiInit] = apiCall as unknown as [string, RequestInit]; - const apiHeaders = apiInit.headers as unknown as Headers; - expect(apiHeaders.get("Authorization")).toBe("Bearer mtls-token"); - }); -}); - -describe("ManagementClient construction guard — useMTLS without fetch", () => { - it("throws at construction when useMTLS is set without a custom fetch", () => { - expect( - () => - new ManagementClient({ - domain: DOMAIN, - clientId: "test-client-id", - clientSecret: "test-client-secret", - useMTLS: true, - // fetch intentionally omitted — must throw before any network call - }), - ).toThrow("useMTLS requires a custom fetch implementation"); - }); -}); - -describe("ManagementClient silently drops user-supplied fetcher option", () => { - it("does not invoke a user-supplied fetcher and falls through to the custom fetch", async () => { - const fetcherMock = jest.fn(); - const myFetchMock = jest.fn().mockResolvedValue( - new Response(JSON.stringify({ users: [], length: 0 }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - - // fetcher is deleted by ManagementClient before being passed to Fern core to - // prevent callers from bypassing SDK internals. Verify it is never invoked. - const client = new ManagementClient({ - domain: DOMAIN, - token: TOKEN, - fetch: myFetchMock as unknown as typeof fetch, - fetcher: fetcherMock, - } as any); - - await client.users.list(); - - expect(fetcherMock).not.toHaveBeenCalled(); - expect(myFetchMock).toHaveBeenCalled(); - }); -}); - -describe("ManagementClient telemetry:false omits Auth0-Client on api/v2 requests", () => { - it("does not send the Auth0-Client header on api/v2 calls when telemetry is disabled", async () => { - const myFetchMock = jest.fn().mockResolvedValue( - new Response(JSON.stringify({ users: [], length: 0 }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - - const client = new ManagementClient({ - domain: DOMAIN, - token: TOKEN, - telemetry: false, - fetch: myFetchMock as unknown as typeof fetch, - }); - - await client.users.list(); - - const [, init] = myFetchMock.mock.calls[0] as [string, RequestInit]; - // Fern passes a Headers instance; Headers.get() is case-insensitive per spec. - const headers = init.headers as unknown as Headers; - // createTelemetryHeaders skips the Auth0-Client header when telemetry === false. - expect(headers.get("Auth0-Client")).toBeNull(); - expect(headers.get("auth0-client")).toBeNull(); - }); -}); diff --git a/src/management/tests/unit/token-provider.test.ts b/src/management/tests/unit/token-provider.test.ts deleted file mode 100644 index 39ed69a7af..0000000000 --- a/src/management/tests/unit/token-provider.test.ts +++ /dev/null @@ -1,541 +0,0 @@ -import { jest } from "@jest/globals"; - -// Mock jose BEFORE imports -const mockImportPKCS8 = jest.fn<() => Promise<{ type: string }>>().mockResolvedValue({ type: "fake-key" }); -const mockSign = jest.fn<() => Promise>().mockResolvedValue("mock-client-assertion-jwt"); - -const MockSignJWT = jest.fn().mockImplementation(() => ({ - setProtectedHeader: jest.fn().mockReturnThis(), - setIssuedAt: jest.fn().mockReturnThis(), - setIssuer: jest.fn().mockReturnThis(), - setSubject: jest.fn().mockReturnThis(), - setAudience: jest.fn().mockReturnThis(), - setExpirationTime: jest.fn().mockReturnThis(), - setJti: jest.fn().mockReturnThis(), - sign: mockSign, -})); - -jest.mock("jose", () => ({ - importPKCS8: mockImportPKCS8, - SignJWT: MockSignJWT, - base64url: { - encode: (input: Uint8Array | string) => { - const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input; - return Buffer.from(bytes).toString("base64url"); - }, - }, -})); - -// NOW import TokenProvider (after mock setup) -import { TokenProvider } from "../../wrapper/token-provider.js"; -import { ManagementError } from "../../errors/ManagementError.js"; - -const DOMAIN = "test-domain.auth0.com"; -const TOKEN_URL = `https://${DOMAIN}/oauth/token`; -const AUDIENCE = `https://${DOMAIN}/api/v2/`; - -/** Build a minimal Response-like object that satisfies the TokenProvider fetch contract */ -function makeOkResponse(body: { access_token: string; expires_in: number }) { - return { - ok: true, - status: 200, - statusText: "OK", - json: async () => body, - text: async () => JSON.stringify(body), - } as unknown as Response; -} - -function makeErrorResponse(status: number, errorCode: string, description: string) { - const body = JSON.stringify({ error: errorCode, error_description: description }); - return { - ok: false, - status, - statusText: description, - json: async () => JSON.parse(body), - text: async () => body, - } as unknown as Response; -} - -describe("TokenProvider (raw fetch + jose)", () => { - const opts = { - domain: DOMAIN, - clientId: "test-client-id", - clientSecret: "test-client-secret", - audience: AUDIENCE, - }; - - let fetchSpy: jest.MockedFunction; - - beforeEach(() => { - fetchSpy = jest - .spyOn(globalThis, "fetch") - .mockImplementation(() => - Promise.reject(new Error("fetch not mocked for this test")), - ) as unknown as jest.MockedFunction; - mockImportPKCS8.mockClear(); - mockSign.mockClear(); - MockSignJWT.mockClear(); - }); - - afterEach(() => { - jest.restoreAllMocks(); - jest.useRealTimers(); - }); - - describe("TC-2.1 — Token Acquired (Client-Secret)", () => { - it("should get an access token with client-secret credentials", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "mock-access-token", expires_in: 86400 })); - - const tp = new TokenProvider(opts); - const token = await tp.getAccessToken(); - - expect(token).toBe("mock-access-token"); - expect(fetchSpy).toHaveBeenCalledTimes(1); - expect(fetchSpy).toHaveBeenCalledWith(TOKEN_URL, expect.objectContaining({ method: "POST" })); - - // Verify body contains correct client_secret params - const callBody = (fetchSpy.mock.calls[0][1] as RequestInit).body as string; - const params = new URLSearchParams(callBody); - expect(params.get("grant_type")).toBe("client_credentials"); - expect(params.get("client_id")).toBe(opts.clientId); - expect(params.get("client_secret")).toBe(opts.clientSecret); - expect(params.get("audience")).toBe(opts.audience); - }); - }); - - describe("TC-2.2 — Token Acquired (Client-Assertion)", () => { - it("should get an access token with client-assertion credentials", async () => { - const optsAssertion = { - domain: DOMAIN, - clientId: "test-client-id", - clientAssertionSigningKey: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBg...", - clientAssertionSigningAlg: "RS256" as const, - audience: AUDIENCE, - }; - - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "mock-assertion-token", expires_in: 3600 })); - - const tp = new TokenProvider(optsAssertion); - const token = await tp.getAccessToken(); - - expect(token).toBe("mock-assertion-token"); - expect(mockImportPKCS8).toHaveBeenCalledWith( - optsAssertion.clientAssertionSigningKey, - optsAssertion.clientAssertionSigningAlg, - ); - expect(MockSignJWT).toHaveBeenCalled(); - expect(mockSign).toHaveBeenCalled(); - - const callBody = (fetchSpy.mock.calls[0][1] as RequestInit).body as string; - const params = new URLSearchParams(callBody); - expect(params.get("client_assertion")).toBe("mock-client-assertion-jwt"); - expect(params.get("client_assertion_type")).toBe("urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); - }); - }); - - describe("TC-2.3 — Cache Hit", () => { - it("should return cached token on second call within validity", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "cached-token", expires_in: 3600 })); - - const tp = new TokenProvider(opts); - const token1 = await tp.getAccessToken(); - const token2 = await tp.getAccessToken(); - - expect(token1).toBe("cached-token"); - expect(token2).toBe("cached-token"); - expect(fetchSpy).toHaveBeenCalledTimes(1); // single request - }); - }); - - describe("TC-2.4 — Leeway Refresh", () => { - it("should refresh token when within 10s of expiry (leeway)", async () => { - const originalDateNow = Date.now; - let currentTime = 1000000000000; // Fixed start time in ms - Date.now = jest.fn(() => currentTime); - - // First response: expires in 3600s - fetchSpy - .mockResolvedValueOnce(makeOkResponse({ access_token: "token-1", expires_in: 3600 })) - .mockResolvedValueOnce(makeOkResponse({ access_token: "token-2", expires_in: 3600 })); - - const tp = new TokenProvider(opts); - - // First call - const token1 = await tp.getAccessToken(); - expect(token1).toBe("token-1"); - - // Advance time to 5s before expiry (within 10s LEEWAY) - // expiresAt = currentTime + 3600 * 1000; LEEWAY check: Date.now() > expiresAt - 10000 - currentTime += (3600 - 5) * 1000; - - const token2 = await tp.getAccessToken(); - expect(token2).toBe("token-2"); - expect(fetchSpy).toHaveBeenCalledTimes(2); - - Date.now = originalDateNow; - }); - }); - - describe("TC-2.5 — In-Flight Dedup", () => { - it("should deduplicate concurrent calls to single request", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "shared-token", expires_in: 3600 })); - - const tp = new TokenProvider(opts); - - const [token1, token2, token3] = await Promise.all([ - tp.getAccessToken(), - tp.getAccessToken(), - tp.getAccessToken(), - ]); - - expect(token1).toBe("shared-token"); - expect(token2).toBe("shared-token"); - expect(token3).toBe("shared-token"); - expect(fetchSpy).toHaveBeenCalledTimes(1); // single request for 3 concurrent calls - }); - }); - - describe("TC-2.6 — Error Path", () => { - it("should throw ManagementError on non-2xx response", async () => { - fetchSpy.mockResolvedValue(makeErrorResponse(401, "invalid_client", "Client authentication failed")); - - const tp = new TokenProvider(opts); - - const err = await tp.getAccessToken().catch((e) => e); - expect(err).toBeInstanceOf(ManagementError); - expect(err.statusCode).toBe(401); - expect(fetchSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe("TC-2.7 — Error Not Cached", () => { - it("should retry after failed request (no error caching)", async () => { - fetchSpy - .mockResolvedValueOnce(makeErrorResponse(500, "server_error", "Internal Server Error")) - .mockResolvedValueOnce(makeOkResponse({ access_token: "retry-success-token", expires_in: 3600 })); - - const tp = new TokenProvider(opts); - - // First call fails - const err = await tp.getAccessToken().catch((e) => e); - expect(err).toBeInstanceOf(ManagementError); - expect(err.statusCode).toBe(500); - - // Second call succeeds - const token = await tp.getAccessToken(); - - expect(token).toBe("retry-success-token"); - expect(fetchSpy).toHaveBeenCalledTimes(2); // retry issued - }); - }); - - describe("TC-2.8 — Token Expired", () => { - it("should refresh token after expiry", async () => { - const originalDateNow = Date.now; - let currentTime = 1000000000000; - Date.now = jest.fn(() => currentTime); - - fetchSpy - .mockResolvedValueOnce(makeOkResponse({ access_token: "token-1", expires_in: 86400 })) - .mockResolvedValueOnce(makeOkResponse({ access_token: "token-2", expires_in: 86400 })); - - const tp = new TokenProvider(opts); - const token1 = await tp.getAccessToken(); - - // Advance time by 1 day + 20s (beyond expiry + LEEWAY) - currentTime += (86400 + 20) * 1000; - - const token2 = await tp.getAccessToken(); - - expect(token1).toBe("token-1"); - expect(token2).toBe("token-2"); - expect(fetchSpy).toHaveBeenCalledTimes(2); - - Date.now = originalDateNow; - }); - }); - - describe("TC-2.9 — mTLS: customFetch forwarded when fetch provided", () => { - it("should call the custom fetch function when useMTLS=true and fetch is provided", async () => { - const mockCustomFetch = jest - .fn() - .mockResolvedValue(makeOkResponse({ access_token: "mtls-token", expires_in: 3600 })); - - const mtlsOpts = { - domain: DOMAIN, - clientId: "test-client-id", - clientSecret: "test-client-secret", - audience: AUDIENCE, - useMTLS: true, - fetch: mockCustomFetch, - }; - - const tp = new TokenProvider(mtlsOpts as any); - const token = await tp.getAccessToken(); - - expect(token).toBe("mtls-token"); - // Custom fetch was called, not the global one - expect(mockCustomFetch).toHaveBeenCalledTimes(1); - expect(mockCustomFetch).toHaveBeenCalledWith( - `https://mtls.${DOMAIN}/oauth/token`, - expect.objectContaining({ method: "POST" }), - ); - // Global fetch should NOT have been called - expect(fetchSpy).not.toHaveBeenCalled(); - }); - }); - - describe("TC-2.10 — mTLS: throw at construction when no fetch provided", () => { - it("should throw a descriptive error at construction time when useMTLS=true and fetch is absent", () => { - const mtlsOptsNoFetch = { - domain: DOMAIN, - clientId: "test-client-id", - clientSecret: "test-client-secret", - audience: AUDIENCE, - useMTLS: true, - // no fetch - }; - - expect(() => new TokenProvider(mtlsOptsNoFetch as any)).toThrow( - "ManagementClient: useMTLS requires a custom fetch implementation.", - ); - // Global fetch should NOT have been called (error thrown at construction) - expect(fetchSpy).not.toHaveBeenCalled(); - }); - }); - - describe("TC-2.11 — domain validation: throw at construction for invalid domain", () => { - it("should throw when domain contains a slash", () => { - expect(() => new TokenProvider({ ...opts, domain: "tenant.auth0.com/path" } as any)).toThrow( - /invalid domain/, - ); - }); - - it("should throw when domain contains a query string", () => { - expect(() => new TokenProvider({ ...opts, domain: "tenant.auth0.com?foo=bar" } as any)).toThrow( - /invalid domain/, - ); - }); - }); - - describe("TC-2.12 — mTLS + clientAssertion: allowed (RFC 8705 cert-bound token)", () => { - it("should NOT throw when both useMTLS and clientAssertionSigningKey are provided, and hit the mtls alias", async () => { - // mTLS is a transport-layer concern independent of the auth method. private_key_jwt with a - // TLS client certificate is a valid RFC 8705 setup and was supported in v6, so it must not throw. - const mockCustomFetch = jest - .fn() - .mockResolvedValue(makeOkResponse({ access_token: "mtls-assertion-token", expires_in: 3600 })); - - const tp = new TokenProvider({ - domain: DOMAIN, - clientId: "test-client-id", - clientAssertionSigningKey: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBg...", - clientAssertionSigningAlg: "RS256" as const, - audience: AUDIENCE, - useMTLS: true, - fetch: mockCustomFetch, - } as any); - - const token = await tp.getAccessToken(); - - expect(token).toBe("mtls-assertion-token"); - // Certificate-bound token issued via the mTLS alias, authenticating with client_assertion. - expect(mockCustomFetch).toHaveBeenCalledWith( - `https://mtls.${DOMAIN}/oauth/token`, - expect.objectContaining({ method: "POST" }), - ); - const callBody = (mockCustomFetch.mock.calls[0][1] as RequestInit).body as string; - const params = new URLSearchParams(callBody); - expect(params.get("client_assertion")).toBe("mock-client-assertion-jwt"); - }); - }); - - describe("TC-2.13 — custom headers forwarded to token request", () => { - it("should forward plain-string headers from options.headers to the token fetch", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "header-token", expires_in: 3600 })); - - const tp = new TokenProvider({ - ...opts, - headers: { - "User-Agent": "my-app/1.0", - "X-Custom": "value", - }, - } as any); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - expect(callHeaders["user-agent"]).toBe("my-app/1.0"); - expect(callHeaders["x-custom"]).toBe("value"); - // SDK headers still present - expect(callHeaders["content-type"]).toBe("application/x-www-form-urlencoded"); - }); - - it("should silently skip supplier-function headers", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "header-token", expires_in: 3600 })); - - const tp = new TokenProvider({ - ...opts, - headers: { - "User-Agent": "my-app/1.0", - "X-Supplier": () => "dynamic-value", - }, - } as any); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - expect(callHeaders["user-agent"]).toBe("my-app/1.0"); - expect(callHeaders["x-supplier"]).toBeUndefined(); - }); - - it("SDK-controlled headers should override user-supplied headers with same name", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "header-token", expires_in: 3600 })); - - const tp = new TokenProvider({ - ...opts, - headers: { - "Content-Type": "text/plain", // should be overridden - }, - } as any); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - expect(callHeaders["content-type"]).toBe("application/x-www-form-urlencoded"); - }); - }); - - describe("TC-2.14 — header case normalization: lowercase user key overridden by SDK", () => { - it("should normalize lowercase user header key and let SDK value win", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "norm-token", expires_in: 3600 })); - - const tp = new TokenProvider({ - ...opts, - headers: { "content-type": "text/plain" }, // lowercase, SDK must win - } as any); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - const ctKeys = Object.keys(callHeaders).filter((k) => k.toLowerCase() === "content-type"); - expect(ctKeys).toHaveLength(1); - expect(callHeaders[ctKeys[0]]).toBe("application/x-www-form-urlencoded"); - }); - }); - - describe("TC-2.15 — typed error carries statusCode and OAuth error body", () => { - it("should throw ManagementError with statusCode and body.error on 401", async () => { - fetchSpy.mockResolvedValue(makeErrorResponse(401, "invalid_client", "Client authentication failed.")); - - const tp = new TokenProvider(opts); - const err = await tp.getAccessToken().catch((e) => e); - - expect(err).toBeInstanceOf(ManagementError); - expect(err.statusCode).toBe(401); - expect((err.body as { error: string }).error).toBe("invalid_client"); - expect((err.body as { error_description: string }).error_description).toBe("Client authentication failed."); - }); - }); - - describe("TC-2.16 — token request timeout throws ManagementError", () => { - it("should throw ManagementError with statusCode 408 on AbortSignal timeout (TimeoutError)", async () => { - const abortError = Object.assign(new Error("The operation was aborted."), { - name: "TimeoutError", - }); - fetchSpy.mockRejectedValue(abortError); - - const tp = new TokenProvider(opts); - const err = await tp.getAccessToken().catch((e) => e); - - expect(err).toBeInstanceOf(ManagementError); - expect(err.statusCode).toBe(408); - }); - - it("should throw ManagementError with statusCode 408 when the custom fetch aborts (AbortError)", async () => { - // node-fetch (mTLS path) rejects with an AbortError, not a TimeoutError. - const abortError = Object.assign(new Error("The operation was aborted."), { - name: "AbortError", - }); - fetchSpy.mockRejectedValue(abortError); - - const tp = new TokenProvider(opts); - const err = await tp.getAccessToken().catch((e) => e); - - expect(err).toBeInstanceOf(ManagementError); - expect(err.statusCode).toBe(408); - }); - }); - - describe("TC-2.17 — non-JSON error body preserved and message set", () => { - it("should keep the raw text body when the error response is not JSON", async () => { - const nonJson = { - ok: false, - status: 502, - statusText: "Bad Gateway", - json: async () => { - throw new Error("Unexpected token < in JSON"); - }, - text: async () => "502 Bad Gateway", - } as unknown as Response; - fetchSpy.mockResolvedValue(nonJson); - - const tp = new TokenProvider(opts); - const err = await tp.getAccessToken().catch((e) => e); - - expect(err).toBeInstanceOf(ManagementError); - expect(err.statusCode).toBe(502); - expect(err.body).toBe("502 Bad Gateway"); - expect(err.message).toContain("token request failed"); - }); - }); - - // Helper: decode the Auth0-Client telemetry header (base64url JSON). - // The jose mock's base64url.encode uses the real Buffer base64url encoding, - // so Buffer decode round-trips it. - function decodeTelemetry(header: string): { name: string; version: string } { - return JSON.parse(Buffer.from(header, "base64url").toString()); - } - - describe("TC-2.18 — telemetry:false omits Auth0-Client header", () => { - it("should not send the auth0-client header when telemetry is disabled", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "no-telemetry-token", expires_in: 3600 })); - - const tp = new TokenProvider({ ...opts, telemetry: false } as any); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - expect(callHeaders["auth0-client"]).toBeUndefined(); - }); - }); - - describe("TC-2.19 — clientInfo override sets custom name/version in Auth0-Client header", () => { - it("should encode the supplied clientInfo name and version", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "client-info-token", expires_in: 3600 })); - - const tp = new TokenProvider({ - ...opts, - clientInfo: { name: "my-custom-sdk", version: "9.9.9" }, - } as any); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - expect(callHeaders["auth0-client"]).toBeDefined(); - const decoded = decodeTelemetry(callHeaders["auth0-client"]); - expect(decoded.name).toBe("my-custom-sdk"); - expect(decoded.version).toBe("9.9.9"); - }); - }); - - describe("TC-2.20 — default Auth0-Client header carries node-auth0 identity JSON", () => { - it("should send a decodable auth0-client header with name and version", async () => { - fetchSpy.mockResolvedValue(makeOkResponse({ access_token: "default-telemetry-token", expires_in: 3600 })); - - const tp = new TokenProvider(opts); - await tp.getAccessToken(); - - const callHeaders = (fetchSpy.mock.calls[0][1] as RequestInit).headers as Record; - expect(callHeaders["auth0-client"]).toBeDefined(); - const decoded = decodeTelemetry(callHeaders["auth0-client"]); - expect(decoded.name).toBe("node-auth0"); - expect(typeof decoded.version).toBe("string"); - expect(decoded.version.length).toBeGreaterThan(0); - }); - }); -}); diff --git a/src/management/tests/wire/clients.test.ts b/src/management/tests/wire/clients.test.ts index 23fada2c79..682e9a1db9 100644 --- a/src/management/tests/wire/clients.test.ts +++ b/src/management/tests/wire/clients.test.ts @@ -400,6 +400,8 @@ describe("ClientsClient", () => { third_party_client_access: { default_value: "block", allowed_values: ["allow"] }, connection_deletion_behavior: "allow", invitation_landing_client_id: "invitation_landing_client_id", + enforce_permission_ceiling: true, + enforce_self_assignment_restriction: true, }, identity_assertion_authorization_grant: { active: true }, anonymous_sessions: { active: true }, @@ -1067,6 +1069,8 @@ describe("ClientsClient", () => { third_party_client_access: { default_value: "block", allowed_values: ["allow"] }, connection_deletion_behavior: "allow", invitation_landing_client_id: "invitation_landing_client_id", + enforce_permission_ceiling: true, + enforce_self_assignment_restriction: true, }, identity_assertion_authorization_grant: { active: true }, anonymous_sessions: { active: true }, @@ -1426,6 +1430,8 @@ describe("ClientsClient", () => { third_party_client_access: { default_value: "block", allowed_values: ["allow"] }, connection_deletion_behavior: "allow", invitation_landing_client_id: "invitation_landing_client_id", + enforce_permission_ceiling: true, + enforce_self_assignment_restriction: true, }, identity_assertion_authorization_grant: { active: true }, anonymous_sessions: { active: true }, @@ -1762,6 +1768,8 @@ describe("ClientsClient", () => { third_party_client_access: { default_value: "block", allowed_values: ["allow"] }, connection_deletion_behavior: "allow", invitation_landing_client_id: "invitation_landing_client_id", + enforce_permission_ceiling: true, + enforce_self_assignment_restriction: true, }, identity_assertion_authorization_grant: { active: true }, anonymous_sessions: { active: true }, diff --git a/src/management/tests/wire/organizations/connections.test.ts b/src/management/tests/wire/organizations/connections.test.ts index 45032aadd9..ea28010d3d 100644 --- a/src/management/tests/wire/organizations/connections.test.ts +++ b/src/management/tests/wire/organizations/connections.test.ts @@ -20,6 +20,7 @@ describe("ConnectionsClient", () => { show_as_button: true, is_signup_enabled: true, organization_access_level: "none", + organization_member_access_level: "none", is_enabled: true, connection_id: "connection_id", }, @@ -134,6 +135,7 @@ describe("ConnectionsClient", () => { show_as_button: true, is_signup_enabled: true, organization_access_level: "none", + organization_member_access_level: "none", is_enabled: true, connection_id: "connection_id", connection: { name: "name", strategy: "strategy" }, @@ -296,6 +298,7 @@ describe("ConnectionsClient", () => { show_as_button: true, is_signup_enabled: true, organization_access_level: "none", + organization_member_access_level: "none", is_enabled: true, connection_id: "connection_id", connection: { name: "name", strategy: "strategy" }, @@ -471,6 +474,7 @@ describe("ConnectionsClient", () => { show_as_button: true, is_signup_enabled: true, organization_access_level: "none", + organization_member_access_level: "none", is_enabled: true, connection_id: "connection_id", connection: { name: "name", strategy: "strategy" }, diff --git a/src/management/tests/wire/resourceServers.test.ts b/src/management/tests/wire/resourceServers.test.ts index 2480df1697..2efd98c292 100644 --- a/src/management/tests/wire/resourceServers.test.ts +++ b/src/management/tests/wire/resourceServers.test.ts @@ -163,6 +163,7 @@ describe("ResourceServersClient", () => { enforce_policies: true, token_lifetime_for_anonymous_access_tokens: 1, token_dialect: "access_token", + access_token: { claims_mapping: { custom_claims: [{ name: "name", expression: "expression" }] } }, token_encryption: { format: "compact-nested-jwe", encryption_key: { name: "name", alg: "RSA-OAEP-256", kid: "kid", pem: "pem" }, @@ -518,6 +519,7 @@ describe("ResourceServersClient", () => { enforce_policies: true, token_lifetime_for_anonymous_access_tokens: 1, token_dialect: "access_token", + access_token: { claims_mapping: { custom_claims: [{ name: "name", expression: "expression" }] } }, token_encryption: { format: "compact-nested-jwe", encryption_key: { name: "name", alg: "RSA-OAEP-256", kid: "kid", pem: "pem" }, @@ -750,6 +752,7 @@ describe("ResourceServersClient", () => { enforce_policies: true, token_lifetime_for_anonymous_access_tokens: 1, token_dialect: "access_token", + access_token: { claims_mapping: { custom_claims: [{ name: "name", expression: "expression" }] } }, token_encryption: { format: "compact-nested-jwe", encryption_key: { name: "name", alg: "RSA-OAEP-256", kid: "kid", pem: "pem" }, diff --git a/src/management/tests/wire/tenants/settings.test.ts b/src/management/tests/wire/tenants/settings.test.ts index 0ea5264fb0..9abbd3f850 100644 --- a/src/management/tests/wire/tenants/settings.test.ts +++ b/src/management/tests/wire/tenants/settings.test.ts @@ -63,6 +63,7 @@ describe("SettingsClient", () => { sandbox_versions_available: ["sandbox_versions_available"], default_redirection_uri: "default_redirection_uri", enabled_locales: ["am"], + access_token: { claims_mapping: { custom_claims: [{ name: "name", expression: "expression" }] } }, security_headers: { content_security_policy: { enabled: true, policies: [{}] }, x_xss_protection: { enabled: true, mode: "block", report_uri: "report_uri" }, @@ -208,6 +209,7 @@ describe("SettingsClient", () => { sandbox_versions_available: ["sandbox_versions_available"], default_redirection_uri: "default_redirection_uri", enabled_locales: ["am"], + access_token: { claims_mapping: { custom_claims: [{ name: "name", expression: "expression" }] } }, security_headers: { content_security_policy: { enabled: true, policies: [{}] }, x_xss_protection: { enabled: true, mode: "block", report_uri: "report_uri" }, diff --git a/v6_MIGRATION_GUIDE.md b/v6_MIGRATION_GUIDE.md deleted file mode 100644 index d1eb6bd699..0000000000 --- a/v6_MIGRATION_GUIDE.md +++ /dev/null @@ -1,158 +0,0 @@ -# V6 Migration Guide - -A guide to migrating the Auth0 Node.js SDK from `5.x` to `6.x`. - -- [Overall changes](#overall-changes) -- [Breaking changes](#breaking-changes) - - [ConnectionAttributeIdentifier replaced with identifier-specific types](#connectionattributeidentifier-replaced-with-identifier-specific-types) - - [PhoneProviderProtectionBackoffStrategyEnum value change](#phoneproviderprotectionbackoffstrategyenum-value-change) - - [users.federatedConnectionsTokensets removed](#usersfederatedconnectionstokensets-removed) - - [federated_connections_access_tokens removed from connection options](#federated_connections_access_tokens-removed-from-connection-options) - -## Overall changes - -V6 addresses type correctness for database connection attribute identifiers, aligns the phone provider backoff strategy enum with the updated API, and removes the federated connections tokensets user sub-client. There are no changes to the Authentication API — any code written for the Authentication API in `5.x` will continue to work in `6.x`. - -## Breaking changes - -### ConnectionAttributeIdentifier replaced with identifier-specific types - -In v5, all three attribute identifiers (email, phone number, and username) shared a single `ConnectionAttributeIdentifier` type for their `identifier` field. This was incorrect — each identifier type supports different values for `default_method`. - -In v6, `ConnectionAttributeIdentifier` has been removed and replaced with three separate types: - -| Attribute | Old type | New type | `default_method` values | -| -------------- | ------------------------------- | ----------------------------- | ----------------------------- | -| `email` | `ConnectionAttributeIdentifier` | `EmailAttributeIdentifier` | `"password"` \| `"email_otp"` | -| `phone_number` | `ConnectionAttributeIdentifier` | `PhoneAttributeIdentifier` | `"password"` \| `"phone_otp"` | -| `username` | `ConnectionAttributeIdentifier` | `UsernameAttributeIdentifier` | _(no `default_method`)_ | - -**Before (v5):** - -```ts -import { Management } from "auth0"; - -const identifier: Management.ConnectionAttributeIdentifier = { - active: true, - default_method: "email_otp", -}; -``` - -**After (v6):** - -```ts -import { Management } from "auth0"; - -// For email attribute -const emailIdentifier: Management.EmailAttributeIdentifier = { - active: true, - default_method: "email_otp", -}; - -// For phone_number attribute -const phoneIdentifier: Management.PhoneAttributeIdentifier = { - active: true, - default_method: "phone_otp", -}; - -// For username attribute (no default_method) -const usernameIdentifier: Management.UsernameAttributeIdentifier = { - active: true, -}; -``` - -If you were using `ConnectionAttributeIdentifier` as a type annotation in your own code, update it to the appropriate identifier-specific type based on which attribute it applies to. - ---- - -### PhoneProviderProtectionBackoffStrategyEnum value change - -The `PhoneProviderProtectionBackoffStrategyEnum` enum has been updated to reflect a change in the Auth0 API. The `None` variant has been renamed to `Default`, and its string value has changed from `"none"` to `"default"`. - -**Before (v5):** - -```ts -import { Management } from "auth0"; - -const strategy = Management.PhoneProviderProtectionBackoffStrategyEnum.None; // "none" -``` - -**After (v6):** - -```ts -import { Management } from "auth0"; - -const strategy = Management.PhoneProviderProtectionBackoffStrategyEnum.Default; // "default" -``` - -If you were passing this value directly as a string `"none"`, update it to `"default"` to match the updated API. - ---- - -### users.federatedConnectionsTokensets removed - -The `client.users.federatedConnectionsTokensets` sub-client has been removed. This includes the `list()` and `delete()` methods. - -**Before (v5):** - -```ts -// List active federated connection tokensets for a user -const tokensets = await client.users.federatedConnectionsTokensets.list("user_id"); - -// Delete a tokenset -await client.users.federatedConnectionsTokensets.delete("user_id", "tokenset_id"); -``` - -**After (v6):** - -These methods are no longer available. Remove any calls to `client.users.federatedConnectionsTokensets` from your code. - ---- - -### federated_connections_access_tokens removed from connection options - -The `federated_connections_access_tokens` field has been removed from all connection option types, including create and update. This affects OIDC, Azure AD, Google Apps, and other connection strategies. Remove it from any create or update payloads. - -**Before (v5):** - -```ts -// On create -await client.connections.create({ - strategy: "oidc", - name: "my-connection", - options: { - federated_connections_access_tokens: { ... }, - // other options - }, -}); - -// On update -await client.connections.update("connection_id", { - options: { - federated_connections_access_tokens: { ... }, - // other options - }, -}); -``` - -**After (v6):** - -```ts -// On create -await client.connections.create({ - strategy: "oidc", - name: "my-connection", - options: { - // remove federated_connections_access_tokens - // other options - }, -}); - -// On update -await client.connections.update("connection_id", { - options: { - // remove federated_connections_access_tokens - // other options - }, -}); -``` diff --git a/v7_MIGRATION_GUIDE.md b/v7_MIGRATION_GUIDE.md deleted file mode 100644 index 95d366b1e8..0000000000 --- a/v7_MIGRATION_GUIDE.md +++ /dev/null @@ -1,146 +0,0 @@ -# V7 Migration Guide - -A guide to migrating the Auth0 Node.js SDK from `6.x` to `7.x`. - -> **Migrating with an AI agent?** Point it at the Auth0 migration skill first. The skill lives in [`auth0/agent-skills`](https://github.com/auth0/agent-skills) as the `auth0` skill (migration intent: `migrate-node-auth0`). It encodes the authentication-layer rewrite rules and a verify loop. - -- [Overall changes](#overall-changes) -- [Breaking changes](#breaking-changes) - - [Authentication API removed from the main entrypoint](#authentication-api-removed-from-the-main-entrypoint) - - [Removed exports](#removed-exports) - - [ManagementClient mTLS requires an explicit `fetch`](#managementclient-mtls-requires-an-explicit-fetch) - - [mTLS works with both client secret and client assertion](#mtls-works-with-both-client-secret-and-client-assertion) - - [`domain` must be a bare hostname](#domain-must-be-a-bare-hostname) - - [Token acquisition failures throw `ManagementError`](#token-acquisition-failures-throw-managementerror) - - [`uuid` dependency removed](#uuid-dependency-removed) -- [Migrating authentication code](#migrating-authentication-code) -- [Staying on the legacy entrypoint](#staying-on-the-legacy-entrypoint) - -## Overall changes - -V7 makes `node-auth0` a **Management-API-only SDK**. The Authentication API layer (`AuthenticationClient`, its sub-clients, and `UserInfoClient`) has been removed from the main entrypoint. `ManagementClient` continues to work exactly as before; it now acquires its internal token directly via the client credentials grant rather than through the removed authentication layer. - -If your code only uses `ManagementClient`, the upgrade is small: address the Management-side breaking changes below (mTLS, domain validation, error type) and you are done. If your code uses `AuthenticationClient` or `UserInfoClient`, that code must move to a dedicated package; see [Migrating authentication code](#migrating-authentication-code). - -## Breaking changes - -### Authentication API removed from the main entrypoint - -`AuthenticationClient` and `UserInfoClient` are no longer exported from the `auth0` main entrypoint. The stateless authentication layer now lives in [`@auth0/auth0-auth-js`](https://github.com/auth0/auth0-auth-js), and the server-managed session layer lives in [`@auth0/auth0-server-js`](https://github.com/auth0/auth0-auth-js/tree/main/packages/auth0-server-js). - -**Before (v6):** - -```ts -import { AuthenticationClient, UserInfoClient } from "auth0"; - -const auth = new AuthenticationClient({ domain, clientId, clientSecret }); -const tokens = await auth.oauth.clientCredentialsGrant({ audience }); -``` - -**After (v7):** - -```ts -import { AuthClient } from "@auth0/auth0-auth-js"; - -const auth = new AuthClient({ domain, clientId, clientSecret }); -const tokens = await auth.getTokenByClientCredentials({ audience }); -``` - -The complete method-by-method mapping, the four cross-cutting behavior changes (return shape, casing, token expiry, error model), and the session-app wiring are documented in the dedicated [Authentication Migration Guide](https://github.com/auth0/node-auth0/tree/master/auth-migration). This guide does not repeat that detail. - -If you need the old clients unchanged as a stopgap, they still ship from the [legacy entrypoint](#staying-on-the-legacy-entrypoint). - -### Removed exports - -The following symbols were exported from the main entrypoint in v6 and are removed in v7. Each moves to `@auth0/auth0-auth-js`, or remains available from the `auth0/legacy` entrypoint at its v4.x shape. - -| Removed export (v6) | Replacement in v7 | -| ---------------------------- | ------------------------------------------------------------------------------------- | -| `AuthenticationClient` | `AuthClient` from `@auth0/auth0-auth-js` | -| `UserInfoClient` | `AuthClient.getUserInfo()` from `@auth0/auth0-auth-js`, or read `TokenResponse.claims` | -| `AuthApiError` | Per-operation typed errors from `@auth0/auth0-auth-js` (`TokenByCodeError`, `TokenByRefreshTokenError`, …); use their `.cause` | -| `AuthenticationClientOptions`| `AuthClientOptions` from `@auth0/auth0-auth-js` | -| `IDTokenValidateOptions` | Validation is internal to the grant call; pass `organization` / `nonce` / `maxAge` to the grant and read `TokenResponse.claims` | -| `IdTokenValidatorError` | Thrown internally by the grant as a typed error on claim mismatch | -| `TokenSet` | `TokenResponse` from `@auth0/auth0-auth-js` (camelCase fields; `expiresAt` is absolute) | -| `SUBJECT_TOKEN_TYPES` | Pass the token-type URN string directly to `exchangeToken` in `@auth0/auth0-auth-js` | -| `UserInfoResponse` | Return type of `AuthClient.getUserInfo()` in `@auth0/auth0-auth-js` | -| `UserInfoError` | Typed error from `AuthClient.getUserInfo()` in `@auth0/auth0-auth-js` | -| `ResponseError` | Management API calls throw `ManagementError` | -| `FetchError` | Management API calls throw `ManagementError` | -| `JSONApiResponse` | Responses return the data directly (no wrapper) | - -`ManagementClient`, the `Management` namespace, and `ManagementError` are unchanged and still exported. - -### ManagementClient mTLS requires an explicit `fetch` - -A `ManagementClient` constructed with `useMTLS: true` must now supply an explicit `fetch` option carrying the client certificate. The client throws at construction if `useMTLS` is set without a `fetch`. Previously a missing fetch surfaced as silent `401`s at request time; failing at construction makes the misconfiguration obvious. - -The token endpoint automatically uses the `mtls.{domain}` host when `useMTLS` is enabled. - -```ts -// v7: throws at construction if `fetch` is omitted -const mgmt = new ManagementClient({ - domain, - clientId, - clientSecret, - useMTLS: true, - fetch: mtlsCapableFetch, // now required -}); -``` - -### mTLS works with both client secret and client assertion - -`useMTLS` works with both `clientSecret` and `clientAssertionSigningKey`. mTLS (RFC 8705) is a transport-layer concern: the TLS client certificate yields a certificate-bound token regardless of which client authentication method is used. An explicit `fetch` option is always required when `useMTLS` is set. - -### `domain` must be a bare hostname - -`domain` must be a bare host such as `tenant.us.auth0.com`. A value containing a scheme, slashes, or a query string now throws at construction instead of producing malformed request URLs later. - -```ts -// throws in v7 -new ManagementClient({ domain: "https://tenant.us.auth0.com/", ... }); -// correct -new ManagementClient({ domain: "tenant.us.auth0.com", ... }); -``` - -### Token acquisition failures throw `ManagementError` - -When the internal client-credentials token request fails, the client now throws a `ManagementError` (previously a plain `Error`). The error carries `statusCode` and a parsed `body` with the OAuth error details. A request that exceeds the 10-second timeout throws `ManagementError` with status `408`. - -```ts -import { ManagementError } from "auth0"; - -try { - await mgmt.users.getAll(); -} catch (e) { - if (e instanceof ManagementError) { - console.error(e.statusCode, e.body); - } -} -``` - -### `uuid` dependency removed - -The `uuid` package is no longer a dependency. If your project imported `uuid` transitively through `auth0`, add it to your own `dependencies`. - -## Migrating authentication code - -If your app calls `AuthenticationClient` or `UserInfoClient`, follow the dedicated [Authentication Migration Guide](https://github.com/auth0/node-auth0/tree/master/auth-migration). Start with [`auth-migration/index.md`](https://github.com/auth0/node-auth0/blob/master/auth-migration/index.md) for the OIDC token grants section; the incremental flow, session, and troubleshooting pages live in the same [`auth-migration/`](https://github.com/auth0/node-auth0/tree/master/auth-migration) directory. It covers: - -- Choosing between `@auth0/auth0-auth-js` (stateless token grants) and `@auth0/auth0-server-js` (server-managed sessions). -- The complete method-by-method API mapping for `.oauth`, `.database`, `.passwordless`, `.backchannel`, `.tokenExchange`, and `UserInfoClient`. -- The four cross-cutting behavior changes: return shape (envelope dropped), casing (snake_case → camelCase), token expiry (`expires_in` relative → `expiresAt` absolute, a silent high-risk change), and the typed error model with `isMfaRequiredError()`. -- Wiring the `auth0-server-js` session lifecycle when you want the SDK to own login, cookies, refresh, and logout. - -The Management API is explicitly out of scope in that guide: a file that keeps using `ManagementClient` from `auth0` while importing `@auth0/auth0-auth-js` for authentication is correct and expected. - -## Staying on the legacy entrypoint - -If you cannot migrate the authentication code immediately, the `auth0/legacy` entrypoint still ships `AuthenticationClient` and `UserInfoClient` at their v4.x configuration format and method signatures. This is a stopgap, not a destination; the legacy shapes differ from the current API and will not receive new features. - -```ts -import { AuthenticationClient } from "auth0/legacy"; -``` - -Plan the move to `@auth0/auth0-auth-js` / `@auth0/auth0-server-js` rather than treating the legacy entrypoint as permanent. diff --git a/yarn.lock b/yarn.lock index 6d58e4c3b6..a60c68bce0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -893,11 +893,11 @@ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@*": - version "22.20.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.20.2.tgz#daae777b5f5965a587f50cc80d5364c2ddf27e36" - integrity sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw== + version "26.5.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-26.5.1.tgz#b19c390e15813f402a94b86e3af9042f792138be" + integrity sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g== dependencies: - undici-types "~6.21.0" + undici-types "~8.9.0" "@types/node@^20.0.0": version "20.19.43" @@ -1190,7 +1190,7 @@ acorn-walk@^8.0.2: dependencies: acorn "^8.11.0" -acorn@^8.1.0, acorn@^8.11.0, acorn@^8.15.0, acorn@^8.16.0, acorn@^8.8.1: +acorn@^8.1.0, acorn@^8.11.0, acorn@^8.15.0, acorn@^8.8.1: version "8.18.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.18.0.tgz#4faf01b2d6d326bfeed97aea1f52220b5f4c1940" integrity sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ== @@ -1385,22 +1385,22 @@ balanced-match@^4.0.2: integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== baseline-browser-mapping@^2.11.20: - version "2.11.22" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.22.tgz#d8b612f54517d8b3a31734623779e5ab8c5ff0b9" - integrity sha512-pWc4w51fBFd7mav43/zKRC+RI6f4yfzQoVlfvE8dECePyfkn1bzLp01Fj0QACcyCZyFhiEMyD2qScfKRWgWibA== + version "2.11.23" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz#304c980a35de0f460cf12985359d6e11494c1ab4" + integrity sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ== brace-expansion@^1.1.7: - version "1.1.18" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.18.tgz#3ce74d89885136be1535341f8c3d4425c29a5cab" - integrity sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw== + version "1.1.21" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.21.tgz#edf4fab5c64d051aea5a8def49aba1c7522279f3" + integrity sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^5.0.8: - version "5.0.9" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.9.tgz#7c72438809b5fa5babf54199a1f1c281a6984fcf" - integrity sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg== + version "5.0.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.12.tgz#995fbb4750a77c4d16a7dc942ff2ca6ef8e675ec" + integrity sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ== dependencies: balanced-match "^4.0.2" @@ -1692,9 +1692,9 @@ dunder-proto@^1.0.1: gopd "^1.2.0" electron-to-chromium@^1.5.420: - version "1.5.427" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.427.tgz#f8693d109cf116d4a6efa98bc620b9fc7f265f67" - integrity sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw== + version "1.5.428" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz#36bdb39055d71ab08a54855508ef33940bc467e6" + integrity sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw== emittery@^0.13.1: version "0.13.1" @@ -1711,10 +1711,10 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -enhanced-resolve@^5.24.4: - version "5.24.5" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz#b4dad3255b7545f07ba5535189868e9f85f47573" - integrity sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A== +enhanced-resolve@^5.25.0: + version "5.25.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.25.1.tgz#9d919bfa898b116e58f20e95127c047693158771" + integrity sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w== dependencies: graceful-fs "^4.2.4" tapable "^2.3.3" @@ -1975,9 +1975,9 @@ fast-levenshtein@^2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-uri@^3.0.1: - version "3.1.7" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.7.tgz#743157d957f3cbb4c65310e033dc2ad4ad7dc60a" - integrity sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg== + version "3.1.8" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.8.tgz#f7db8d942e20eead3cfe93b0beeb4eb0169e938b" + integrity sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg== fb-watchman@^2.0.0: version "2.0.2" @@ -3009,9 +3009,9 @@ makeerror@1.0.12: tmpl "1.0.5" markdown-it@^14.3.0: - version "14.3.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.3.1.tgz#8974e2473779363ed5682ea377f31564ee51e292" - integrity sha512-4Ej49aYTDFIQ+uBkfX8GBvJGccoARxxPep+7aWTs55ozbjQJpW9M26Fe53vnGgvLeVzva/amzjQQaQu9w0vMhA== + version "14.3.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.3.2.tgz#eb41b5855120836603c33c1be4c92f58001f8a26" + integrity sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q== dependencies: argparse "^2.0.1" entities "^4.5.0" @@ -3502,10 +3502,10 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -schema-utils@^4.3.3: - version "4.4.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.4.0.tgz#fbc4f90ab5047f01a9c8b07347e74f2cdb567067" - integrity sha512-ZzWFVzFgyRHi/T2Ecxm37lL6J9+hZO0P9L7LCuLxAbC6XWxkvxcaQBCXhQxMGn/Tdt9nD7zIBk0ELwhMQ3UEDg== +schema-utils@^4.3.3, schema-utils@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.5.0.tgz#aaf8d588bd2b19511175175a5222baef648c009c" + integrity sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA== dependencies: "@types/json-schema" "^7.0.15" ajv "^8.20.0" @@ -3754,17 +3754,17 @@ tinyglobby@^0.2.15: fdir "^6.5.0" picomatch "^4.0.4" -tldts-core@^7.4.12: - version "7.4.12" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.12.tgz#db2ed323e525394f9b65380ef47a0076aa105d36" - integrity sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ== +tldts-core@^7.4.13: + version "7.4.13" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.13.tgz#5dd4ed4730b34bb12cdc1f8a86c80f07c9780670" + integrity sha512-mbYsrih5FRtGxs3Usvl/PqwJsNpp+jsmrdFviiK02teHDG0/HebBG/pqCylje3kzgXYzuLoHJF/0mz9W53t8Xg== tldts@^7.0.5: - version "7.4.12" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.12.tgz#b764e7e3d2cc0ad66ca1a75504a702f3e90a91e2" - integrity sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA== + version "7.4.13" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.13.tgz#50e9a884162e6091844dadf0696df9353eb8b2be" + integrity sha512-iHtaIWWIbMDkCeJdTBzZFGgbluE5J+oHlb2g7+oAz1S1gpuVpabRZdQyd471Vl8UUkcz2vXSL8xZH2kyCe8tfA== dependencies: - tldts-core "^7.4.12" + tldts-core "^7.4.13" tmpl@1.0.5: version "1.0.5" @@ -3894,6 +3894,11 @@ undici-types@~6.21.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +undici-types@~8.9.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-8.9.0.tgz#e240d97c8b5d85e5347ce73d25865c7906c1ec9f" + integrity sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg== + undici@^7.12.0: version "7.29.1" resolved "https://registry.yarnpkg.com/undici/-/undici-7.29.1.tgz#7741c6fc8b3e1a48e30323833642bfbe841443ad" @@ -3973,26 +3978,24 @@ webpack-sources@^3.5.1: integrity sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw== webpack@^5.105.4: - version "5.110.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.110.3.tgz#e122b66f6226b7af8f209b6102cd13238c4813a8" - integrity sha512-GuizBzRvo9YPpyoNMf3ag7AzxbaW85qrRSqTha345KyJbAFPt3/cMzBM0h+RWg7SK/7DdzRLINP3LvQ0hvr4hg== + version "5.111.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.111.0.tgz#65e7e0044d69373ea0129ef401c0299aaba52764" + integrity sha512-A2R74kfE6b3eLKS91iZiol03Ebx7avmJJBOyiP+4LePW9NNeNJGQkKeSQos4dk6R4H0YAMYlv+aOrs1CyWT3eA== dependencies: "@types/estree" "^1.0.8" "@types/json-schema" "^7.0.15" "@webassemblyjs/ast" "^1.14.1" "@webassemblyjs/wasm-edit" "^1.14.1" "@webassemblyjs/wasm-parser" "^1.14.1" - acorn "^8.16.0" browserslist "^4.28.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.24.4" + enhanced-resolve "^5.25.0" es-module-lexer "^2.1.0" events "^3.2.0" graceful-fs "^4.2.11" mime-db "^1.54.0" minimizer-webpack-plugin "^5.7.0" - neo-async "^2.6.2" - schema-utils "^4.3.3" + schema-utils "^4.5.0" tapable "^2.3.0" watchpack "^2.5.2" webpack-sources "^3.5.1" @@ -4100,9 +4103,9 @@ yallist@^3.0.2: integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml@^2.8.2, yaml@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4" - integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA== + version "2.9.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.1.tgz#c16233fb31944e705cfefaff38795587f57588ce" + integrity sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw== yargs-parser@^21.1.1: version "21.1.1" From fd6fd65032357b2e7d368e9a5fad9d419ad268ec Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 11:47:28 +0000 Subject: [PATCH 2/2] [fern-replay] Applied customizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patches applied (5): - patch-066ff9e0: docs: add v6 migration guide for breaking changes in v6.0.0 (#1368) - patch-d8faf7a2: docs: update v6 migration guide with federatedConnectionsTokensets and federated_connections_access_tokens breaking changes (#1370) - patch-0f7baa6f: fix: auto-stamp SDK_VERSION from package.json during build (#1382) - patch-b942f5d6: docs: add Authentication API migration guide (auth0-auth-js / auth0-server-js) (#1395) - patch-842d2f51: docs: add v7 migration guide (v6 → v7) (#1396) Patches with unresolved conflicts (7): - patch-490e5634: feat(management): add sub-package exports and management auth helper (#1373) - patch-74170bb0: Release v6.1.0 (#1381) - patch-4fcb39a4: Release v6.2.0 (#1389) - patch-f9d64134: Release v6.3.0 (#1393) - patch-4d450aa4: Release v6.4.0 (#1401) - patch-d4614ec0: Release v7.0.0 (#1402) - patch-e1e90b89: Release v7.1.0 (#1405) Run `fern-replay resolve` to apply these customizations. --- .fern/replay.lock | 1874 +++++++++++++++++++++++- .shiprc | 6 + AUTH_MIGRATION_GUIDE.md | 14 + auth-migration/authentication-flows.md | 234 +++ auth-migration/index.md | 732 +++++++++ auth-migration/server-side-sessions.md | 163 +++ auth-migration/troubleshooting.md | 32 + v6_MIGRATION_GUIDE.md | 158 ++ v7_MIGRATION_GUIDE.md | 146 ++ 9 files changed, 3353 insertions(+), 6 deletions(-) create mode 100644 .shiprc create mode 100644 AUTH_MIGRATION_GUIDE.md create mode 100644 auth-migration/authentication-flows.md create mode 100644 auth-migration/index.md create mode 100644 auth-migration/server-side-sessions.md create mode 100644 auth-migration/troubleshooting.md create mode 100644 v6_MIGRATION_GUIDE.md create mode 100644 v7_MIGRATION_GUIDE.md diff --git a/.fern/replay.lock b/.fern/replay.lock index f9bed0522a..c169b26f15 100644 --- a/.fern/replay.lock +++ b/.fern/replay.lock @@ -42,14 +42,20 @@ generations: cli_version: unknown generator_versions: fernapi/fern-typescript-sdk: 3.72.5 -current_generation: 4bc190d09b0bc5a6648d590b1670491c11d0b1e6 + - commit_sha: b96b8d7eee204c8a279672038c146c2fb309a6b1 + tree_hash: dd0456080b56f0f580d6bb9f6d149a564c724c26 + timestamp: 2026-09-15T11:47:15.122Z + cli_version: unknown + generator_versions: + fernapi/fern-typescript-sdk: 3.72.5 +current_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 patches: - id: patch-066ff9e0 content_hash: sha256:5b87ec9a63fc57aba4840d0d335d472c621e838fc1d61515b30660df39d0dbc2 original_commit: 066ff9e02c45f4abf46791a3f070c762f7ecd924 original_message: "docs: add v6 migration guide for breaking changes in v6.0.0 (#1368)" original_author: Ankita Tripathi <51994119+ankita10119@users.noreply.github.com> - base_generation: 4bc190d09b0bc5a6648d590b1670491c11d0b1e6 + base_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 files: - v6_MIGRATION_GUIDE.md patch_content: | @@ -383,7 +389,7 @@ patches: original_commit: d8faf7a21efbed3bd75a0f36ff0d190bb9dd986b original_message: "docs: update v6 migration guide with federatedConnectionsTokensets and federated_connections_access_tokens breaking changes (#1370)" original_author: Ankita Tripathi <51994119+ankita10119@users.noreply.github.com> - base_generation: 4bc190d09b0bc5a6648d590b1670491c11d0b1e6 + base_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 files: - v6_MIGRATION_GUIDE.md patch_content: | @@ -8225,7 +8231,7 @@ patches: original_commit: 0f7baa6fa58a2944cd9e39abbfd8caf7be8d7e8c original_message: "fix: auto-stamp SDK_VERSION from package.json during build (#1382)" original_author: Ankita Tripathi <51994119+ankita10119@users.noreply.github.com> - base_generation: 4bc190d09b0bc5a6648d590b1670491c11d0b1e6 + base_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 files: - .shiprc patch_content: | @@ -13702,7 +13708,7 @@ patches: original_commit: b942f5d6b1b5f3e565f3315f12421c58ae005bec original_message: "docs: add Authentication API migration guide (auth0-auth-js / auth0-server-js) (#1395)" original_author: tusharpandey13 - base_generation: 4bc190d09b0bc5a6648d590b1670491c11d0b1e6 + base_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 files: - AUTH_MIGRATION_GUIDE.md - auth-migration/authentication-flows.md @@ -16102,7 +16108,7 @@ patches: original_commit: 842d2f5189493b78e420c17c968c80bc239e143a original_message: "docs: add v7 migration guide (v6 → v7) (#1396)" original_author: tusharpandey13 - base_generation: 4bc190d09b0bc5a6648d590b1670491c11d0b1e6 + base_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 files: - v7_MIGRATION_GUIDE.md patch_content: | @@ -18240,3 +18246,1859 @@ patches: src/management/version.ts: | export const SDK_VERSION = "7.0.0"; status: unresolved + - id: patch-e1e90b89 + content_hash: sha256:782bc97057fa4ab960e1ff03f9f052fd6152e4b7f9371ca9ac7452c2ed298cd7 + original_commit: e1e90b89a0a8bfc8c1b69c93d5d8c89dfa2966f1 + original_message: Release v7.1.0 (#1405) + original_author: Ankita Tripathi <51994119+ankita10119@users.noreply.github.com> + base_generation: 6151df70eb9eff2e0416b35853f209da4a7aa0a0 + files: + - package.json + - src/management/version.ts + patch_content: | + diff --git a/package.json b/package.json + index e43b49a27..85bc016c6 100644 + --- a/package.json + +++ b/package.json + @@ -1,6 +1,6 @@ + { + "name": "auth0", + - "version": "7.0.0", + + "version": "7.1.0", + "private": false, + "repository": { + "type": "git", + diff --git a/src/management/version.ts b/src/management/version.ts + index 6bb53d57b..07bfe918b 100644 + --- a/src/management/version.ts + +++ b/src/management/version.ts + @@ -1 +1 @@ + -export const SDK_VERSION = "7.0.0"; + +export const SDK_VERSION = "7.1.0"; + theirs_snapshot: + package.json: | + { + "name": "auth0", + "version": "7.1.0", + "private": false, + "repository": { + "type": "git", + "url": "git+https://github.com/auth0/node-auth0.git" + }, + "license": "MIT", + "type": "commonjs", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.mts", + "default": "./dist/esm/index.mjs" + }, + "require": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index.js" + }, + "default": "./dist/cjs/index.js" + }, + "./actions": { + "import": { + "types": "./dist/esm/management/api/resources/actions/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/exports.js" + }, + "./agents": { + "import": { + "types": "./dist/esm/management/api/resources/agents/exports.d.mts", + "default": "./dist/esm/management/api/resources/agents/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/agents/exports.d.ts", + "default": "./dist/cjs/management/api/resources/agents/exports.js" + }, + "default": "./dist/cjs/management/api/resources/agents/exports.js" + }, + "./branding": { + "import": { + "types": "./dist/esm/management/api/resources/branding/exports.d.mts", + "default": "./dist/esm/management/api/resources/branding/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/branding/exports.d.ts", + "default": "./dist/cjs/management/api/resources/branding/exports.js" + }, + "default": "./dist/cjs/management/api/resources/branding/exports.js" + }, + "./clientGrants": { + "import": { + "types": "./dist/esm/management/api/resources/clientGrants/exports.d.mts", + "default": "./dist/esm/management/api/resources/clientGrants/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/clientGrants/exports.d.ts", + "default": "./dist/cjs/management/api/resources/clientGrants/exports.js" + }, + "default": "./dist/cjs/management/api/resources/clientGrants/exports.js" + }, + "./clients": { + "import": { + "types": "./dist/esm/management/api/resources/clients/exports.d.mts", + "default": "./dist/esm/management/api/resources/clients/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/clients/exports.d.ts", + "default": "./dist/cjs/management/api/resources/clients/exports.js" + }, + "default": "./dist/cjs/management/api/resources/clients/exports.js" + }, + "./connectionProfiles": { + "import": { + "types": "./dist/esm/management/api/resources/connectionProfiles/exports.d.mts", + "default": "./dist/esm/management/api/resources/connectionProfiles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connectionProfiles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connectionProfiles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connectionProfiles/exports.js" + }, + "./connections": { + "import": { + "types": "./dist/esm/management/api/resources/connections/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/exports.js" + }, + "./customDomains": { + "import": { + "types": "./dist/esm/management/api/resources/customDomains/exports.d.mts", + "default": "./dist/esm/management/api/resources/customDomains/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/customDomains/exports.d.ts", + "default": "./dist/cjs/management/api/resources/customDomains/exports.js" + }, + "default": "./dist/cjs/management/api/resources/customDomains/exports.js" + }, + "./deviceCredentials": { + "import": { + "types": "./dist/esm/management/api/resources/deviceCredentials/exports.d.mts", + "default": "./dist/esm/management/api/resources/deviceCredentials/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/deviceCredentials/exports.d.ts", + "default": "./dist/cjs/management/api/resources/deviceCredentials/exports.js" + }, + "default": "./dist/cjs/management/api/resources/deviceCredentials/exports.js" + }, + "./emailTemplates": { + "import": { + "types": "./dist/esm/management/api/resources/emailTemplates/exports.d.mts", + "default": "./dist/esm/management/api/resources/emailTemplates/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/emailTemplates/exports.d.ts", + "default": "./dist/cjs/management/api/resources/emailTemplates/exports.js" + }, + "default": "./dist/cjs/management/api/resources/emailTemplates/exports.js" + }, + "./eventStreams": { + "import": { + "types": "./dist/esm/management/api/resources/eventStreams/exports.d.mts", + "default": "./dist/esm/management/api/resources/eventStreams/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/eventStreams/exports.d.ts", + "default": "./dist/cjs/management/api/resources/eventStreams/exports.js" + }, + "default": "./dist/cjs/management/api/resources/eventStreams/exports.js" + }, + "./events": { + "import": { + "types": "./dist/esm/management/api/resources/events/exports.d.mts", + "default": "./dist/esm/management/api/resources/events/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/events/exports.d.ts", + "default": "./dist/cjs/management/api/resources/events/exports.js" + }, + "default": "./dist/cjs/management/api/resources/events/exports.js" + }, + "./flows": { + "import": { + "types": "./dist/esm/management/api/resources/flows/exports.d.mts", + "default": "./dist/esm/management/api/resources/flows/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/flows/exports.d.ts", + "default": "./dist/cjs/management/api/resources/flows/exports.js" + }, + "default": "./dist/cjs/management/api/resources/flows/exports.js" + }, + "./forms": { + "import": { + "types": "./dist/esm/management/api/resources/forms/exports.d.mts", + "default": "./dist/esm/management/api/resources/forms/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/forms/exports.d.ts", + "default": "./dist/cjs/management/api/resources/forms/exports.js" + }, + "default": "./dist/cjs/management/api/resources/forms/exports.js" + }, + "./userGrants": { + "import": { + "types": "./dist/esm/management/api/resources/userGrants/exports.d.mts", + "default": "./dist/esm/management/api/resources/userGrants/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/userGrants/exports.d.ts", + "default": "./dist/cjs/management/api/resources/userGrants/exports.js" + }, + "default": "./dist/cjs/management/api/resources/userGrants/exports.js" + }, + "./groups": { + "import": { + "types": "./dist/esm/management/api/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/groups/exports.js" + }, + "./guardian": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/exports.js" + }, + "./hooks": { + "import": { + "types": "./dist/esm/management/api/resources/hooks/exports.d.mts", + "default": "./dist/esm/management/api/resources/hooks/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/hooks/exports.d.ts", + "default": "./dist/cjs/management/api/resources/hooks/exports.js" + }, + "default": "./dist/cjs/management/api/resources/hooks/exports.js" + }, + "./jobs": { + "import": { + "types": "./dist/esm/management/api/resources/jobs/exports.d.mts", + "default": "./dist/esm/management/api/resources/jobs/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/jobs/exports.d.ts", + "default": "./dist/cjs/management/api/resources/jobs/exports.js" + }, + "default": "./dist/cjs/management/api/resources/jobs/exports.js" + }, + "./logStreams": { + "import": { + "types": "./dist/esm/management/api/resources/logStreams/exports.d.mts", + "default": "./dist/esm/management/api/resources/logStreams/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/logStreams/exports.d.ts", + "default": "./dist/cjs/management/api/resources/logStreams/exports.js" + }, + "default": "./dist/cjs/management/api/resources/logStreams/exports.js" + }, + "./logs": { + "import": { + "types": "./dist/esm/management/api/resources/logs/exports.d.mts", + "default": "./dist/esm/management/api/resources/logs/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/logs/exports.d.ts", + "default": "./dist/cjs/management/api/resources/logs/exports.js" + }, + "default": "./dist/cjs/management/api/resources/logs/exports.js" + }, + "./networkAcls": { + "import": { + "types": "./dist/esm/management/api/resources/networkAcls/exports.d.mts", + "default": "./dist/esm/management/api/resources/networkAcls/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/networkAcls/exports.d.ts", + "default": "./dist/cjs/management/api/resources/networkAcls/exports.js" + }, + "default": "./dist/cjs/management/api/resources/networkAcls/exports.js" + }, + "./organizations": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/exports.js" + }, + "./prompts": { + "import": { + "types": "./dist/esm/management/api/resources/prompts/exports.d.mts", + "default": "./dist/esm/management/api/resources/prompts/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/prompts/exports.d.ts", + "default": "./dist/cjs/management/api/resources/prompts/exports.js" + }, + "default": "./dist/cjs/management/api/resources/prompts/exports.js" + }, + "./rateLimitPolicies": { + "import": { + "types": "./dist/esm/management/api/resources/rateLimitPolicies/exports.d.mts", + "default": "./dist/esm/management/api/resources/rateLimitPolicies/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/rateLimitPolicies/exports.d.ts", + "default": "./dist/cjs/management/api/resources/rateLimitPolicies/exports.js" + }, + "default": "./dist/cjs/management/api/resources/rateLimitPolicies/exports.js" + }, + "./refreshTokens": { + "import": { + "types": "./dist/esm/management/api/resources/refreshTokens/exports.d.mts", + "default": "./dist/esm/management/api/resources/refreshTokens/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/refreshTokens/exports.d.ts", + "default": "./dist/cjs/management/api/resources/refreshTokens/exports.js" + }, + "default": "./dist/cjs/management/api/resources/refreshTokens/exports.js" + }, + "./resourceServers": { + "import": { + "types": "./dist/esm/management/api/resources/resourceServers/exports.d.mts", + "default": "./dist/esm/management/api/resources/resourceServers/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/resourceServers/exports.d.ts", + "default": "./dist/cjs/management/api/resources/resourceServers/exports.js" + }, + "default": "./dist/cjs/management/api/resources/resourceServers/exports.js" + }, + "./roles": { + "import": { + "types": "./dist/esm/management/api/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/roles/exports.js" + }, + "./rules": { + "import": { + "types": "./dist/esm/management/api/resources/rules/exports.d.mts", + "default": "./dist/esm/management/api/resources/rules/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/rules/exports.d.ts", + "default": "./dist/cjs/management/api/resources/rules/exports.js" + }, + "default": "./dist/cjs/management/api/resources/rules/exports.js" + }, + "./rulesConfigs": { + "import": { + "types": "./dist/esm/management/api/resources/rulesConfigs/exports.d.mts", + "default": "./dist/esm/management/api/resources/rulesConfigs/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/rulesConfigs/exports.d.ts", + "default": "./dist/cjs/management/api/resources/rulesConfigs/exports.js" + }, + "default": "./dist/cjs/management/api/resources/rulesConfigs/exports.js" + }, + "./selfServiceProfiles": { + "import": { + "types": "./dist/esm/management/api/resources/selfServiceProfiles/exports.d.mts", + "default": "./dist/esm/management/api/resources/selfServiceProfiles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/selfServiceProfiles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/selfServiceProfiles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/selfServiceProfiles/exports.js" + }, + "./sessions": { + "import": { + "types": "./dist/esm/management/api/resources/sessions/exports.d.mts", + "default": "./dist/esm/management/api/resources/sessions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/sessions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/sessions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/sessions/exports.js" + }, + "./stats": { + "import": { + "types": "./dist/esm/management/api/resources/stats/exports.d.mts", + "default": "./dist/esm/management/api/resources/stats/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/stats/exports.d.ts", + "default": "./dist/cjs/management/api/resources/stats/exports.js" + }, + "default": "./dist/cjs/management/api/resources/stats/exports.js" + }, + "./supplementalSignals": { + "import": { + "types": "./dist/esm/management/api/resources/supplementalSignals/exports.d.mts", + "default": "./dist/esm/management/api/resources/supplementalSignals/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/supplementalSignals/exports.d.ts", + "default": "./dist/cjs/management/api/resources/supplementalSignals/exports.js" + }, + "default": "./dist/cjs/management/api/resources/supplementalSignals/exports.js" + }, + "./tickets": { + "import": { + "types": "./dist/esm/management/api/resources/tickets/exports.d.mts", + "default": "./dist/esm/management/api/resources/tickets/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/tickets/exports.d.ts", + "default": "./dist/cjs/management/api/resources/tickets/exports.js" + }, + "default": "./dist/cjs/management/api/resources/tickets/exports.js" + }, + "./tokenExchangeProfiles": { + "import": { + "types": "./dist/esm/management/api/resources/tokenExchangeProfiles/exports.d.mts", + "default": "./dist/esm/management/api/resources/tokenExchangeProfiles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/tokenExchangeProfiles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/tokenExchangeProfiles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/tokenExchangeProfiles/exports.js" + }, + "./userAttributeProfiles": { + "import": { + "types": "./dist/esm/management/api/resources/userAttributeProfiles/exports.d.mts", + "default": "./dist/esm/management/api/resources/userAttributeProfiles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/userAttributeProfiles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/userAttributeProfiles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/userAttributeProfiles/exports.js" + }, + "./userBlocks": { + "import": { + "types": "./dist/esm/management/api/resources/userBlocks/exports.d.mts", + "default": "./dist/esm/management/api/resources/userBlocks/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/userBlocks/exports.d.ts", + "default": "./dist/cjs/management/api/resources/userBlocks/exports.js" + }, + "default": "./dist/cjs/management/api/resources/userBlocks/exports.js" + }, + "./users": { + "import": { + "types": "./dist/esm/management/api/resources/users/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/exports.js" + }, + "./actions/versions": { + "import": { + "types": "./dist/esm/management/api/resources/actions/resources/versions/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/resources/versions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/resources/versions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/resources/versions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/resources/versions/exports.js" + }, + "./actions/executions": { + "import": { + "types": "./dist/esm/management/api/resources/actions/resources/executions/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/resources/executions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/resources/executions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/resources/executions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/resources/executions/exports.js" + }, + "./actions/modules": { + "import": { + "types": "./dist/esm/management/api/resources/actions/resources/modules/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/resources/modules/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/resources/modules/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/resources/modules/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/resources/modules/exports.js" + }, + "./actions/triggers": { + "import": { + "types": "./dist/esm/management/api/resources/actions/resources/triggers/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/resources/triggers/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/resources/triggers/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/resources/triggers/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/resources/triggers/exports.js" + }, + "./actions/modules/versions": { + "import": { + "types": "./dist/esm/management/api/resources/actions/resources/modules/resources/versions/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/resources/modules/resources/versions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/resources/modules/resources/versions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/resources/modules/resources/versions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/resources/modules/resources/versions/exports.js" + }, + "./actions/triggers/bindings": { + "import": { + "types": "./dist/esm/management/api/resources/actions/resources/triggers/resources/bindings/exports.d.mts", + "default": "./dist/esm/management/api/resources/actions/resources/triggers/resources/bindings/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/actions/resources/triggers/resources/bindings/exports.d.ts", + "default": "./dist/cjs/management/api/resources/actions/resources/triggers/resources/bindings/exports.js" + }, + "default": "./dist/cjs/management/api/resources/actions/resources/triggers/resources/bindings/exports.js" + }, + "./anomaly": { + "import": { + "types": "./dist/esm/management/api/resources/anomaly/exports.d.mts", + "default": "./dist/esm/management/api/resources/anomaly/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/anomaly/exports.d.ts", + "default": "./dist/cjs/management/api/resources/anomaly/exports.js" + }, + "default": "./dist/cjs/management/api/resources/anomaly/exports.js" + }, + "./anomaly/blocks": { + "import": { + "types": "./dist/esm/management/api/resources/anomaly/resources/blocks/exports.d.mts", + "default": "./dist/esm/management/api/resources/anomaly/resources/blocks/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/anomaly/resources/blocks/exports.d.ts", + "default": "./dist/cjs/management/api/resources/anomaly/resources/blocks/exports.js" + }, + "default": "./dist/cjs/management/api/resources/anomaly/resources/blocks/exports.js" + }, + "./attackProtection": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/exports.js" + }, + "./attackProtection/botDetection": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/resources/botDetection/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/resources/botDetection/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/resources/botDetection/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/resources/botDetection/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/resources/botDetection/exports.js" + }, + "./attackProtection/breachedPasswordDetection": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/resources/breachedPasswordDetection/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/resources/breachedPasswordDetection/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/resources/breachedPasswordDetection/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/resources/breachedPasswordDetection/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/resources/breachedPasswordDetection/exports.js" + }, + "./attackProtection/bruteForceProtection": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/resources/bruteForceProtection/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/resources/bruteForceProtection/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/resources/bruteForceProtection/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/resources/bruteForceProtection/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/resources/bruteForceProtection/exports.js" + }, + "./attackProtection/captcha": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/resources/captcha/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/resources/captcha/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/resources/captcha/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/resources/captcha/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/resources/captcha/exports.js" + }, + "./attackProtection/phoneProviderProtection": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/resources/phoneProviderProtection/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/resources/phoneProviderProtection/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/resources/phoneProviderProtection/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/resources/phoneProviderProtection/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/resources/phoneProviderProtection/exports.js" + }, + "./attackProtection/suspiciousIpThrottling": { + "import": { + "types": "./dist/esm/management/api/resources/attackProtection/resources/suspiciousIpThrottling/exports.d.mts", + "default": "./dist/esm/management/api/resources/attackProtection/resources/suspiciousIpThrottling/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/attackProtection/resources/suspiciousIpThrottling/exports.d.ts", + "default": "./dist/cjs/management/api/resources/attackProtection/resources/suspiciousIpThrottling/exports.js" + }, + "default": "./dist/cjs/management/api/resources/attackProtection/resources/suspiciousIpThrottling/exports.js" + }, + "./branding/templates": { + "import": { + "types": "./dist/esm/management/api/resources/branding/resources/templates/exports.d.mts", + "default": "./dist/esm/management/api/resources/branding/resources/templates/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/branding/resources/templates/exports.d.ts", + "default": "./dist/cjs/management/api/resources/branding/resources/templates/exports.js" + }, + "default": "./dist/cjs/management/api/resources/branding/resources/templates/exports.js" + }, + "./branding/themes": { + "import": { + "types": "./dist/esm/management/api/resources/branding/resources/themes/exports.d.mts", + "default": "./dist/esm/management/api/resources/branding/resources/themes/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/branding/resources/themes/exports.d.ts", + "default": "./dist/cjs/management/api/resources/branding/resources/themes/exports.js" + }, + "default": "./dist/cjs/management/api/resources/branding/resources/themes/exports.js" + }, + "./branding/phone": { + "import": { + "types": "./dist/esm/management/api/resources/branding/resources/phone/exports.d.mts", + "default": "./dist/esm/management/api/resources/branding/resources/phone/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/branding/resources/phone/exports.d.ts", + "default": "./dist/cjs/management/api/resources/branding/resources/phone/exports.js" + }, + "default": "./dist/cjs/management/api/resources/branding/resources/phone/exports.js" + }, + "./branding/phone/providers": { + "import": { + "types": "./dist/esm/management/api/resources/branding/resources/phone/resources/providers/exports.d.mts", + "default": "./dist/esm/management/api/resources/branding/resources/phone/resources/providers/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/branding/resources/phone/resources/providers/exports.d.ts", + "default": "./dist/cjs/management/api/resources/branding/resources/phone/resources/providers/exports.js" + }, + "default": "./dist/cjs/management/api/resources/branding/resources/phone/resources/providers/exports.js" + }, + "./branding/phone/templates": { + "import": { + "types": "./dist/esm/management/api/resources/branding/resources/phone/resources/templates/exports.d.mts", + "default": "./dist/esm/management/api/resources/branding/resources/phone/resources/templates/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/branding/resources/phone/resources/templates/exports.d.ts", + "default": "./dist/cjs/management/api/resources/branding/resources/phone/resources/templates/exports.js" + }, + "default": "./dist/cjs/management/api/resources/branding/resources/phone/resources/templates/exports.js" + }, + "./clientGrants/organizations": { + "import": { + "types": "./dist/esm/management/api/resources/clientGrants/resources/organizations/exports.d.mts", + "default": "./dist/esm/management/api/resources/clientGrants/resources/organizations/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/clientGrants/resources/organizations/exports.d.ts", + "default": "./dist/cjs/management/api/resources/clientGrants/resources/organizations/exports.js" + }, + "default": "./dist/cjs/management/api/resources/clientGrants/resources/organizations/exports.js" + }, + "./clients/credentials": { + "import": { + "types": "./dist/esm/management/api/resources/clients/resources/credentials/exports.d.mts", + "default": "./dist/esm/management/api/resources/clients/resources/credentials/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/clients/resources/credentials/exports.d.ts", + "default": "./dist/cjs/management/api/resources/clients/resources/credentials/exports.js" + }, + "default": "./dist/cjs/management/api/resources/clients/resources/credentials/exports.js" + }, + "./clients/connections": { + "import": { + "types": "./dist/esm/management/api/resources/clients/resources/connections/exports.d.mts", + "default": "./dist/esm/management/api/resources/clients/resources/connections/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/clients/resources/connections/exports.d.ts", + "default": "./dist/cjs/management/api/resources/clients/resources/connections/exports.js" + }, + "default": "./dist/cjs/management/api/resources/clients/resources/connections/exports.js" + }, + "./connections/directoryProvisioning": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/directoryProvisioning/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/directoryProvisioning/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/directoryProvisioning/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/directoryProvisioning/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/directoryProvisioning/exports.js" + }, + "./connections/scimConfiguration": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/scimConfiguration/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/scimConfiguration/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/scimConfiguration/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/scimConfiguration/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/scimConfiguration/exports.js" + }, + "./connections/clients": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/clients/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/clients/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/clients/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/clients/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/clients/exports.js" + }, + "./connections/keys": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/keys/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/keys/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/keys/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/keys/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/keys/exports.js" + }, + "./connections/users": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/users/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/users/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/users/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/users/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/users/exports.js" + }, + "./connections/directoryProvisioning/synchronizations": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/directoryProvisioning/resources/synchronizations/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/directoryProvisioning/resources/synchronizations/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/directoryProvisioning/resources/synchronizations/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/directoryProvisioning/resources/synchronizations/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/directoryProvisioning/resources/synchronizations/exports.js" + }, + "./connections/scimConfiguration/tokens": { + "import": { + "types": "./dist/esm/management/api/resources/connections/resources/scimConfiguration/resources/tokens/exports.d.mts", + "default": "./dist/esm/management/api/resources/connections/resources/scimConfiguration/resources/tokens/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/connections/resources/scimConfiguration/resources/tokens/exports.d.ts", + "default": "./dist/cjs/management/api/resources/connections/resources/scimConfiguration/resources/tokens/exports.js" + }, + "default": "./dist/cjs/management/api/resources/connections/resources/scimConfiguration/resources/tokens/exports.js" + }, + "./emails": { + "import": { + "types": "./dist/esm/management/api/resources/emails/exports.d.mts", + "default": "./dist/esm/management/api/resources/emails/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/emails/exports.d.ts", + "default": "./dist/cjs/management/api/resources/emails/exports.js" + }, + "default": "./dist/cjs/management/api/resources/emails/exports.js" + }, + "./emails/provider": { + "import": { + "types": "./dist/esm/management/api/resources/emails/resources/provider/exports.d.mts", + "default": "./dist/esm/management/api/resources/emails/resources/provider/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/emails/resources/provider/exports.d.ts", + "default": "./dist/cjs/management/api/resources/emails/resources/provider/exports.js" + }, + "default": "./dist/cjs/management/api/resources/emails/resources/provider/exports.js" + }, + "./eventStreams/deliveries": { + "import": { + "types": "./dist/esm/management/api/resources/eventStreams/resources/deliveries/exports.d.mts", + "default": "./dist/esm/management/api/resources/eventStreams/resources/deliveries/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/eventStreams/resources/deliveries/exports.d.ts", + "default": "./dist/cjs/management/api/resources/eventStreams/resources/deliveries/exports.js" + }, + "default": "./dist/cjs/management/api/resources/eventStreams/resources/deliveries/exports.js" + }, + "./eventStreams/redeliveries": { + "import": { + "types": "./dist/esm/management/api/resources/eventStreams/resources/redeliveries/exports.d.mts", + "default": "./dist/esm/management/api/resources/eventStreams/resources/redeliveries/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/eventStreams/resources/redeliveries/exports.d.ts", + "default": "./dist/cjs/management/api/resources/eventStreams/resources/redeliveries/exports.js" + }, + "default": "./dist/cjs/management/api/resources/eventStreams/resources/redeliveries/exports.js" + }, + "./experimentation": { + "import": { + "types": "./dist/esm/management/api/resources/experimentation/exports.d.mts", + "default": "./dist/esm/management/api/resources/experimentation/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/experimentation/exports.d.ts", + "default": "./dist/cjs/management/api/resources/experimentation/exports.js" + }, + "default": "./dist/cjs/management/api/resources/experimentation/exports.js" + }, + "./experimentation/experiments": { + "import": { + "types": "./dist/esm/management/api/resources/experimentation/resources/experiments/exports.d.mts", + "default": "./dist/esm/management/api/resources/experimentation/resources/experiments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/experimentation/resources/experiments/exports.d.ts", + "default": "./dist/cjs/management/api/resources/experimentation/resources/experiments/exports.js" + }, + "default": "./dist/cjs/management/api/resources/experimentation/resources/experiments/exports.js" + }, + "./flows/executions": { + "import": { + "types": "./dist/esm/management/api/resources/flows/resources/executions/exports.d.mts", + "default": "./dist/esm/management/api/resources/flows/resources/executions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/flows/resources/executions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/flows/resources/executions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/flows/resources/executions/exports.js" + }, + "./flows/vault": { + "import": { + "types": "./dist/esm/management/api/resources/flows/resources/vault/exports.d.mts", + "default": "./dist/esm/management/api/resources/flows/resources/vault/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/flows/resources/vault/exports.d.ts", + "default": "./dist/cjs/management/api/resources/flows/resources/vault/exports.js" + }, + "default": "./dist/cjs/management/api/resources/flows/resources/vault/exports.js" + }, + "./flows/vault/connections": { + "import": { + "types": "./dist/esm/management/api/resources/flows/resources/vault/resources/connections/exports.d.mts", + "default": "./dist/esm/management/api/resources/flows/resources/vault/resources/connections/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/flows/resources/vault/resources/connections/exports.d.ts", + "default": "./dist/cjs/management/api/resources/flows/resources/vault/resources/connections/exports.js" + }, + "default": "./dist/cjs/management/api/resources/flows/resources/vault/resources/connections/exports.js" + }, + "./groups/members": { + "import": { + "types": "./dist/esm/management/api/resources/groups/resources/members/exports.d.mts", + "default": "./dist/esm/management/api/resources/groups/resources/members/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/groups/resources/members/exports.d.ts", + "default": "./dist/cjs/management/api/resources/groups/resources/members/exports.js" + }, + "default": "./dist/cjs/management/api/resources/groups/resources/members/exports.js" + }, + "./groups/roles": { + "import": { + "types": "./dist/esm/management/api/resources/groups/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/groups/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/groups/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/groups/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/groups/resources/roles/exports.js" + }, + "./guardian/enrollments": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/enrollments/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/enrollments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/enrollments/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/enrollments/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/enrollments/exports.js" + }, + "./guardian/factors": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/exports.js" + }, + "./guardian/policies": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/policies/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/policies/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/policies/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/policies/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/policies/exports.js" + }, + "./guardian/factors/email": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/resources/email/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/resources/email/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/email/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/email/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/email/exports.js" + }, + "./guardian/factors/phone": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/resources/phone/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/resources/phone/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/phone/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/phone/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/phone/exports.js" + }, + "./guardian/factors/pushNotification": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/resources/pushNotification/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/resources/pushNotification/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/pushNotification/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/pushNotification/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/pushNotification/exports.js" + }, + "./guardian/factors/sms": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/resources/sms/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/resources/sms/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/sms/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/sms/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/sms/exports.js" + }, + "./guardian/factors/duo": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/resources/duo/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/resources/duo/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/duo/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/duo/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/duo/exports.js" + }, + "./guardian/factors/duo/settings": { + "import": { + "types": "./dist/esm/management/api/resources/guardian/resources/factors/resources/duo/resources/settings/exports.d.mts", + "default": "./dist/esm/management/api/resources/guardian/resources/factors/resources/duo/resources/settings/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/duo/resources/settings/exports.d.ts", + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/duo/resources/settings/exports.js" + }, + "default": "./dist/cjs/management/api/resources/guardian/resources/factors/resources/duo/resources/settings/exports.js" + }, + "./hooks/secrets": { + "import": { + "types": "./dist/esm/management/api/resources/hooks/resources/secrets/exports.d.mts", + "default": "./dist/esm/management/api/resources/hooks/resources/secrets/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/hooks/resources/secrets/exports.d.ts", + "default": "./dist/cjs/management/api/resources/hooks/resources/secrets/exports.js" + }, + "default": "./dist/cjs/management/api/resources/hooks/resources/secrets/exports.js" + }, + "./jobs/usersExports": { + "import": { + "types": "./dist/esm/management/api/resources/jobs/resources/usersExports/exports.d.mts", + "default": "./dist/esm/management/api/resources/jobs/resources/usersExports/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/jobs/resources/usersExports/exports.d.ts", + "default": "./dist/cjs/management/api/resources/jobs/resources/usersExports/exports.js" + }, + "default": "./dist/cjs/management/api/resources/jobs/resources/usersExports/exports.js" + }, + "./jobs/usersImports": { + "import": { + "types": "./dist/esm/management/api/resources/jobs/resources/usersImports/exports.d.mts", + "default": "./dist/esm/management/api/resources/jobs/resources/usersImports/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/jobs/resources/usersImports/exports.d.ts", + "default": "./dist/cjs/management/api/resources/jobs/resources/usersImports/exports.js" + }, + "default": "./dist/cjs/management/api/resources/jobs/resources/usersImports/exports.js" + }, + "./jobs/verificationEmail": { + "import": { + "types": "./dist/esm/management/api/resources/jobs/resources/verificationEmail/exports.d.mts", + "default": "./dist/esm/management/api/resources/jobs/resources/verificationEmail/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/jobs/resources/verificationEmail/exports.d.ts", + "default": "./dist/cjs/management/api/resources/jobs/resources/verificationEmail/exports.js" + }, + "default": "./dist/cjs/management/api/resources/jobs/resources/verificationEmail/exports.js" + }, + "./jobs/errors": { + "import": { + "types": "./dist/esm/management/api/resources/jobs/resources/errors/exports.d.mts", + "default": "./dist/esm/management/api/resources/jobs/resources/errors/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/jobs/resources/errors/exports.d.ts", + "default": "./dist/cjs/management/api/resources/jobs/resources/errors/exports.js" + }, + "default": "./dist/cjs/management/api/resources/jobs/resources/errors/exports.js" + }, + "./keys": { + "import": { + "types": "./dist/esm/management/api/resources/keys/exports.d.mts", + "default": "./dist/esm/management/api/resources/keys/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/keys/exports.d.ts", + "default": "./dist/cjs/management/api/resources/keys/exports.js" + }, + "default": "./dist/cjs/management/api/resources/keys/exports.js" + }, + "./keys/customSigning": { + "import": { + "types": "./dist/esm/management/api/resources/keys/resources/customSigning/exports.d.mts", + "default": "./dist/esm/management/api/resources/keys/resources/customSigning/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/keys/resources/customSigning/exports.d.ts", + "default": "./dist/cjs/management/api/resources/keys/resources/customSigning/exports.js" + }, + "default": "./dist/cjs/management/api/resources/keys/resources/customSigning/exports.js" + }, + "./keys/encryption": { + "import": { + "types": "./dist/esm/management/api/resources/keys/resources/encryption/exports.d.mts", + "default": "./dist/esm/management/api/resources/keys/resources/encryption/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/keys/resources/encryption/exports.d.ts", + "default": "./dist/cjs/management/api/resources/keys/resources/encryption/exports.js" + }, + "default": "./dist/cjs/management/api/resources/keys/resources/encryption/exports.js" + }, + "./keys/networkAcls": { + "import": { + "types": "./dist/esm/management/api/resources/keys/resources/networkAcls/exports.d.mts", + "default": "./dist/esm/management/api/resources/keys/resources/networkAcls/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/keys/resources/networkAcls/exports.d.ts", + "default": "./dist/cjs/management/api/resources/keys/resources/networkAcls/exports.js" + }, + "default": "./dist/cjs/management/api/resources/keys/resources/networkAcls/exports.js" + }, + "./keys/signing": { + "import": { + "types": "./dist/esm/management/api/resources/keys/resources/signing/exports.d.mts", + "default": "./dist/esm/management/api/resources/keys/resources/signing/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/keys/resources/signing/exports.d.ts", + "default": "./dist/cjs/management/api/resources/keys/resources/signing/exports.js" + }, + "default": "./dist/cjs/management/api/resources/keys/resources/signing/exports.js" + }, + "./organizations/clientGrants": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/clientGrants/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/clientGrants/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/clientGrants/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/clientGrants/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/clientGrants/exports.js" + }, + "./organizations/clients": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/clients/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/clients/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/clients/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/clients/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/clients/exports.js" + }, + "./organizations/connections": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/connections/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/connections/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/connections/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/connections/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/connections/exports.js" + }, + "./organizations/discoveryDomains": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/discoveryDomains/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/discoveryDomains/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/discoveryDomains/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/discoveryDomains/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/discoveryDomains/exports.js" + }, + "./organizations/enabledConnections": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/enabledConnections/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/enabledConnections/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/enabledConnections/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/enabledConnections/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/enabledConnections/exports.js" + }, + "./organizations/invitations": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/invitations/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/invitations/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/invitations/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/invitations/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/invitations/exports.js" + }, + "./organizations/members": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/members/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/members/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/members/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/members/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/members/exports.js" + }, + "./organizations/organizationTemplate": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/organizationTemplate/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/organizationTemplate/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/organizationTemplate/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/organizationTemplate/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/organizationTemplate/exports.js" + }, + "./organizations/groups": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/groups/exports.js" + }, + "./organizations/groups/roles": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/groups/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/groups/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/groups/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/groups/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/groups/resources/roles/exports.js" + }, + "./organizations/members/effectiveRoles": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/members/resources/effectiveRoles/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/members/resources/effectiveRoles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/exports.js" + }, + "./organizations/members/roles": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/members/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/members/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/members/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/roles/exports.js" + }, + "./organizations/members/effectiveRoles/sources": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/exports.js" + }, + "./organizations/members/effectiveRoles/sources/groups": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/members/resources/effectiveRoles/resources/sources/resources/groups/exports.js" + }, + "./organizations/roles": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/roles/exports.js" + }, + "./organizations/roles/members": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/roles/resources/members/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/roles/resources/members/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/roles/resources/members/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/roles/resources/members/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/roles/resources/members/exports.js" + }, + "./organizations/roles/groups": { + "import": { + "types": "./dist/esm/management/api/resources/organizations/resources/roles/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/organizations/resources/roles/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/organizations/resources/roles/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/organizations/resources/roles/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/organizations/resources/roles/resources/groups/exports.js" + }, + "./prompts/rendering": { + "import": { + "types": "./dist/esm/management/api/resources/prompts/resources/rendering/exports.d.mts", + "default": "./dist/esm/management/api/resources/prompts/resources/rendering/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/prompts/resources/rendering/exports.d.ts", + "default": "./dist/cjs/management/api/resources/prompts/resources/rendering/exports.js" + }, + "default": "./dist/cjs/management/api/resources/prompts/resources/rendering/exports.js" + }, + "./prompts/customText": { + "import": { + "types": "./dist/esm/management/api/resources/prompts/resources/customText/exports.d.mts", + "default": "./dist/esm/management/api/resources/prompts/resources/customText/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/prompts/resources/customText/exports.d.ts", + "default": "./dist/cjs/management/api/resources/prompts/resources/customText/exports.js" + }, + "default": "./dist/cjs/management/api/resources/prompts/resources/customText/exports.js" + }, + "./prompts/partials": { + "import": { + "types": "./dist/esm/management/api/resources/prompts/resources/partials/exports.d.mts", + "default": "./dist/esm/management/api/resources/prompts/resources/partials/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/prompts/resources/partials/exports.d.ts", + "default": "./dist/cjs/management/api/resources/prompts/resources/partials/exports.js" + }, + "default": "./dist/cjs/management/api/resources/prompts/resources/partials/exports.js" + }, + "./riskAssessments": { + "import": { + "types": "./dist/esm/management/api/resources/riskAssessments/exports.d.mts", + "default": "./dist/esm/management/api/resources/riskAssessments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/riskAssessments/exports.d.ts", + "default": "./dist/cjs/management/api/resources/riskAssessments/exports.js" + }, + "default": "./dist/cjs/management/api/resources/riskAssessments/exports.js" + }, + "./riskAssessments/settings": { + "import": { + "types": "./dist/esm/management/api/resources/riskAssessments/resources/settings/exports.d.mts", + "default": "./dist/esm/management/api/resources/riskAssessments/resources/settings/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/riskAssessments/resources/settings/exports.d.ts", + "default": "./dist/cjs/management/api/resources/riskAssessments/resources/settings/exports.js" + }, + "default": "./dist/cjs/management/api/resources/riskAssessments/resources/settings/exports.js" + }, + "./riskAssessments/settings/newDevice": { + "import": { + "types": "./dist/esm/management/api/resources/riskAssessments/resources/settings/resources/newDevice/exports.d.mts", + "default": "./dist/esm/management/api/resources/riskAssessments/resources/settings/resources/newDevice/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/riskAssessments/resources/settings/resources/newDevice/exports.d.ts", + "default": "./dist/cjs/management/api/resources/riskAssessments/resources/settings/resources/newDevice/exports.js" + }, + "default": "./dist/cjs/management/api/resources/riskAssessments/resources/settings/resources/newDevice/exports.js" + }, + "./roles/groups": { + "import": { + "types": "./dist/esm/management/api/resources/roles/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/roles/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/roles/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/roles/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/roles/resources/groups/exports.js" + }, + "./roles/permissions": { + "import": { + "types": "./dist/esm/management/api/resources/roles/resources/permissions/exports.d.mts", + "default": "./dist/esm/management/api/resources/roles/resources/permissions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/roles/resources/permissions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/roles/resources/permissions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/roles/resources/permissions/exports.js" + }, + "./roles/users": { + "import": { + "types": "./dist/esm/management/api/resources/roles/resources/users/exports.d.mts", + "default": "./dist/esm/management/api/resources/roles/resources/users/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/roles/resources/users/exports.d.ts", + "default": "./dist/cjs/management/api/resources/roles/resources/users/exports.js" + }, + "default": "./dist/cjs/management/api/resources/roles/resources/users/exports.js" + }, + "./selfServiceProfiles/customText": { + "import": { + "types": "./dist/esm/management/api/resources/selfServiceProfiles/resources/customText/exports.d.mts", + "default": "./dist/esm/management/api/resources/selfServiceProfiles/resources/customText/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/selfServiceProfiles/resources/customText/exports.d.ts", + "default": "./dist/cjs/management/api/resources/selfServiceProfiles/resources/customText/exports.js" + }, + "default": "./dist/cjs/management/api/resources/selfServiceProfiles/resources/customText/exports.js" + }, + "./selfServiceProfiles/ssoTicket": { + "import": { + "types": "./dist/esm/management/api/resources/selfServiceProfiles/resources/ssoTicket/exports.d.mts", + "default": "./dist/esm/management/api/resources/selfServiceProfiles/resources/ssoTicket/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/selfServiceProfiles/resources/ssoTicket/exports.d.ts", + "default": "./dist/cjs/management/api/resources/selfServiceProfiles/resources/ssoTicket/exports.js" + }, + "default": "./dist/cjs/management/api/resources/selfServiceProfiles/resources/ssoTicket/exports.js" + }, + "./tenants": { + "import": { + "types": "./dist/esm/management/api/resources/tenants/exports.d.mts", + "default": "./dist/esm/management/api/resources/tenants/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/tenants/exports.d.ts", + "default": "./dist/cjs/management/api/resources/tenants/exports.js" + }, + "default": "./dist/cjs/management/api/resources/tenants/exports.js" + }, + "./tenants/settings": { + "import": { + "types": "./dist/esm/management/api/resources/tenants/resources/settings/exports.d.mts", + "default": "./dist/esm/management/api/resources/tenants/resources/settings/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/tenants/resources/settings/exports.d.ts", + "default": "./dist/cjs/management/api/resources/tenants/resources/settings/exports.js" + }, + "default": "./dist/cjs/management/api/resources/tenants/resources/settings/exports.js" + }, + "./users/authenticationMethods": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/authenticationMethods/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/authenticationMethods/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/authenticationMethods/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/authenticationMethods/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/authenticationMethods/exports.js" + }, + "./users/authenticators": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/authenticators/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/authenticators/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/authenticators/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/authenticators/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/authenticators/exports.js" + }, + "./users/connectedAccounts": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/connectedAccounts/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/connectedAccounts/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/connectedAccounts/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/connectedAccounts/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/connectedAccounts/exports.js" + }, + "./users/effectivePermissions": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/effectivePermissions/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/effectivePermissions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/exports.js" + }, + "./users/effectiveRoles": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/effectiveRoles/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/effectiveRoles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/exports.js" + }, + "./users/enrollments": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/enrollments/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/enrollments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/enrollments/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/enrollments/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/enrollments/exports.js" + }, + "./users/groups": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/groups/exports.js" + }, + "./users/identities": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/identities/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/identities/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/identities/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/identities/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/identities/exports.js" + }, + "./users/logs": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/logs/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/logs/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/logs/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/logs/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/logs/exports.js" + }, + "./users/multifactor": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/multifactor/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/multifactor/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/multifactor/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/multifactor/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/multifactor/exports.js" + }, + "./users/organizations": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/organizations/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/organizations/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/organizations/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/organizations/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/organizations/exports.js" + }, + "./users/permissions": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/permissions/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/permissions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/permissions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/permissions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/permissions/exports.js" + }, + "./users/riskAssessments": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/riskAssessments/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/riskAssessments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/riskAssessments/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/riskAssessments/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/riskAssessments/exports.js" + }, + "./users/roles": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/roles/exports.js" + }, + "./users/refreshToken": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/refreshToken/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/refreshToken/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/refreshToken/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/refreshToken/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/refreshToken/exports.js" + }, + "./users/sessions": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/sessions/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/sessions/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/sessions/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/sessions/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/sessions/exports.js" + }, + "./users/effectivePermissions/sources": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/effectivePermissions/resources/sources/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/effectivePermissions/resources/sources/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/resources/sources/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/resources/sources/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/resources/sources/exports.js" + }, + "./users/effectivePermissions/sources/roles": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/effectivePermissions/resources/sources/resources/roles/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/effectivePermissions/resources/sources/resources/roles/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/resources/sources/resources/roles/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/resources/sources/resources/roles/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/effectivePermissions/resources/sources/resources/roles/exports.js" + }, + "./users/effectiveRoles/sources": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/effectiveRoles/resources/sources/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/effectiveRoles/resources/sources/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/resources/sources/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/resources/sources/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/resources/sources/exports.js" + }, + "./users/effectiveRoles/sources/groups": { + "import": { + "types": "./dist/esm/management/api/resources/users/resources/effectiveRoles/resources/sources/resources/groups/exports.d.mts", + "default": "./dist/esm/management/api/resources/users/resources/effectiveRoles/resources/sources/resources/groups/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/resources/sources/resources/groups/exports.d.ts", + "default": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/resources/sources/resources/groups/exports.js" + }, + "default": "./dist/cjs/management/api/resources/users/resources/effectiveRoles/resources/sources/resources/groups/exports.js" + }, + "./verifiableCredentials": { + "import": { + "types": "./dist/esm/management/api/resources/verifiableCredentials/exports.d.mts", + "default": "./dist/esm/management/api/resources/verifiableCredentials/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/verifiableCredentials/exports.d.ts", + "default": "./dist/cjs/management/api/resources/verifiableCredentials/exports.js" + }, + "default": "./dist/cjs/management/api/resources/verifiableCredentials/exports.js" + }, + "./verifiableCredentials/verification": { + "import": { + "types": "./dist/esm/management/api/resources/verifiableCredentials/resources/verification/exports.d.mts", + "default": "./dist/esm/management/api/resources/verifiableCredentials/resources/verification/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/verifiableCredentials/resources/verification/exports.d.ts", + "default": "./dist/cjs/management/api/resources/verifiableCredentials/resources/verification/exports.js" + }, + "default": "./dist/cjs/management/api/resources/verifiableCredentials/resources/verification/exports.js" + }, + "./verifiableCredentials/verification/templates": { + "import": { + "types": "./dist/esm/management/api/resources/verifiableCredentials/resources/verification/resources/templates/exports.d.mts", + "default": "./dist/esm/management/api/resources/verifiableCredentials/resources/verification/resources/templates/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/verifiableCredentials/resources/verification/resources/templates/exports.d.ts", + "default": "./dist/cjs/management/api/resources/verifiableCredentials/resources/verification/resources/templates/exports.js" + }, + "default": "./dist/cjs/management/api/resources/verifiableCredentials/resources/verification/resources/templates/exports.js" + }, + "./package.json": "./package.json", + "./legacy": { + "types": "./legacy/exports/index.d.ts", + "import": { + "types": "./legacy/exports/index.d.mts", + "default": "./legacy/exports/index.mjs" + }, + "require": { + "types": "./legacy/exports/index.d.ts", + "default": "./legacy/exports/index.js" + } + }, + "./management": { + "import": { + "types": "./dist/esm/management/index.d.mts", + "default": "./dist/esm/management/index.mjs" + }, + "require": { + "types": "./dist/cjs/management/index.d.ts", + "default": "./dist/cjs/management/index.js" + }, + "default": "./dist/cjs/management/index.js" + } + }, + "files": [ + "legacy", + "package.json", + "dist", + "reference.md", + "README.md", + "LICENSE" + ], + "scripts": { + "format": "prettier . --write --ignore-unknown", + "format:check": "prettier . --check --ignore-unknown", + "lint": "eslint . --ext .js,.ts,.tsx", + "lint:fix": "eslint . --ext .js,.ts,.tsx --fix", + "check": "yarn format:check", + "check:fix": "yarn format", + "build": "yarn build:cjs && yarn build:esm", + "build:cjs": "tsc --project ./tsconfig.cjs.json", + "build:esm": "tsc --project ./tsconfig.esm.json && node scripts/rename-to-esm-files.js dist/esm", + "test": "jest --config jest.config.mjs", + "test:unit": "jest --selectProjects unit", + "test:wire": "jest --selectProjects wire", + "prepare": "husky", + "lint:check": "eslint . --ext .js,.ts,.tsx", + "lint:package": "publint --pack npm", + "test:coverage": "jest --config jest.config.mjs --coverage", + "test:coverage:unit": "jest --selectProjects unit --coverage", + "test:coverage:browser": "jest --selectProjects browser --coverage", + "test:coverage:wire": "jest --selectProjects wire --coverage", + "docs": "typedoc", + "docs:clean": "rm -rf docs", + "docs:build": "yarn docs:clean && yarn docs", + "precommit": "lint-staged", + "validate": "yarn lint:check && yarn format --check && yarn build && yarn test && yarn lint:package" + }, + "dependencies": { + "uuid": "^11.1.1", + "jose": "^5.0.0", + "auth0-legacy": "npm:auth0@^4.37.1" + }, + "devDependencies": { + "webpack": "^5.105.4", + "ts-loader": "^9.5.4", + "jest": "^29.7.0", + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.14", + "ts-jest": "^29.3.4", + "jest-environment-jsdom": "^29.7.0", + "msw": "2.11.2", + "@types/node": "^20.0.0", + "typescript": "~5.9.3", + "prettier": "3.8.1", + "typedoc": "^0.28.7", + "typedoc-plugin-missing-exports": "^4.0.0", + "nock": "^14.0.6", + "undici": "^7.12.0", + "@eslint/js": "^9.32.0", + "@typescript-eslint/eslint-plugin": "^8.38.0", + "@typescript-eslint/parser": "^8.38.0", + "eslint": "^9.32.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.3", + "husky": "^9.1.7", + "lint-staged": "^16.1.4", + "publint": "^0.3.12" + }, + "browser": { + "fs": false, + "os": false, + "path": false, + "stream": false, + "crypto": false + }, + "packageManager": "yarn@1.22.22", + "engines": { + "node": "^20.19.0 || ^22.12.0 || ^24.0.0 || ^26.0.0" + }, + "sideEffects": false, + "bugs": { + "url": "https://github.com/auth0/node-auth0/issues" + }, + "homepage": "https://github.com/auth0/node-auth0", + "keywords": [ + "auth0", + "authentication", + "login", + "auth", + "jwt", + "management api", + "json web token" + ], + "description": "Auth0 Node.js SDK for the Management API v2.", + "lint-staged": { + "*.{js,ts,tsx}": [ + "eslint --fix", + "prettier --write" + ], + "*.{json,md,yml,yaml}": [ + "prettier --write" + ] + } + } + src/management/version.ts: | + export const SDK_VERSION = "7.1.0"; + status: unresolved diff --git a/.shiprc b/.shiprc new file mode 100644 index 0000000000..d1791e98e5 --- /dev/null +++ b/.shiprc @@ -0,0 +1,6 @@ +{ + "files": { + ".version": [], + "src/management/version.ts": [] + } +} diff --git a/AUTH_MIGRATION_GUIDE.md b/AUTH_MIGRATION_GUIDE.md new file mode 100644 index 0000000000..64626f8067 --- /dev/null +++ b/AUTH_MIGRATION_GUIDE.md @@ -0,0 +1,14 @@ +# Authentication Migration Guide + +This guide lives in the [`auth-migration/`](./auth-migration/) directory. + +**→ Start here: [`auth-migration/index.md`](./auth-migration/index.md)**: migrate your authentication code off the `auth0` package to [`@auth0/auth0-auth-js`](https://github.com/auth0/auth0-auth-js) (stateless token grants) or [`@auth0/auth0-server-js`](https://github.com/auth0/auth0-auth-js/tree/main/packages/auth0-server-js) (server-managed sessions). + +The directory contains: + +- [`auth-migration/index.md`](./auth-migration/index.md): the main guide covering OIDC token grants and the four cross-cutting breaking changes. +- [`auth-migration/authentication-flows.md`](./auth-migration/authentication-flows.md): database, passwordless, backchannel (CIBA), token exchange, and `UserInfoClient`. +- [`auth-migration/server-side-sessions.md`](./auth-migration/server-side-sessions.md): the `@auth0/auth0-server-js` session layer. +- [`auth-migration/troubleshooting.md`](./auth-migration/troubleshooting.md): FAQ and gotchas. + +> **Migrating with an AI agent?** Point it at the Auth0 migration skill: the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`. diff --git a/auth-migration/authentication-flows.md b/auth-migration/authentication-flows.md new file mode 100644 index 0000000000..6f7bace2a1 --- /dev/null +++ b/auth-migration/authentication-flows.md @@ -0,0 +1,234 @@ +# Migrating the other authentication flows + +This is the incremental part of the [Authentication Migration Guide](./index.md). Start with the guide's [OIDC token grants](./index.md#oidc-token-grants) and cross-cutting breaking changes before you touch anything here. Everything below builds on those changes, so apply them to every rewrite on this page too. + +> **Migrating with an AI agent?** Point it at the Auth0 migration skill (the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`). + +Migrate one flow at a time. Only the flows your app actually uses need attention; skip the rest. + +- [Database connections](#database-connections) +- [Passwordless](#passwordless) +- [Backchannel authentication (CIBA)](#backchannel-authentication-ciba) +- [Token exchange (RFC 8693)](#token-exchange-rfc-8693) +- [UserInfoClient](#userinfoclient) +- [Quick lookup table](#quick-lookup-table) + +Unless a row routes explicitly to `@auth0/auth0-server-js`, the replacement lives on the `@auth0/auth0-auth-js` `AuthClient` (or a sub-client: `authClient.database`, `authClient.passwordless`, `authClient.mfa`, `authClient.passkey`). + +## Database connections + +Database connection operations move to the `authClient.database` sub-client. Names and required parameters stay the same; only casing and return shape change. + +### `database.signUp` → `authClient.database.signUp` + +```ts +// before +const resp = await auth0.database.signUp({ + email, + password, + connection: "Username-Password-Authentication", + given_name: "Ada", + family_name: "Lovelace", + user_metadata: { plan: "free" }, +}); +const userId = resp.data.id; +// after +const result = await authClient.database.signUp({ + email, + password, + connection: "Username-Password-Authentication", + givenName: "Ada", + familyName: "Lovelace", + userMetadata: { plan: "free" }, +}); +const userId = result.id; +``` + +> ID normalization is preserved: node-auth0 mapped the server's `_id | user_id | id` onto a single `id`. The new SDK does the same, so `result.id` is always present. Do not add your own `_id` fallback. + +### `database.changePassword` → `authClient.database.changePassword` + +node-auth0 returned a `TextApiResponse` (read via `.data`); the new SDK returns the plain `string` directly. + +```ts +// before +const resp = await auth0.database.changePassword({ email, connection: "Username-Password-Authentication" }); +const message = resp.data; // plain-text confirmation +// after +const message = await authClient.database.changePassword({ email, connection: "Username-Password-Authentication" }); +``` + +> `changePassword` requires `connection` plus at least one of `email` or `username`: either identifier is accepted, not `email` alone. + +## Passwordless + +node-auth0 lumped "start" (send the code or link) and "login" (redeem the code) onto one sub-client. The new SDK splits them: starting stays on `authClient.passwordless`; redeeming a code becomes a top-level grant method on `AuthClient`. + +### `passwordless.sendEmail` → `authClient.passwordless.sendEmail` + +```ts +// before +await auth0.passwordless.sendEmail({ email, send: "code" }); +// after +await authClient.passwordless.sendEmail({ email, send: "code" }); +``` + +> Default changed: node-auth0 defaulted `send` to `'link'` (magic link). The new SDK defaults `send` to `'code'` (one-time password). If you relied on the implicit default to send magic links, set `send: 'link'` explicitly. + +### `passwordless.sendSMS` → `authClient.passwordless.sendSms` + +Note the casing change: `sendSMS` → `sendSms`, and `phone_number` → `phoneNumber`. + +```ts +// before +await auth0.passwordless.sendSMS({ phone_number: "+15551234567" }); +// after +await authClient.passwordless.sendSms({ phoneNumber: "+15551234567" }); +``` + +### `passwordless.loginWithEmail` → `getTokenByPasswordlessEmail` + +Redeeming the one-time password is now a grant method on `AuthClient`, not on the passwordless sub-client. + +```ts +// before +const resp = await auth0.passwordless.loginWithEmail({ email, code, audience, scope }); +const token = resp.data.access_token; +// after +const tokens = await authClient.getTokenByPasswordlessEmail({ email, code, audience, scope }); +const token = tokens.accessToken; +``` + +### `passwordless.loginWithSMS` → `getTokenByPasswordlessSms` + +```ts +// before +const resp = await auth0.passwordless.loginWithSMS({ phone_number, code }); +// after +const tokens = await authClient.getTokenByPasswordlessSms({ phoneNumber, code }); +``` + +> Session apps: `@auth0/auth0-server-js` exposes `startPasswordless` / `completePasswordless` / `completePasswordlessMagicLink`, which both send the code and establish a session. Use those instead of the two-step auth-js flow when the SDK owns the session. See [Migrating session apps](./server-side-sessions.md). + +## Backchannel authentication (CIBA) + +CIBA is Client-Initiated Backchannel Authentication. + +### `backchannel.authorize` → `initiateBackchannelAuthentication` + +```ts +// before +const resp = await auth0.backchannel.authorize({ + binding_message: "ABC123", + scope: "openid", + userId: "auth0|123", +}); +const authReqId = resp.auth_req_id; +// after +const { authReqId, expiresIn, interval } = await authClient.initiateBackchannelAuthentication({ + bindingMessage: "ABC123", + loginHint: { sub: "auth0|123" }, // login_hint is an object with `sub`, not a bare string + authorizationParams: { scope: "openid" }, // scope goes here, NOT as a top-level key +}); +``` + +### `backchannel.backchannelGrant` → `backchannelAuthenticationGrant` + +```ts +// before +const resp = await auth0.backchannel.backchannelGrant({ auth_req_id: authReqId }); +// after +const tokens = await authClient.backchannelAuthenticationGrant({ authReqId }); +``` + +> One-shot convenience: `authClient.backchannelAuthentication({ ... })` initiates and polls to completion, returning a `TokenResponse`. Use it if your code did the initiate-then-poll loop by hand. +> +> Session apps: for CIBA that also establishes a session, see [Migrating session apps](./server-side-sessions.md). + +## Token exchange (RFC 8693) + +```ts +// before +const resp = await auth0.tokenExchange.exchangeToken({ + subject_token_type: "urn:example:custom", + subject_token: token, + audience: "https://api.example.com", + scope: "read", +}); +// after +const tokens = await authClient.exchangeToken({ + subjectTokenType: "urn:example:custom", + subjectToken: token, + audience: "https://api.example.com", + scope: "read", +}); +``` + +> `exchangeToken` is overloaded: a custom-exchange profile shape (`subjectTokenType` + `subjectToken` + `audience`) and a Token Vault shape (`connection` present). Presence of `connection` routes to the vault path. The custom-exchange profile is the RFC 8693 replacement for `tokenExchange.exchangeToken`. +> +> Session apps: `@auth0/auth0-server-js` exposes `loginWithCustomTokenExchange` (exchange, then establish a session) and `customTokenExchange` (exchange, then return tokens with no session). + +## UserInfoClient + +The standalone `UserInfoClient` from node-auth0 does not exist in the new SDK. Choose the replacement based on what the app needs: + +| Your intent | Replacement | +| --- | --- | +| Wanted user profile claims right after login | Read `TokenResponse.claims` from the grant result; the SDK already decodes the ID token. No extra `/userinfo` round-trip needed. **Preferred.** | +| Wanted a live `/userinfo` response for an arbitrary access token | `await authClient.getUserInfo({ accessToken })`, a direct method on `AuthClient`. | +| Wanted the profile in a server-rendered app with a session | `await serverClient.getUser()` returns the stored user claims from the session. | + +**Before (node-auth0):** + +```ts +import { UserInfoClient } from "auth0"; +const userInfo = new UserInfoClient({ domain }); +const resp = await userInfo.getUserInfo(accessToken); +const profile = resp.data; // { sub, name, email, ... } +``` + +**After (preferred): use the claims you already have:** + +```ts +const tokens = await authClient.getTokenByCode(callbackUrl, {}); +const profile = tokens.claims; // { sub, name, email, ... } decoded from the id_token +``` + +**After (direct method):** for when you only have an access token: + +```ts +// Takes an options object: { accessToken, expectedSubject? } +const profile = await authClient.getUserInfo({ accessToken }); +``` + +> Prefer reading `claims` over any `/userinfo` call: it avoids a network round-trip and the claims are already validated by the SDK. + +## Quick lookup table + +The complete node-auth0 → new SDK map, including the OIDC methods covered in the main guide. + +| node-auth0 | new SDK equivalent | Layer | +| --- | --- | --- | +| `oauth.authorizationCodeGrant` | `authClient.getTokenByCode(url, opts)` | auth-js | +| `oauth.authorizationCodeGrantWithPKCE` | `authClient.getTokenByCode(url, { codeVerifier })` | auth-js | +| `oauth.refreshTokenGrant` | `authClient.getTokenByRefreshToken({ refreshToken })` | auth-js | +| `oauth.passwordGrant` | `authClient.getTokenByPassword({ ... })` | auth-js | +| `oauth.clientCredentialsGrant` | `authClient.getTokenByClientCredentials({ audience })` | auth-js | +| `oauth.revokeRefreshToken` | `authClient.revokeToken({ token })` / `serverClient.revokeRefreshToken()` | auth-js / server-js | +| `oauth.tokenForConnection` | `authClient.exchangeToken({ connection, ... })` | auth-js | +| `oauth.pushedAuthorization` | `authClient.buildAuthorizationUrl({ pushedAuthorizationRequests: true })` | auth-js | +| `database.signUp` | `authClient.database.signUp({ ... })` | auth-js | +| `database.changePassword` | `authClient.database.changePassword({ ... })` | auth-js | +| `passwordless.sendEmail` | `authClient.passwordless.sendEmail({ ... })` | auth-js | +| `passwordless.sendSMS` | `authClient.passwordless.sendSms({ phoneNumber })` | auth-js | +| `passwordless.loginWithEmail` | `authClient.getTokenByPasswordlessEmail({ ... })` | auth-js | +| `passwordless.loginWithSMS` | `authClient.getTokenByPasswordlessSms({ ... })` | auth-js | +| `backchannel.authorize` | `authClient.initiateBackchannelAuthentication({ ... })` | auth-js | +| `backchannel.backchannelGrant` | `authClient.backchannelAuthenticationGrant({ authReqId })` | auth-js | +| `tokenExchange.exchangeToken` | `authClient.exchangeToken({ subjectTokenType, subjectToken, audience })` | auth-js | +| `UserInfoClient.getUserInfo` | `TokenResponse.claims` (preferred) / `authClient.getUserInfo({ accessToken })` / `serverClient.getUser()` | auth-js / server-js | +| (no equivalent): build `/authorize` URL | `authClient.buildAuthorizationUrl({ ... })` | auth-js | +| (no equivalent): build `/v2/logout` URL | `authClient.buildLogoutUrl({ returnTo })` | auth-js | +| `ManagementClient.*` | **not migrated, stays on `auth0`** | n/a | + +When you finish a flow, return to the [verification checklist](./index.md#verification-checklist) and confirm the four cross-cutting changes for every call site you touched. diff --git a/auth-migration/index.md b/auth-migration/index.md new file mode 100644 index 0000000000..85a11c45c7 --- /dev/null +++ b/auth-migration/index.md @@ -0,0 +1,732 @@ +# Authentication Migration Guide + +A guide to migrating your authentication code off the `auth0` package (node-auth0) to the modern Auth0 server SDKs: [`@auth0/auth0-auth-js`](https://github.com/auth0/auth0-auth-js) for stateless token grants, and [`@auth0/auth0-server-js`](https://github.com/auth0/auth0-auth-js/tree/main/packages/auth0-server-js) for server-managed sessions. + +> **Migrating with an AI agent?** Point it at the Auth0 migration skill first. The skill lives in [`auth0/agent-skills`](https://github.com/auth0/agent-skills) as the `auth0` skill (migration intent: `migrate-node-auth0`). It encodes the target-SDK routing, the four cross-cutting breaking changes, the method-by-method mapping, and a build-until-green verify loop. + +## Contents + +- [How to use this guide](#how-to-use-this-guide) +- [Overview](#overview) + - [Who this is for](#who-this-is-for) + - [Scope](#scope) +- [Choosing your target SDK](#choosing-your-target-sdk) +- [Prerequisites](#prerequisites) +- [Installation and constructor mapping](#installation-and-constructor-mapping) +- [OIDC token grants](#oidc-token-grants) + - [Optional: migrate only OIDC while staying on v6](#optional-migrate-only-oidc-while-staying-on-v6) +- [Cross-cutting breaking changes](#cross-cutting-breaking-changes) + - [1. Return shape](#1-return-shape) + - [2. Casing](#2-casing) + - [3. Token expiry](#3-token-expiry) + - [4. Error model](#4-error-model) +- [Verification checklist](#verification-checklist) +- [Continue the migration](#continue-the-migration) + - [Other authentication flows](#other-authentication-flows) + - [Server-side sessions](#server-side-sessions) + - [Troubleshooting](#troubleshooting) + +## How to use this guide + +This is a reference, not a linear read. You do not have to work through it top to bottom; migrate only the flows your app actually uses, in whatever order suits you. Most apps finish after the [OIDC token grants](#oidc-token-grants) section. + +The work falls into three phases: + +| Phase | What you do | Where | +| --- | --- | --- | +| **Before**: orient and set up | Pick your target SDK, check prerequisites, install the package, map constructor options. | [Choosing your target SDK](#choosing-your-target-sdk), [Prerequisites](#prerequisites), [Installation and constructor mapping](#installation-and-constructor-mapping) | +| **During**: rewrite call sites | Rewrite the OIDC token grants (in this file), then the other flows and the session layer as needed. Apply the four cross-cutting breaking changes to every call site. | [OIDC token grants](#oidc-token-grants), [Cross-cutting breaking changes](#cross-cutting-breaking-changes), [`authentication-flows.md`](./authentication-flows.md), [`server-side-sessions.md`](./server-side-sessions.md) | +| **After**: verify | Run the build-until-green checklist; confirm no residue and that `ManagementClient` code is untouched. | [Verification checklist](#verification-checklist) | + +Suggested order: start with the OIDC grants and cross-cutting changes (the whole job for most apps), then the [other flows](./authentication-flows.md) you actually use, then [session apps](./server-side-sessions.md) if you want the SDK to own sessions. Stuck? See [`troubleshooting.md`](./troubleshooting.md). + +## Overview + +node-auth0's `AuthenticationClient` is a stateless HTTP client. Every method is a single call to an Auth0 Authentication API endpoint that returns a response object. It has no notion of a logged-in user, no session, no cookie, no token store, and no automatic refresh. Anything stateful in a node-auth0 app (persisting tokens, deciding when to refresh, tracking the login across requests) was written by you *around* node-auth0. + +The modern stack splits those two concerns into two packages: + +- `@auth0/auth0-auth-js` is the stateless token layer. It is the direct successor to `AuthenticationClient`: the same "one method equals one API call equals one result" model, with modern ergonomics (camelCase, typed errors, direct return values, per-request options). +- `@auth0/auth0-server-js` is a stateful session layer built on top of auth0-auth-js. It owns the login redirect flow, a pluggable state/transaction store, cookie handling, automatic token refresh, and logout. It is the successor to the *session code you hand-rolled*, not to `AuthenticationClient` itself. + +### Who this is for + +You are running a Node.js backend that imports the `auth0` package and calls `AuthenticationClient` (or `UserInfoClient`) to perform token grants, database signup, passwordless, CIBA, token exchange, or userinfo lookups. You want to move that code to the current first-party server SDKs. This is a surgical rewrite of the authentication layer: routes, controllers, business logic, data access, and framework wiring stay as they are. You touch the smallest possible surface: the files that import and call node-auth0's Authentication API. + +### Scope + +In scope: + +- `AuthenticationClient` and its sub-clients: `.oauth`, `.database`, `.passwordless`, `.backchannel`, `.tokenExchange` +- `UserInfoClient` +- The auth error types (`AuthApiError`) and token-validation types (`IDTokenValidateOptions`, `IdTokenValidatorError`) + +Out of scope, do not touch: + +- `ManagementClient` (Management API v2). It is not being migrated and stays on the `auth0` package. +- Application routes, view/controller logic, database code, and any non-auth use of the `auth0` package. + +> If a file uses `ManagementClient`, leave that code alone. Only rewrite the `AuthenticationClient` / `UserInfoClient` parts. + +## Choosing your target SDK + +The routing question is: do you want to keep owning your session, or hand that responsibility to the SDK? + +### Decision table + +| If your code… | Migrate to | Why | +| --- | --- | --- | +| Only performs token grants / DB signup / passwordless / userinfo and manages its own session (or is a machine-to-machine service backend) | `@auth0/auth0-auth-js` | Direct, near 1:1 replacement for `AuthenticationClient`. Same stateless model. | +| Wants the SDK to own the login redirect flow, session storage, cookies, token refresh, and logout (a server-rendered web app) | `@auth0/auth0-server-js` | Adds a session layer node-auth0 never had. This is a rewrite of the session handling, not a method-for-method port. | + +**Default recommendation:** start with `@auth0/auth0-auth-js` for a faithful parity migration. Choose `@auth0/auth0-server-js` only when you currently hand-roll session/cookie/refresh logic around node-auth0 and would benefit from the SDK owning it. + +### Signals + +Signals that point to auth0-auth-js: + +- Predominant use is `clientCredentialsGrant` (machine-to-machine). There is no user, so there is no session to own. +- The app already has a session framework it is happy with and only calls node-auth0 for token grants. +- The app is an API, worker, or CLI, not a browser-facing web server. +- You want the smallest, most mechanical, lowest-risk migration. + +Signals that point to auth0-server-js: + +- The app performs a browser redirect login and reads `req.session.user` (or equivalent) on later requests. +- You wrote refresh-on-expiry logic, a token cache, or logout-with-revocation by hand. +- You use `express-openid-connect` today and want a first-party, framework-agnostic replacement. +- You are on a server framework (Express, Fastify, Hono, Next.js) and want the SDK to manage cookies. + +### Mixing both + +A single app can use both: auth0-server-js for the user-facing login/session, and auth0-auth-js directly for a separate machine-to-machine `clientCredentialsGrant` to call another API. `ServerClient` even exposes the underlying `AuthClient` via `serverClient.authClient` for occasional low-level needs. Do not force everything onto one package. + +## Prerequisites + +### Node.js version + +Both target SDKs need Node.js 20 LTS or newer. Verify the project's runtime before installing. + +### SDK versions + +- `@auth0/auth0-auth-js` >= `1.13.0` +- `@auth0/auth0-server-js` >= `1.13.0` + +Both are published on npm; install the current `latest`. `1.13.0` is the floor for the full API surface used in this guide (`getUserInfo`, per-request `RequestOptions`, and `fullResponse`). + +## Installation and constructor mapping + +Add the target package: + +```bash +# auth-js target (stateless token grants) +npm install @auth0/auth0-auth-js + +# server-js target (server-managed sessions), pulls in auth0-auth-js transitively +npm install @auth0/auth0-server-js +``` + +Keep the `auth0` package installed if the app still uses `ManagementClient`. + +### Imports + +```ts +// before +import { AuthenticationClient, UserInfoClient, AuthApiError } from "auth0"; + +// after: auth-js target +import { AuthClient, TokenByCodeError, isMfaRequiredError } from "@auth0/auth0-auth-js"; + +// after: server-js target +import { ServerClient } from "@auth0/auth0-server-js"; +``` + +> Keep the `auth0` import if the file also uses `ManagementClient`. It is correct for a file to import both `auth0` (for `ManagementClient`) and `@auth0/auth0-auth-js` (for authentication). Only remove the `auth0` import from files where it was used *solely* for `AuthenticationClient` / `UserInfoClient`. + +### AuthClient options + +The constructor options mostly carry over with camelCase names. A few are renamed or dropped. + +**Before (node-auth0):** + +```ts +new AuthenticationClient({ + domain: "tenant.us.auth0.com", + clientId: "...", + clientSecret: "...", // OR clientAssertionSigningKey + clientAssertionSigningKey: "...", + clientAssertionSigningAlg: "RS256", + idTokenSigningAlg: "RS256", // for manual id_token validation + clockTolerance: 60, // seconds, for validation + useMTLS: false, + telemetry: true, + headers: { "X-Custom": "..." }, // sent on every request + timeoutDuration: 10000, // ms + retry: { + /* ... */ + }, + agent: undiciDispatcher, + fetch: customFetch, + middleware: [ + /* ... */ + ], +}); +``` + +**After (auth0-auth-js):** + +```ts +import { AuthClient } from "@auth0/auth0-auth-js"; + +new AuthClient({ + domain: "tenant.us.auth0.com", // same (no scheme) + clientId: "...", // same + clientSecret: "...", // same + clientAssertionSigningKey: "...", // same (string | CryptoKey) + clientAssertionSigningAlg: "RS256", // same + authorizationParams: { + // NEW: default scope/audience/redirect_uri for URL builders + scope: "openid profile email", + audience: "https://api.example.com", + redirect_uri: "https://app.example.com/callback", + }, + useMtls: false, // RENAMED from useMTLS (lowercase tls) + customFetch: fetch, // RENAMED from fetch + telemetry: { + /* ... */ + }, // structured TelemetryConfig + discoveryCache: { ttl, maxEntries }, // NEW: OIDC discovery / JWKS cache +}); +``` + +Option-by-option: + +| node-auth0 | auth0-auth-js | Notes | +| --- | --- | --- | +| `domain` | `domain` | Unchanged. No `https://` scheme. | +| `clientId` | `clientId` | Unchanged. | +| `clientSecret` | `clientSecret` | Unchanged. | +| `clientAssertionSigningKey` | `clientAssertionSigningKey` | Unchanged. Now also accepts a `CryptoKey`. | +| `clientAssertionSigningAlg` | `clientAssertionSigningAlg` | Unchanged. | +| `useMTLS` | `useMtls` | Renamed (casing). | +| `fetch` | `customFetch` | Renamed. | +| `telemetry: boolean` | `telemetry: TelemetryConfig` | Now a structured object. | +| `headers` (global) | per-request `RequestOptions.headers` | Moved to per-request options; set per call site rather than globally. | +| `timeoutDuration` | per-request `RequestOptions.signal` | Use an `AbortSignal.timeout(ms)` on the call. | +| `retry` | configure via `customFetch` | Wrap your fetch with retry if needed. | +| `agent` | configure via `customFetch` | Set the dispatcher inside your custom fetch. | +| `middleware` | `customFetch` | Compose behavior in the fetch wrapper. | +| `idTokenSigningAlg` | (internal) | ID-token validation is internal; read `TokenResponse.claims`. | +| `clockTolerance` | (internal) | Handled internally during validation. | + +### ServerClient options + +`ServerClient` wraps an `AuthClient` and adds the session machinery. It shares the auth options and adds required stores. This constructor and the stores it needs are covered in [`server-side-sessions.md`](./server-side-sessions.md); reach for it only when you route to server-js. + +### Global config to per-request options + +node-auth0's global constructor options for `headers`, `timeoutDuration`, `agent`, `retry`, and `middleware` have no direct constructor equivalents in auth0-auth-js. Instead, the new SDK's methods accept a trailing `RequestOptions` parameter: + +```ts +import type { RequestOptions } from "@auth0/auth0-server-js"; // or '@auth0/auth0-auth-js' + +const tokens = await authClient.getTokenByClientCredentials( + { audience: "https://api.example.com" }, + { + headers: { "X-Custom": "value" }, + signal: AbortSignal.timeout(5000), // timeout in ms + } satisfies RequestOptions, +); +``` + +`@auth0/auth0-server-js` re-exports `RequestOptions`, `ApiResponse`, and `FullResponseOption` from `@auth0/auth0-auth-js`, so you can import any of them from either package. + +Arity rule: MFA methods (`authClient.mfa.*`) take `requestOptions` as the 2nd argument; store-first methods (session-owning methods on `serverClient`) take it as the 3rd argument after the store context; cache hits ignore it entirely. + +Common patterns: + +- Global headers: apply via `RequestOptions.headers` on each call that needs it, or wrap `customFetch` once to inject it everywhere. +- Timeout: replace `timeoutDuration: 10000` with `signal: AbortSignal.timeout(10000)` on the call. +- Agent (Node.js dispatcher): wrap `customFetch` to inject the agent into the underlying HTTP transport. +- Retry / middleware: compose behavior in a `customFetch` wrapper passed either at construction or per request. + +## OIDC token grants + +This is the core of the migration and, for most apps, the whole of it. These are the `AuthenticationClient.oauth.*` grants that drive OpenID Connect login and machine-to-machine token acquisition. All of them move onto the `AuthClient` instance directly (not a sub-client). + +Before you touch any method, internalize the four [cross-cutting breaking changes](#cross-cutting-breaking-changes); they apply to *every* rewrite here and on the incremental pages. + +Naming conventions used throughout: + +| node-auth0 | new SDKs | +| --- | --- | +| Params and response fields use the snake_case wire shape: `client_id`, `refresh_token`, `access_token`, `expires_in`, `phone_number` | camelCase: `clientId`, `refreshToken`, `accessToken`, `expiresAt`, `phoneNumber` | +| Methods take a `bodyParameters` object (+ optional `initOverrides`) | Methods take a single `options` object (+ optional trailing `RequestOptions` for per-request `signal`, `headers`, `customFetch`) | +| Every method returns a `JSONApiResponse` / `VoidApiResponse` / `TextApiResponse` wrapper | Methods return the domain object directly (`TokenResponse`, `SignUpResult`, `string`, `void`) | + +### `oauth.authorizationCodeGrant` → `getTokenByCode` + +The single most important semantic change in the whole migration. In node-auth0 you pass the raw authorization `code` (and `redirect_uri`) that you extracted from the callback query string yourself. In auth0-auth-js you pass the entire callback `URL`; the SDK extracts `code` and enforces PKCE, and `redirect_uri` comes from the `AuthClient` config / `authorizationParams`. The stateless `AuthClient` does **not** validate OAuth `state` — that is your responsibility (or use `@auth0/auth0-server-js` `completeInteractiveLogin`, which owns a transaction store and validates `state` for you). + +**Before (node-auth0):** + +```ts +import { AuthenticationClient } from "auth0"; + +const auth0 = new AuthenticationClient({ domain, clientId, clientSecret }); + +// You parsed `code` out of the callback URL yourself. +const resp = await auth0.oauth.authorizationCodeGrant({ + code, + redirect_uri: "https://app.example.com/callback", +}); +const accessToken = resp.data.access_token; +const expiresIn = resp.data.expires_in; // relative seconds +const reqId = resp.headers.get("x-request-id"); // metadata on success +``` + +**After (auth0-auth-js):** + +```ts +import { AuthClient } from "@auth0/auth0-auth-js"; + +const authClient = new AuthClient({ domain, clientId, clientSecret }); + +// `url` is a URL object for the full incoming request URL, +// e.g. new URL(req.url, `https://${req.headers.host}`) +const tokens = await authClient.getTokenByCode(url, { + // options; e.g. codeVerifier (PKCE) or organization +}); +const accessToken = tokens.accessToken; +const expiresAt = tokens.expiresAt; // absolute Unix seconds +``` + +> If your code manually parses `req.query.code`, that parsing is now the SDK's job. Delete it and hand the SDK the full URL. The SDK reads `code` from the URL and validates the PKCE verifier; it does **not** validate OAuth `state`. **Keep your existing `state` check** (compare the `state` query parameter against what you stored before the redirect) — or migrate to `@auth0/auth0-server-js` `completeInteractiveLogin`, which handles `state` validation automatically. (`getTokenByCode` options are `codeVerifier` and `organization`.) If the node-auth0 code read `resp.headers.get(...)` on success, see [Reading HTTP response metadata](#reading-http-response-metadata-fullresponse). Error-path metadata remains accessible on the typed error. + +> **Warning:** Do not delete your `state`/CSRF check when migrating to `AuthClient.getTokenByCode`. The stateless client does not validate `state`. Removing the check silently disables CSRF protection on the authorization-code flow. + +### `oauth.authorizationCodeGrantWithPKCE` → `getTokenByCode` (with verifier) + +PKCE (Proof Key for Code Exchange) is folded into the same method; supply the code verifier via options. Typically the verifier was produced earlier by `buildAuthorizationUrl` (below), which returns a `codeVerifier` for you to persist. + +```ts +// before +const resp = await auth0.oauth.authorizationCodeGrantWithPKCE({ + code, + code_verifier: verifier, + redirect_uri: "https://app.example.com/callback", +}); + +// after +const tokens = await authClient.getTokenByCode(url, { + codeVerifier: verifier, +}); +``` + +> If you build the authorization URL yourself today, prefer switching to `authClient.buildAuthorizationUrl()` (below) so the SDK generates and returns the `codeVerifier`, then persist it and pass it back to `getTokenByCode`. + +### `oauth.refreshTokenGrant` → `getTokenByRefreshToken` + +```ts +// before +const resp = await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); +// after +const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); +``` + +### `oauth.passwordGrant` → `getTokenByPassword` + +```ts +// before +const resp = await auth0.oauth.passwordGrant({ + username, + password, + realm: "Username-Password-Authentication", + audience, + scope, +}); +// after +const tokens = await authClient.getTokenByPassword({ + username, + password, + realm: "Username-Password-Authentication", + audience, + scope, +}); +``` + +### `oauth.clientCredentialsGrant` → `getTokenByClientCredentials` + +The canonical machine-to-machine grant. This is the most common reason to stay on auth0-auth-js rather than adopt server-js: there is no user session involved. + +```ts +// before +const resp = await auth0.oauth.clientCredentialsGrant({ audience: "https://api.example.com" }); +const token = resp.data.access_token; +// after +const tokens = await authClient.getTokenByClientCredentials({ audience: "https://api.example.com" }); +const token = tokens.accessToken; +``` + +### `oauth.revokeRefreshToken` → `revokeToken` + +Renamed, and simplified return (was `VoidApiResponse`, now `void`). + +```ts +// before +await auth0.oauth.revokeRefreshToken({ token: rt }); +// after +await authClient.revokeToken({ token: rt }); +``` + +> **Session apps:** if you are migrating to server-js and this revoke was part of logout, use `serverClient.revokeRefreshToken()` instead of the low-level `revokeToken`. By default it reads the refresh token from the session; you can also pass an explicit `{ token }` in its options. + +### Build the authorization and logout URLs + +node-auth0 left `/authorize` URL construction to the caller (or to `express-openid-connect`). The new SDK gives you `buildAuthorizationUrl()` and `buildLogoutUrl()`. When migrating a redirect login, replace hand-built `/authorize` and `/v2/logout` URLs with these: + +```ts +const { authorizationUrl, codeVerifier } = await authClient.buildAuthorizationUrl({ + authorizationParams: { redirect_uri, scope: "openid profile email", audience }, +}); +// ... later, on logout: +const logoutUrl = await authClient.buildLogoutUrl({ returnTo: "https://app.example.com" }); +``` + +> Pushed Authorization Requests (PAR): there is no standalone PAR method. Pass `pushedAuthorizationRequests: true` to `buildAuthorizationUrl`: the SDK performs the PAR POST and returns an authorization URL that references the resulting `request_uri`. Requires the tenant to expose a `pushed_authorization_request_endpoint`; the SDK throws if PAR is requested but unsupported. This replaces node-auth0's `oauth.pushedAuthorization`. + +Once the OIDC grants are rewritten and the [cross-cutting breaking changes](#cross-cutting-breaking-changes) are applied, run the [verification checklist](#verification-checklist). If your app also uses database, passwordless, CIBA, token exchange, or `UserInfoClient`, continue with [`authentication-flows.md`](./authentication-flows.md). If you want the SDK to own sessions, see [`server-side-sessions.md`](./server-side-sessions.md). + +### Optional: migrate only OIDC while staying on v6 + +You do not have to migrate everything at once, and you do not have to wait for v7. node-auth0 v6 still ships `AuthenticationClient` alongside `ManagementClient`, so you can move your OIDC login and token grant code off `AuthenticationClient` to `@auth0/auth0-auth-js` now, incrementally, while the rest of the app keeps using `auth0` v6 unchanged. + +A common and fully supported end state: + +- OIDC / token grant code: migrated to `@auth0/auth0-auth-js` (the grants covered in this section). +- Other auth flows you have not gotten to yet: still on `AuthenticationClient` from `auth0` v6. +- Management API: still on `ManagementClient` from `auth0` (never migrates). + +The OIDC grants above are a complete, shippable step on their own; finishing them is a valid stopping point even if you migrate nothing else. Move on to [`authentication-flows.md`](./authentication-flows.md) and [`server-side-sessions.md`](./server-side-sessions.md) later, at your own pace. When you eventually upgrade to v7 (which removes the Authentication API from the main entrypoint; see the [v7 Migration Guide](../v7_MIGRATION_GUIDE.md)), the OIDC work is already done. + +## Cross-cutting breaking changes + +Every call-site rewrite in this guide and on the incremental pages is subject to four changes that cut across all methods. They cause the overwhelming majority of migration defects, and three of the four are *silent*: the code compiles and often runs, but produces wrong behavior at runtime. Apply each one deliberately. + +1. [Return shape: `JSONApiResponse` → domain object](#1-return-shape) +2. [Casing: snake_case wire shape → camelCase](#2-casing) +3. [Token expiry: `expires_in` (relative) → `expiresAt` (absolute)](#3-token-expiry), most dangerous +4. [Error model: `AuthApiError` → typed per-operation errors](#4-error-model) + +### 1. Return shape + +node-auth0 wraps most Authentication API results in a response envelope: + +- `JSONApiResponse`: has `.data` (the payload), `.status` (number), `.statusText`, `.headers` (a `Headers` object). +- `VoidApiResponse`: same envelope, `.data` is `undefined` (used by `sendEmail`, `revokeRefreshToken`, …). +- `TextApiResponse`: `.data` is a `string` (used by `database.changePassword`). + +Exception: `backchannel.authorize`, `backchannel.backchannelGrant`, and `tokenExchange.exchangeToken` return domain objects directly (no `.data` wrapper) in node-auth0. + +The new SDKs drop the envelope and return the domain object directly: + +- Token grants return a `TokenResponse` instance. +- `database.signUp` returns a `SignUpResult` object. +- `database.changePassword` returns a `string`. +- `sendEmail` / `sendSms` / `revokeToken` return `void`. + +HTTP metadata (status code, response headers such as `x-request-id`, `retry-after`, rate-limit headers) is available through the typed error objects on failure paths. On success paths, metadata is available via the opt-in `fullResponse` envelope (see [Reading HTTP response metadata](#reading-http-response-metadata-fullresponse)). It is no longer on the bare success value by default. + +The rewrite: delete `.data` indirection on every success path: + +```ts +// before +const resp = await auth0.oauth.clientCredentialsGrant({ audience }); +const token = resp.data.access_token; +const status = resp.status; + +// after +const tokens = await authClient.getTokenByClientCredentials({ audience }); +const token = tokens.accessToken; +``` + +```ts +// before: changePassword returned TextApiResponse +const resp = await auth0.database.changePassword({ email, connection }); +console.log(resp.data); + +// after: returns the string directly +const message = await authClient.database.changePassword({ email, connection }); +console.log(message); +``` + +> `changePassword` requires `connection` plus at least one of `email` or `username`: either identifier is accepted, not `email` alone. + +#### Reading HTTP response metadata (fullResponse) + +When your node-auth0 code reads HTTP response metadata (status, headers) on a success path, migrate to the opt-in envelope rather than dropping the read. This is most common when you track rate limits, log request IDs, or check retry-after headers for dashboard telemetry. + +```ts +// before (node-auth0): metadata on the success envelope +const resp = await auth0.oauth.clientCredentialsGrant({ audience }); +const remaining = resp.headers.get("x-ratelimit-remaining"); +const token = resp.data.access_token; + +// after: opt in to the envelope, read the native Response +const { data, response } = await authClient.getTokenByClientCredentials({ audience, fullResponse: true }); +const remaining = response.headers.get("x-ratelimit-remaining"); +const token = data.accessToken; +``` + +The same opt-in covers the non-token Authentication API methods that node-auth0 wrapped in a `JSONApiResponse` / `TextApiResponse` / `VoidApiResponse`: + +| Method | Bare return | `fullResponse: true` return | +| --- | --- | --- | +| `database.signUp` | `SignUpResult` | `ApiResponse` | +| `database.changePassword` | `string` | `ApiResponse` | +| `passwordless.sendEmail` | `void` | `ApiResponse` (`data` is `undefined`) | +| `passwordless.sendSms` | `void` | `ApiResponse` (`data` is `undefined`) | + +```ts +// before (node-auth0): read the request id off the signup envelope +const resp = await auth0.database.signUp({ email, password, connection }); +const reqId = resp.headers.get("x-request-id"); + +// after: opt in to the envelope +const { data, response } = await authClient.database.signUp({ email, password, connection, fullResponse: true }); +const reqId = response.headers.get("x-request-id"); + +// void-returning methods expose the Response with an undefined `data` +const { response: sendResp } = await authClient.passwordless.sendEmail({ email, fullResponse: true }); +const rateLimit = sendResp.headers.get("x-ratelimit-remaining"); +``` + +Caveats: + +- Pass `fullResponse: true` as a literal, not a variable. Using spread (`{ ...opts, fullResponse: true }`) widens `true` to `boolean`, causing TypeScript overload resolution to fall back to the bare return type. Fix: pass `{ ...opts, fullResponse: true as const }` or include `fullResponse` as an inline literal in the options object. +- Performance: `@auth0/auth0-auth-js` does not cache tokens: every `AuthClient` grant method performs a live token-endpoint round-trip regardless of `fullResponse`, so the flag adds no extra network cost at this layer. (Token caching and reuse live in `@auth0/auth0-server-js`'s session store, not in the auth-js `AuthClient`.) The only in-memory cache in auth-js is for OIDC discovery / JWKS metadata, which is unrelated to `fullResponse`. +- Reserved headers: a caller `Authorization` header is ignored and the telemetry `Auth0-Client` header always wins; `RequestOptions.headers` cannot override them. +- Per-request `customFetch` replaces the base transport for that call but does not inherit mutual TLS (mTLS). If you rely on mTLS, the supplied fetch must itself be mTLS-capable. + +Default to the bare return type. Reach for `fullResponse` only where you actually consumed response metadata on success: rate-limit dashboards, request-id logging for support investigations, or retry-after handling. `MissingCapturedResponseError` is an internal-bug sentinel; you do not normally catch it. + +Gotchas: + +- **Void methods.** Code that did `const r = await auth0.passwordless.sendEmail(...)` and then checked `r.status === 200` must drop that check: by default the method returns `void` and throws on failure. Rely on the thrown error instead (see [Error model](#4-error-model)). +- **Header reads.** Any code reading `resp.headers.get('x-ratelimit-remaining')` on a success path needs the opt-in `fullResponse` envelope. Error paths still surface metadata on the typed error. Search your code for `.headers` on response values. +- **Do not hand-roll a compatibility shim.** Resist reintroducing a custom `{ data, status }` shape to minimize downstream diff. Let the domain object flow through; the SDK's opt-in `fullResponse` envelope is the sanctioned channel when you genuinely need the HTTP Response. + +### 2. Casing + +node-auth0's public API exposes the snake_case wire shape verbatim, on both inputs and outputs. The new SDKs use camelCase for the public API and only translate to snake_case at the HTTP boundary internally. + +Input parameters, field map: + +| node-auth0 (snake_case) | new SDK (camelCase) | +| --- | --- | +| `client_id` | `clientId` | +| `client_secret` | `clientSecret` | +| `refresh_token` | `refreshToken` | +| `redirect_uri` | (via `authorizationParams.redirect_uri` on config / builder) | +| `code_verifier` | `codeVerifier` | +| `phone_number` | `phoneNumber` | +| `auth_req_id` | `authReqId` | +| `binding_message` | `bindingMessage` | +| `subject_token` / `subject_token_type` | `subjectToken` / `subjectTokenType` | +| `given_name` / `family_name` | `givenName` / `familyName` | +| `user_metadata` | `userMetadata` | +| `login_hint` | `loginHint` | + +Output fields, `TokenResponse` field map: + +| node-auth0 `TokenSet` (snake_case) | new SDK `TokenResponse` (camelCase) | +| --- | --- | +| `access_token` | `accessToken` | +| `refresh_token` | `refreshToken` | +| `id_token` | `idToken` | +| `token_type` | `tokenType` | +| `expires_in` (relative) | `expiresAt` (absolute, see [Token expiry](#3-token-expiry)) | +| `scope` | `scope` | +| (none): had to decode id_token yourself | `claims` (already-decoded ID token claims) | +| `authorization_details` | `authorizationDetails` | + +Rename fields on both the arguments you pass in and the fields you read out: + +```ts +// before +const resp = await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); +const newRt = resp.data.refresh_token; +const idToken = resp.data.id_token; + +// after +const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); +const newRt = tokens.refreshToken; +const idToken = tokens.idToken; +``` + +> **Gotcha: keys that look renamed but are your data.** `user_metadata` → `userMetadata` is a rename of the *SDK's* parameter. The object *inside* it (e.g. `{ plan: 'free' }`) is passed through untouched. Do not rename your own metadata keys. The same applies to `authorization_details`. + +### 3. Token expiry + +**This is the highest-risk change in the migration. It is silent, it compiles, and it corrupts session lifetimes.** + +- node-auth0 `TokenSet.expires_in` = the token's lifetime in seconds relative to now (e.g. `86400` for a 24-hour token). This is the raw OAuth `expires_in` from the wire. +- new SDK `TokenResponse.expiresAt` = an absolute Unix timestamp in seconds (e.g. `1786000000`) computed by the SDK as roughly `now + expires_in`. + +Existing node-auth0 code almost always converts the relative value to an absolute deadline itself: + +```ts +// before: very common node-auth0 pattern +const resp = await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); +const expiresAtMs = Date.now() + resp.data.expires_in * 1000; // stored deadline +``` + +If you mechanically rename `expires_in` → `expiresAt` and leave the arithmetic, you get: + +```ts +// WRONG: double-counts "now" +const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); +const expiresAtMs = Date.now() + tokens.expiresAt * 1000; // ~ now + (now + lifetime) → far future +``` + +The stored deadline lands decades in the future, so the token is treated as valid long after it has actually expired. The app does not refresh it, so production 401s follow. + +The rewrite: `expiresAt` is *already* the deadline. Do not add `Date.now()`: + +```ts +// after: correct +const tokens = await authClient.getTokenByRefreshToken({ refreshToken: rt }); +const expiresAtMs = tokens.expiresAt * 1000; // absolute; convert s → ms only if you store ms +``` + +If downstream code genuinely needs the *relative* remaining lifetime (e.g. to set a cookie `Max-Age`), compute it from the absolute value: + +```ts +const secondsRemaining = tokens.expiresAt - Math.floor(Date.now() / 1000); +``` + +To find every instance, grep your code for these patterns and inspect each by hand: + +- `expires_in` +- `Date.now() +` near a token result +- `+ expires` / `* 1000` near a token result +- any stored field named `expiresAt`, `expires_at`, `expiry`, `tokenExpiry` fed from a grant + +Every one of these is a candidate for the double-count bug. + +> **Session apps get this for free.** If you migrate to server-js, the SDK owns expiry math inside `getAccessToken`. Delete your `Date.now() + expires_in * 1000` bookkeeping entirely. + +### 4. Error model + +node-auth0 throws a single error type for Authentication API failures: + +```ts +class AuthApiError extends Error { + name: "AuthApiError"; + error: string; // OAuth error code, e.g. 'invalid_grant' + error_description: string; + statusCode: number; + body: string; + headers: Headers; +} +``` + +The new SDKs throw typed, operation-specific error classes: `TokenByCodeError`, `TokenByRefreshTokenError`, `TokenByClientCredentialsError`, `TokenByPasswordError`, `TokenExchangeError`, `TokenRevocationError`, `PasswordlessStartError`, `PasswordlessChallengeError`, `PasswordlessDbGetTokenError`, `MfaEnrollmentError`, and so on. Each carries a structured `.cause` (the underlying OAuth2 error) rather than flat `error` / `error_description` strings. + +The rewrite: generic catch: + +```ts +// before +try { + await auth0.oauth.refreshTokenGrant({ refresh_token: rt }); +} catch (e) { + if (e instanceof AuthApiError && e.error === "invalid_grant") { + // refresh token revoked/expired + } +} + +// after +import { TokenByRefreshTokenError } from "@auth0/auth0-auth-js"; +try { + await authClient.getTokenByRefreshToken({ refreshToken: rt }); +} catch (e) { + if (e instanceof TokenByRefreshTokenError && e.cause?.error === "invalid_grant") { + // refresh token revoked/expired + } +} +``` + +Import the specific error class for the operation you are calling. If you had one broad `catch (e instanceof AuthApiError)` around several different operations, either widen to catch each operation's error type or check the shared base behavior. Prefer the specific type per call site, since it documents which operation can fail. + +#### MFA detection: use the type guard, not the string + +Multi-factor authentication (MFA). A very common node-auth0 pattern is detecting `mfa_required` by string comparison to route the user into an MFA challenge: + +```ts +// before +try { + await auth0.oauth.passwordGrant({ username, password }); +} catch (e) { + if (e instanceof AuthApiError && e.error === "mfa_required") { + // start MFA flow using e (mfa_token is in the body) + } +} +``` + +The new SDK provides `isMfaRequiredError()`, a type guard that narrows the error and gives typed access to the MFA context (including the `mfa_token`). Use it instead of matching the string: + +```ts +// after +import { isMfaRequiredError } from "@auth0/auth0-auth-js"; +try { + await authClient.getTokenByPassword({ username, password }); +} catch (e) { + if (isMfaRequiredError(e)) { + // e is narrowed; drive the MFA challenge via authClient.mfa.* + } +} +``` + +> After detecting `mfa_required`, the MFA enroll/challenge/verify flow that node-auth0 handled ad hoc now lives on `authClient.mfa.*` (`listAuthenticators`, `enrollAuthenticator`, `challengeAuthenticator`, `verify`, and `deleteAuthenticator`). In server-js, `serverClient.mfa.verify()` also persists the resulting tokens to the session. + +#### ID-token validation types + +node-auth0 exposed `IDTokenValidateOptions` and `IdTokenValidatorError` for callers doing manual ID-token validation. The new SDK validates ID tokens internally during grants and exposes the decoded, validated result as `TokenResponse.claims`. Replace manual validation: + +- Options like `organization`, `nonce`, `maxAge` are passed to the grant call (e.g. `getTokenByCode`), and the SDK validates them and throws a typed error on mismatch, so you no longer construct a validator or catch `IdTokenValidatorError` yourself. +- Read the validated claims from `TokenResponse.claims` instead of decoding the `id_token` string. + +## Verification checklist + +The migration is not complete until every check passes in a single pass. For every node-auth0 auth call you rewrote (here or on the incremental pages), confirm all four cross-cutting changes: + +- [ ] **Return shape**: removed `.data` / `.status` / `.headers` access on the success path. +- [ ] **Casing**: renamed every snake_case field on input args and output reads to camelCase. +- [ ] **Expiry**: any code using the old `expires_in` now uses `expiresAt` as an *absolute* timestamp; no `Date.now() +` was left in front of it. +- [ ] **Errors**: `AuthApiError` catches replaced with the specific typed error (`.cause.error`); `mfa_required` string checks replaced with `isMfaRequiredError()`. + +Then run the project gates and repeat the whole loop if any step fails: + +- [ ] Grep for residue: unmigrated `from 'auth0'` auth imports, `.data.` reads on auth responses, and relative `expires_in` arithmetic. +- [ ] `tsc --noEmit`: catches structural mismatches and type errors. +- [ ] `npm test` (or the project's test command): confirms behavior is preserved. +- [ ] Run the linter if the project has one configured. +- [ ] Confirm files that use `ManagementClient` still import and call it from `auth0`; that code must be untouched. + +Do not declare the migration complete until the loop converges: all steps pass in a single iteration. + +## Continue the migration + +Once the OIDC grants and the four cross-cutting changes are in, migrate the rest at your own pace. Each area lives in its own page. + +### Other authentication flows + +Database signup, passwordless, backchannel (CIBA), token exchange, and `UserInfoClient` lookups: see [`authentication-flows.md`](./authentication-flows.md). + +### Server-side sessions + +Routing to `@auth0/auth0-server-js`, where the SDK owns the login redirect flow, session storage, cookies, token refresh, and logout: see [`server-side-sessions.md`](./server-side-sessions.md). + +### Troubleshooting + +Common questions and failure modes (tokens valid for decades, missing `resp.data`, magic-link default flip, `getUserInfo`, `mfa_required` detection, global config): see [`troubleshooting.md`](./troubleshooting.md). diff --git a/auth-migration/server-side-sessions.md b/auth-migration/server-side-sessions.md new file mode 100644 index 0000000000..d040e2986b --- /dev/null +++ b/auth-migration/server-side-sessions.md @@ -0,0 +1,163 @@ +# Migrating session apps to `@auth0/auth0-server-js` + +This page is part of the [Authentication Migration Guide](./index.md). Read it only when you are routing to **`@auth0/auth0-server-js`**: when you want the SDK to own the login redirect flow, session storage, cookies, token refresh, and logout, instead of hand-rolling that around node-auth0. If you only need stateless token grants, stay on the main guide and [`authentication-flows.md`](./authentication-flows.md); you do not need this page. + +> **Migrating with an AI agent?** Point it at the Auth0 migration skill (the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`). It walks the session lifecycle step by step. + +**This is a rewrite of the session handling, not a method-for-method port.** node-auth0 had no session concept, so there is nothing to translate line-for-line. Instead you *replace* your existing session code (your `express-session` wiring, your token cache, your refresh-on-expiry logic, your logout handler) with the ServerClient lifecycle. You still touch only the auth/session code; routes, views, and business logic stay put. + +- [Mental model](#mental-model) +- [Store setup](#store-setup) +- [The redirect-login lifecycle](#the-redirect-login-lifecycle) +- [Logins without a browser redirect](#logins-without-a-browser-redirect) +- [Backchannel logout](#backchannel-logout) + +## Mental model + +A ServerClient login has three durable pieces: + +1. **Transaction store**: short-lived. Holds the in-flight login: the OAuth `state` and the PKCE (Proof Key for Code Exchange) `code_verifier` between the moment you redirect the user to Auth0 and the moment they come back to your callback. Created at `startInteractiveLogin`, consumed at `completeInteractiveLogin`. +2. **State store**: long-lived. Holds the established session: the user claims plus the access / refresh / ID tokens and their absolute expiry. Read on every subsequent request via `getUser`, `getSession`, `getAccessToken`. +3. **Cookies**: how the two stores key themselves to the browser. With a *stateless* store the session data lives encrypted in the cookie itself; with a *stateful* store the cookie holds only an identifier and the data lives in your backend (Redis, database, and so on). + +node-auth0 exposed none of this; you built equivalents by hand. You are swapping your implementation for the SDK's. + +## Store setup + +`@auth0/auth0-server-js` ships store base classes and cookie-backed implementations: + +- `CookieTransactionStore`: transaction store backed entirely by a cookie. Good default. +- `StatelessStateStore`: session lives encrypted in the cookie. No server-side storage; good for serverless or horizontally-scaled deployments with small sessions. +- `StatefulStateStore`: session lives server-side; the cookie holds an id. Use for large sessions or when you need server-side revocation. +- `AbstractTransactionStore` / `AbstractStateStore`: extend these to back a store with your own storage (Redis, Postgres, and so on). These are the exported base-class names. + +All stores accept a `CookieHandler` so they can integrate with any framework's cookie API. The `storeOptions` generic (`TStoreOptions`) is how you thread per-request context (like the framework `req` / `res`) into store reads and writes; every ServerClient method takes an optional trailing `storeOptions` argument for exactly this. + +```ts +import { ServerClient, CookieTransactionStore, StatelessStateStore } from "@auth0/auth0-server-js"; + +const serverClient = new ServerClient({ + domain: process.env.AUTH0_DOMAIN!, + clientId: process.env.AUTH0_CLIENT_ID!, + clientSecret: process.env.AUTH0_CLIENT_SECRET!, + authorizationParams: { + redirect_uri: "https://app.example.com/callback", + scope: "openid profile email offline_access", // offline_access ⇒ refresh token + audience: "https://api.example.com", + }, + transactionStore: new CookieTransactionStore( + { secret: process.env.SESSION_SECRET! }, + cookieHandler, // CookieHandler implementation + ), + stateStore: new StatelessStateStore( + { secret: process.env.SESSION_SECRET! }, + cookieHandler, // CookieHandler implementation + ), +}); +``` + +## The redirect-login lifecycle + +### 1. Start login: replace the hand-built `/authorize` redirect + +Whatever you did to send the user to Auth0 (a hand-constructed `/authorize` URL, or `express-openid-connect`'s `/login`) becomes: + +```ts +// GET /login +app.get("/login", async (req, res) => { + const authorizationUrl = await serverClient.startInteractiveLogin( + { + authorizationParams: { + /* optional per-login overrides */ + }, + appState: { returnTo: req.query.returnTo || "/" }, // seed appState for round-trip + }, + { req, res }, // storeOptions: lets the transaction store write its cookie + ); + res.redirect(authorizationUrl.href); +}); +``` + +`startInteractiveLogin` generates `state` and PKCE, writes them to the transaction store, and returns the fully-formed authorization URL. + +### 2. Complete login: replace the manual code exchange + +The callback handler that used to call `oauth.authorizationCodeGrant` (or `authorizationCodeGrantWithPKCE`) and then stuff tokens into the session becomes a single call: + +```ts +// GET /callback +app.get("/callback", async (req, res) => { + const callbackUrl = new URL(req.url, `https://${req.headers.host}`); + const { appState } = await serverClient.completeInteractiveLogin(callbackUrl, { req, res }); + // Session is now established in the state store. Tokens are NOT your concern anymore. + res.redirect(appState?.returnTo ?? "/"); +}); +``` + +`completeInteractiveLogin` validates `state`, exchanges the code, validates the ID token, writes the session (user + tokens + absolute expiry) to the state store, and clears the transaction. + +### 3. Read the user or session on later requests + +Replace `req.session.user` reads: + +```ts +const user = await serverClient.getUser({ req, res }); // user claims, or undefined +const session = await serverClient.getSession({ req, res }); // full session data, or undefined +``` + +`getUser` / `getSession` return `undefined` when there is no session or it has expired (the store deletes expired sessions on read), so use that as your "not logged in" signal. + +### 4. Get an access token to call an API: refresh is automatic + +Replace your manual "is the token expired? if so refresh" block: + +```ts +const { accessToken } = await serverClient.getAccessToken({ req, res }); +// If the stored access token is expired and a refresh token exists, +// the SDK refreshes and persists the new tokens transparently. +``` + +This is where the `expires_in` → `expiresAt` hazard disappears entirely: the SDK owns expiry math. For a downstream federated connection token (Token Vault), use `serverClient.getAccessTokenForConnection({ connection }, { req, res })`. + +### 5. Logout: replace manual revoke, session clear, and `/v2/logout` redirect + +```ts +// GET /logout +app.get("/logout", async (req, res) => { + const logoutUrl = await serverClient.logout({ returnTo: "https://app.example.com" }, { req, res }); + res.redirect(logoutUrl.href); +}); +``` + +`logout` clears the session from the state store and returns the Auth0 `/v2/logout` URL. If you also revoked the refresh token on logout (via `oauth.revokeRefreshToken`), call `serverClient.revokeRefreshToken({ req, res })` before redirecting; by default it reads the refresh token from the session, so you do not handle the raw token yourself (it also accepts an explicit `{ token }` if you need to revoke a specific one). + +## Logins without a browser redirect + +Some logins do not use a browser redirect: the password grant, passwordless, CIBA, and custom token exchange. If you used node-auth0 for one of these *and* want a server-js session out of it, use the ServerClient methods that both authenticate and write the session, rather than the low-level auth-js grants: + +| Flow | ServerClient method | +| --- | --- | +| Backchannel / CIBA | `loginBackchannel({ ... }, storeOptions)` | +| Passwordless (send) | `startPasswordless({ connection, email \| phoneNumber, ... }, storeOptions)` | +| Passwordless (verify code → session) | `completePasswordless({ connection, email \| phoneNumber, verificationCode }, storeOptions)` | +| Passwordless magic link (callback → session) | `completePasswordlessMagicLink(url, storeOptions)` | +| Custom token exchange → session | `loginWithCustomTokenExchange({ ... }, storeOptions)` | +| MFA verify → session | `serverClient.mfa.verify({ ... }, storeOptions)` | + +Each of these performs the underlying grant *and* persists the resulting tokens to the state store, so the user is logged in afterward, exactly the behavior you previously wrote by hand after a node-auth0 grant. + +## Backchannel logout + +If you implemented an Auth0 back-channel logout endpoint by hand (validating the logout token, then clearing your session store), replace it with: + +```ts +// POST /backchannel-logout +app.post("/backchannel-logout", async (req, res) => { + await serverClient.handleBackchannelLogout(req.body.logout_token, { req, res }); + res.sendStatus(204); +}); +``` + +It validates the logout token and clears the corresponding session. + +When the session layer is wired, return to the [verification checklist](./index.md#verification-checklist) in the main guide. diff --git a/auth-migration/troubleshooting.md b/auth-migration/troubleshooting.md new file mode 100644 index 0000000000..334a37a698 --- /dev/null +++ b/auth-migration/troubleshooting.md @@ -0,0 +1,32 @@ +# Troubleshooting: FAQ and gotchas + +Common questions and failure modes when migrating off the `auth0` package's Authentication API. This page is part of the [Authentication Migration Guide](./index.md); it assumes the terms defined there. + +> **Migrating with an AI agent?** Point it at the Auth0 migration skill (the `auth0` skill in [`auth0/agent-skills`](https://github.com/auth0/agent-skills), migration intent `migrate-node-auth0`). + +### Do I have to migrate everything at once? +No. The OIDC / token-grant work is a complete, shippable step on its own. You can stay on `auth0` v6 and migrate only OIDC, leaving other auth flows on `AuthenticationClient` for now. See [Optional: migrate only OIDC while staying on v6](./index.md#optional-migrate-only-oidc-while-staying-on-v6). + +### Do I have to migrate the Management API too? +No. `ManagementClient` is out of scope and stays on the `auth0` package. A file importing both `auth0` (for management) and `@auth0/auth0-auth-js` (for authentication) is correct. + +### auth0-auth-js or auth0-server-js: which do I pick? +Default to auth0-auth-js for a low-risk parity migration. Pick auth0-server-js only when you want the SDK to own the login redirect flow, session storage, cookies, refresh, and logout. See [Choosing your target SDK](./index.md#choosing-your-target-sdk). + +### My tokens suddenly look valid for decades. What happened? +You almost certainly left `Date.now() +` in front of `expiresAt`. `expiresAt` is already an absolute Unix timestamp, not a relative lifetime. See [Token expiry](./index.md#3-token-expiry). + +### Where did `resp.data` go? +The new SDKs return the domain object directly. Read `tokens.accessToken`, not `resp.data.access_token`. If you truly need HTTP response metadata on a success path, opt into `fullResponse`. + +### My magic-link passwordless flow stopped sending links. +The `send` default changed from `'link'` (node-auth0) to `'code'` (new SDK). Set `send: 'link'` explicitly if you want magic links. See [Passwordless](./authentication-flows.md#passwordless). + +### Where is `getUserInfo`? +Prefer `TokenResponse.claims`; they are already decoded and validated, with no extra round-trip. For an arbitrary access token, use `authClient.getUserInfo({ accessToken })`. In a session app, use `serverClient.getUser()`. See [UserInfoClient](./authentication-flows.md#userinfoclient). + +### Can I still set a global `headers` / `timeout` / `agent` on the client? +Not on the constructor. Move them to the per-call `RequestOptions` argument (`headers`, `signal: AbortSignal.timeout(ms)`) or wrap `customFetch`. + +### How do I detect `mfa_required` now? +Use the `isMfaRequiredError()` type guard, not a string comparison. It narrows the error and exposes the `mfa_token`. Drive the challenge via `authClient.mfa.*`. See [Error model](./index.md#4-error-model). diff --git a/v6_MIGRATION_GUIDE.md b/v6_MIGRATION_GUIDE.md new file mode 100644 index 0000000000..d1eb6bd699 --- /dev/null +++ b/v6_MIGRATION_GUIDE.md @@ -0,0 +1,158 @@ +# V6 Migration Guide + +A guide to migrating the Auth0 Node.js SDK from `5.x` to `6.x`. + +- [Overall changes](#overall-changes) +- [Breaking changes](#breaking-changes) + - [ConnectionAttributeIdentifier replaced with identifier-specific types](#connectionattributeidentifier-replaced-with-identifier-specific-types) + - [PhoneProviderProtectionBackoffStrategyEnum value change](#phoneproviderprotectionbackoffstrategyenum-value-change) + - [users.federatedConnectionsTokensets removed](#usersfederatedconnectionstokensets-removed) + - [federated_connections_access_tokens removed from connection options](#federated_connections_access_tokens-removed-from-connection-options) + +## Overall changes + +V6 addresses type correctness for database connection attribute identifiers, aligns the phone provider backoff strategy enum with the updated API, and removes the federated connections tokensets user sub-client. There are no changes to the Authentication API — any code written for the Authentication API in `5.x` will continue to work in `6.x`. + +## Breaking changes + +### ConnectionAttributeIdentifier replaced with identifier-specific types + +In v5, all three attribute identifiers (email, phone number, and username) shared a single `ConnectionAttributeIdentifier` type for their `identifier` field. This was incorrect — each identifier type supports different values for `default_method`. + +In v6, `ConnectionAttributeIdentifier` has been removed and replaced with three separate types: + +| Attribute | Old type | New type | `default_method` values | +| -------------- | ------------------------------- | ----------------------------- | ----------------------------- | +| `email` | `ConnectionAttributeIdentifier` | `EmailAttributeIdentifier` | `"password"` \| `"email_otp"` | +| `phone_number` | `ConnectionAttributeIdentifier` | `PhoneAttributeIdentifier` | `"password"` \| `"phone_otp"` | +| `username` | `ConnectionAttributeIdentifier` | `UsernameAttributeIdentifier` | _(no `default_method`)_ | + +**Before (v5):** + +```ts +import { Management } from "auth0"; + +const identifier: Management.ConnectionAttributeIdentifier = { + active: true, + default_method: "email_otp", +}; +``` + +**After (v6):** + +```ts +import { Management } from "auth0"; + +// For email attribute +const emailIdentifier: Management.EmailAttributeIdentifier = { + active: true, + default_method: "email_otp", +}; + +// For phone_number attribute +const phoneIdentifier: Management.PhoneAttributeIdentifier = { + active: true, + default_method: "phone_otp", +}; + +// For username attribute (no default_method) +const usernameIdentifier: Management.UsernameAttributeIdentifier = { + active: true, +}; +``` + +If you were using `ConnectionAttributeIdentifier` as a type annotation in your own code, update it to the appropriate identifier-specific type based on which attribute it applies to. + +--- + +### PhoneProviderProtectionBackoffStrategyEnum value change + +The `PhoneProviderProtectionBackoffStrategyEnum` enum has been updated to reflect a change in the Auth0 API. The `None` variant has been renamed to `Default`, and its string value has changed from `"none"` to `"default"`. + +**Before (v5):** + +```ts +import { Management } from "auth0"; + +const strategy = Management.PhoneProviderProtectionBackoffStrategyEnum.None; // "none" +``` + +**After (v6):** + +```ts +import { Management } from "auth0"; + +const strategy = Management.PhoneProviderProtectionBackoffStrategyEnum.Default; // "default" +``` + +If you were passing this value directly as a string `"none"`, update it to `"default"` to match the updated API. + +--- + +### users.federatedConnectionsTokensets removed + +The `client.users.federatedConnectionsTokensets` sub-client has been removed. This includes the `list()` and `delete()` methods. + +**Before (v5):** + +```ts +// List active federated connection tokensets for a user +const tokensets = await client.users.federatedConnectionsTokensets.list("user_id"); + +// Delete a tokenset +await client.users.federatedConnectionsTokensets.delete("user_id", "tokenset_id"); +``` + +**After (v6):** + +These methods are no longer available. Remove any calls to `client.users.federatedConnectionsTokensets` from your code. + +--- + +### federated_connections_access_tokens removed from connection options + +The `federated_connections_access_tokens` field has been removed from all connection option types, including create and update. This affects OIDC, Azure AD, Google Apps, and other connection strategies. Remove it from any create or update payloads. + +**Before (v5):** + +```ts +// On create +await client.connections.create({ + strategy: "oidc", + name: "my-connection", + options: { + federated_connections_access_tokens: { ... }, + // other options + }, +}); + +// On update +await client.connections.update("connection_id", { + options: { + federated_connections_access_tokens: { ... }, + // other options + }, +}); +``` + +**After (v6):** + +```ts +// On create +await client.connections.create({ + strategy: "oidc", + name: "my-connection", + options: { + // remove federated_connections_access_tokens + // other options + }, +}); + +// On update +await client.connections.update("connection_id", { + options: { + // remove federated_connections_access_tokens + // other options + }, +}); +``` diff --git a/v7_MIGRATION_GUIDE.md b/v7_MIGRATION_GUIDE.md new file mode 100644 index 0000000000..95d366b1e8 --- /dev/null +++ b/v7_MIGRATION_GUIDE.md @@ -0,0 +1,146 @@ +# V7 Migration Guide + +A guide to migrating the Auth0 Node.js SDK from `6.x` to `7.x`. + +> **Migrating with an AI agent?** Point it at the Auth0 migration skill first. The skill lives in [`auth0/agent-skills`](https://github.com/auth0/agent-skills) as the `auth0` skill (migration intent: `migrate-node-auth0`). It encodes the authentication-layer rewrite rules and a verify loop. + +- [Overall changes](#overall-changes) +- [Breaking changes](#breaking-changes) + - [Authentication API removed from the main entrypoint](#authentication-api-removed-from-the-main-entrypoint) + - [Removed exports](#removed-exports) + - [ManagementClient mTLS requires an explicit `fetch`](#managementclient-mtls-requires-an-explicit-fetch) + - [mTLS works with both client secret and client assertion](#mtls-works-with-both-client-secret-and-client-assertion) + - [`domain` must be a bare hostname](#domain-must-be-a-bare-hostname) + - [Token acquisition failures throw `ManagementError`](#token-acquisition-failures-throw-managementerror) + - [`uuid` dependency removed](#uuid-dependency-removed) +- [Migrating authentication code](#migrating-authentication-code) +- [Staying on the legacy entrypoint](#staying-on-the-legacy-entrypoint) + +## Overall changes + +V7 makes `node-auth0` a **Management-API-only SDK**. The Authentication API layer (`AuthenticationClient`, its sub-clients, and `UserInfoClient`) has been removed from the main entrypoint. `ManagementClient` continues to work exactly as before; it now acquires its internal token directly via the client credentials grant rather than through the removed authentication layer. + +If your code only uses `ManagementClient`, the upgrade is small: address the Management-side breaking changes below (mTLS, domain validation, error type) and you are done. If your code uses `AuthenticationClient` or `UserInfoClient`, that code must move to a dedicated package; see [Migrating authentication code](#migrating-authentication-code). + +## Breaking changes + +### Authentication API removed from the main entrypoint + +`AuthenticationClient` and `UserInfoClient` are no longer exported from the `auth0` main entrypoint. The stateless authentication layer now lives in [`@auth0/auth0-auth-js`](https://github.com/auth0/auth0-auth-js), and the server-managed session layer lives in [`@auth0/auth0-server-js`](https://github.com/auth0/auth0-auth-js/tree/main/packages/auth0-server-js). + +**Before (v6):** + +```ts +import { AuthenticationClient, UserInfoClient } from "auth0"; + +const auth = new AuthenticationClient({ domain, clientId, clientSecret }); +const tokens = await auth.oauth.clientCredentialsGrant({ audience }); +``` + +**After (v7):** + +```ts +import { AuthClient } from "@auth0/auth0-auth-js"; + +const auth = new AuthClient({ domain, clientId, clientSecret }); +const tokens = await auth.getTokenByClientCredentials({ audience }); +``` + +The complete method-by-method mapping, the four cross-cutting behavior changes (return shape, casing, token expiry, error model), and the session-app wiring are documented in the dedicated [Authentication Migration Guide](https://github.com/auth0/node-auth0/tree/master/auth-migration). This guide does not repeat that detail. + +If you need the old clients unchanged as a stopgap, they still ship from the [legacy entrypoint](#staying-on-the-legacy-entrypoint). + +### Removed exports + +The following symbols were exported from the main entrypoint in v6 and are removed in v7. Each moves to `@auth0/auth0-auth-js`, or remains available from the `auth0/legacy` entrypoint at its v4.x shape. + +| Removed export (v6) | Replacement in v7 | +| ---------------------------- | ------------------------------------------------------------------------------------- | +| `AuthenticationClient` | `AuthClient` from `@auth0/auth0-auth-js` | +| `UserInfoClient` | `AuthClient.getUserInfo()` from `@auth0/auth0-auth-js`, or read `TokenResponse.claims` | +| `AuthApiError` | Per-operation typed errors from `@auth0/auth0-auth-js` (`TokenByCodeError`, `TokenByRefreshTokenError`, …); use their `.cause` | +| `AuthenticationClientOptions`| `AuthClientOptions` from `@auth0/auth0-auth-js` | +| `IDTokenValidateOptions` | Validation is internal to the grant call; pass `organization` / `nonce` / `maxAge` to the grant and read `TokenResponse.claims` | +| `IdTokenValidatorError` | Thrown internally by the grant as a typed error on claim mismatch | +| `TokenSet` | `TokenResponse` from `@auth0/auth0-auth-js` (camelCase fields; `expiresAt` is absolute) | +| `SUBJECT_TOKEN_TYPES` | Pass the token-type URN string directly to `exchangeToken` in `@auth0/auth0-auth-js` | +| `UserInfoResponse` | Return type of `AuthClient.getUserInfo()` in `@auth0/auth0-auth-js` | +| `UserInfoError` | Typed error from `AuthClient.getUserInfo()` in `@auth0/auth0-auth-js` | +| `ResponseError` | Management API calls throw `ManagementError` | +| `FetchError` | Management API calls throw `ManagementError` | +| `JSONApiResponse` | Responses return the data directly (no wrapper) | + +`ManagementClient`, the `Management` namespace, and `ManagementError` are unchanged and still exported. + +### ManagementClient mTLS requires an explicit `fetch` + +A `ManagementClient` constructed with `useMTLS: true` must now supply an explicit `fetch` option carrying the client certificate. The client throws at construction if `useMTLS` is set without a `fetch`. Previously a missing fetch surfaced as silent `401`s at request time; failing at construction makes the misconfiguration obvious. + +The token endpoint automatically uses the `mtls.{domain}` host when `useMTLS` is enabled. + +```ts +// v7: throws at construction if `fetch` is omitted +const mgmt = new ManagementClient({ + domain, + clientId, + clientSecret, + useMTLS: true, + fetch: mtlsCapableFetch, // now required +}); +``` + +### mTLS works with both client secret and client assertion + +`useMTLS` works with both `clientSecret` and `clientAssertionSigningKey`. mTLS (RFC 8705) is a transport-layer concern: the TLS client certificate yields a certificate-bound token regardless of which client authentication method is used. An explicit `fetch` option is always required when `useMTLS` is set. + +### `domain` must be a bare hostname + +`domain` must be a bare host such as `tenant.us.auth0.com`. A value containing a scheme, slashes, or a query string now throws at construction instead of producing malformed request URLs later. + +```ts +// throws in v7 +new ManagementClient({ domain: "https://tenant.us.auth0.com/", ... }); +// correct +new ManagementClient({ domain: "tenant.us.auth0.com", ... }); +``` + +### Token acquisition failures throw `ManagementError` + +When the internal client-credentials token request fails, the client now throws a `ManagementError` (previously a plain `Error`). The error carries `statusCode` and a parsed `body` with the OAuth error details. A request that exceeds the 10-second timeout throws `ManagementError` with status `408`. + +```ts +import { ManagementError } from "auth0"; + +try { + await mgmt.users.getAll(); +} catch (e) { + if (e instanceof ManagementError) { + console.error(e.statusCode, e.body); + } +} +``` + +### `uuid` dependency removed + +The `uuid` package is no longer a dependency. If your project imported `uuid` transitively through `auth0`, add it to your own `dependencies`. + +## Migrating authentication code + +If your app calls `AuthenticationClient` or `UserInfoClient`, follow the dedicated [Authentication Migration Guide](https://github.com/auth0/node-auth0/tree/master/auth-migration). Start with [`auth-migration/index.md`](https://github.com/auth0/node-auth0/blob/master/auth-migration/index.md) for the OIDC token grants section; the incremental flow, session, and troubleshooting pages live in the same [`auth-migration/`](https://github.com/auth0/node-auth0/tree/master/auth-migration) directory. It covers: + +- Choosing between `@auth0/auth0-auth-js` (stateless token grants) and `@auth0/auth0-server-js` (server-managed sessions). +- The complete method-by-method API mapping for `.oauth`, `.database`, `.passwordless`, `.backchannel`, `.tokenExchange`, and `UserInfoClient`. +- The four cross-cutting behavior changes: return shape (envelope dropped), casing (snake_case → camelCase), token expiry (`expires_in` relative → `expiresAt` absolute, a silent high-risk change), and the typed error model with `isMfaRequiredError()`. +- Wiring the `auth0-server-js` session lifecycle when you want the SDK to own login, cookies, refresh, and logout. + +The Management API is explicitly out of scope in that guide: a file that keeps using `ManagementClient` from `auth0` while importing `@auth0/auth0-auth-js` for authentication is correct and expected. + +## Staying on the legacy entrypoint + +If you cannot migrate the authentication code immediately, the `auth0/legacy` entrypoint still ships `AuthenticationClient` and `UserInfoClient` at their v4.x configuration format and method signatures. This is a stopgap, not a destination; the legacy shapes differ from the current API and will not receive new features. + +```ts +import { AuthenticationClient } from "auth0/legacy"; +``` + +Plan the move to `@auth0/auth0-auth-js` / `@auth0/auth0-server-js` rather than treating the legacy entrypoint as permanent.