From d4888054f24299a292dae50c5a22f852313f8fd7 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 06:03:13 +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 - package.json | 44 +- reference.md | 1855 +++++++++++++++-- src/management/api/requests/requests.ts | 225 +- .../experimentation/client/Client.ts | 12 + .../resources/experiments/client/Client.ts | 651 ++++++ .../resources/featureFlags/client/Client.ts | 572 +++++ .../resources/featureFlags}/client/index.ts | 0 .../resources/featureFlags/exports.ts | 5 + .../resources/featureFlags/index.ts | 2 + .../resources/featureFlags/resources/index.ts | 1 + .../resources/variations/client/Client.ts | 473 +++++ .../resources/variations/client/index.ts | 1 + .../resources/variations}/exports.ts | 2 +- .../resources/variations}/index.ts | 0 .../experimentation/resources/index.ts | 2 + .../resources/segments/client/Client.ts | 476 +++++ .../resources/segments/client/index.ts | 1 + .../resources/segments/exports.ts | 4 + .../resources/segments/index.ts | 1 + .../resources/organizations/client/Client.ts | 6 - .../organizations/resources/index.ts | 1 - .../organizationTemplate/client/Client.ts | 266 --- .../api/resources/users/client/Client.ts | 18 +- src/management/api/types/types.ts | 673 +++++- .../wire/experimentation/experiments.test.ts | 1074 ++++++++++ .../wire/experimentation/featureFlags.test.ts | 763 +++++++ .../featureFlags/variations.test.ts | 685 ++++++ .../wire/experimentation/segments.test.ts | 627 ++++++ .../organizationTemplate.test.ts | 306 --- .../tests/wire/resourceServers.test.ts | 5 + v6_MIGRATION_GUIDE.md | 158 -- v7_MIGRATION_GUIDE.md | 146 -- yarn.lock | 284 +-- 39 files changed, 7965 insertions(+), 2555 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 create mode 100644 src/management/api/resources/experimentation/resources/featureFlags/client/Client.ts rename src/management/api/resources/{organizations/resources/organizationTemplate => experimentation/resources/featureFlags}/client/index.ts (100%) create mode 100644 src/management/api/resources/experimentation/resources/featureFlags/exports.ts create mode 100644 src/management/api/resources/experimentation/resources/featureFlags/index.ts create mode 100644 src/management/api/resources/experimentation/resources/featureFlags/resources/index.ts create mode 100644 src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/Client.ts create mode 100644 src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/index.ts rename src/management/api/resources/{organizations/resources/organizationTemplate => experimentation/resources/featureFlags/resources/variations}/exports.ts (60%) rename src/management/api/resources/{organizations/resources/organizationTemplate => experimentation/resources/featureFlags/resources/variations}/index.ts (100%) create mode 100644 src/management/api/resources/experimentation/resources/segments/client/Client.ts create mode 100644 src/management/api/resources/experimentation/resources/segments/client/index.ts create mode 100644 src/management/api/resources/experimentation/resources/segments/exports.ts create mode 100644 src/management/api/resources/experimentation/resources/segments/index.ts delete mode 100644 src/management/api/resources/organizations/resources/organizationTemplate/client/Client.ts create mode 100644 src/management/tests/wire/experimentation/featureFlags.test.ts create mode 100644 src/management/tests/wire/experimentation/featureFlags/variations.test.ts create mode 100644 src/management/tests/wire/experimentation/segments.test.ts delete mode 100644 src/management/tests/wire/organizations/organizationTemplate.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/package.json b/package.json index 165459dd09..034fc9fb22 100644 --- a/package.json +++ b/package.json @@ -848,6 +848,39 @@ }, "default": "./dist/cjs/management/api/resources/experimentation/resources/experiments/exports.js" }, + "./experimentation/featureFlags": { + "import": { + "types": "./dist/esm/management/api/resources/experimentation/resources/featureFlags/exports.d.mts", + "default": "./dist/esm/management/api/resources/experimentation/resources/featureFlags/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/experimentation/resources/featureFlags/exports.d.ts", + "default": "./dist/cjs/management/api/resources/experimentation/resources/featureFlags/exports.js" + }, + "default": "./dist/cjs/management/api/resources/experimentation/resources/featureFlags/exports.js" + }, + "./experimentation/segments": { + "import": { + "types": "./dist/esm/management/api/resources/experimentation/resources/segments/exports.d.mts", + "default": "./dist/esm/management/api/resources/experimentation/resources/segments/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/experimentation/resources/segments/exports.d.ts", + "default": "./dist/cjs/management/api/resources/experimentation/resources/segments/exports.js" + }, + "default": "./dist/cjs/management/api/resources/experimentation/resources/segments/exports.js" + }, + "./experimentation/featureFlags/variations": { + "import": { + "types": "./dist/esm/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.d.mts", + "default": "./dist/esm/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.mjs" + }, + "require": { + "types": "./dist/cjs/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.d.ts", + "default": "./dist/cjs/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.js" + }, + "default": "./dist/cjs/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.js" + }, "./flows/executions": { "import": { "types": "./dist/esm/management/api/resources/flows/resources/executions/exports.d.mts", @@ -1189,17 +1222,6 @@ }, "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", diff --git a/reference.md b/reference.md index 54606ba90a..3eaefe015e 100644 --- a/reference.md +++ b/reference.md @@ -12206,20 +12206,22 @@ await client.userBlocks.delete("id");
-Retrieve details of users. It is possible to: +This endpoint retrieves details of users. It's best suited to interactive, best-effort search and lookups where slightly stale results are acceptable. With it, you can: -- Specify a search criteria for users +- Specify search criteria for users - Sort the users to be returned - Select the fields to be returned - Specify the number of users to retrieve per page and the page index -The `q` query parameter can be used to get users that match the specified criteria [using query string syntax.](https://auth0.com/docs/users/search/v3/query-syntax) +This endpoint is **not suited for use in critical paths**. It is eventually consistent and runs under a short (~2 second) query time limit, so results can be stale and heavy queries can return a 503. -[Learn more about searching for users.](https://auth0.com/docs/users/search/v3) +- Do not use this endpoint for authentication, account linking, or logic inside login-flow Actions. Instead, [look users up directly by ID or email](https://auth0.com/docs/manage-users/user-search/get-users-by-id-or-email#management-api) to get their current state. +- Do not use this endpoint to keep an external system in sync with user data. Instead, subscribe to [Event Streams](https://auth0.com/docs/customize/events/sync-data-across-systems) to receive every change as it happens. +- Do not use this endpoint to enumerate or export your entire user base. Instead, run a [bulk user export](https://auth0.com/docs/manage-users/user-migration/bulk-user-exports) to retrieve the full set. -Read about [best practices](https://auth0.com/docs/users/search/best-practices) when working with the API endpoints for retrieving users. +Use the `q` query parameter to match users with [query string syntax](https://auth0.com/docs/manage-users/user-search/user-search-query-syntax). For full instructions and guidance, see [How to List and Search Users](https://auth0.com/docs/manage-users/user-search/list-and-search-users). -Auth0 limits the number of users you can return. If you exceed this threshold, please redefine your search, use the [export job](https://auth0.com/docs/api/management/v2#!/Jobs/post_users_exports), or the [User Import / Export](https://auth0.com/docs/extensions/user-import-export) extension. +For efficient queries, prefer indexed top-level fields and exact matches. Certain kinds of queries can be slow and may time out, such as filtering on freeform or multi-value fields (like user-defined attributes in `app_metadata` or `user_metadata`) or using leading wildcards.
@@ -19380,6 +19382,357 @@ await client.eventStreams.redeliveries.createById("id", "event_id"); ## Experimentation Experiments +
client.experimentation.experiments.list({ ...params }) -> core.Page<Management.ExperimentListItem, Management.ListExperimentsResponseContent> +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a paginated list of experiments for the tenant, with optional filters. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +const pageableResponse = await client.experimentation.experiments.list({ + from: "from", + take: 1, + status: "draft", + authentication_flow: "authentication_flow", + feature_flag_id: "feature_flag_id", +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.experimentation.experiments.list({ + from: "from", + take: 1, + status: "draft", + authentication_flow: "authentication_flow", + feature_flag_id: "feature_flag_id", +}); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Management.ListExperimentsRequestParameters` + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.experiments.create({ ...params }) -> Management.CreateExperimentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a new experiment for A/B testing. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.create({ + name: "name", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Management.CreateExperimentRequestContent` + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.experiments.get(id) -> Management.GetExperimentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a single experiment with its allocations by ID. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.get("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the experiment to retrieve. + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.experiments.delete(id) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Permanently delete an experiment and its allocations by ID. Active experiments cannot be deleted; pause or complete first. Idempotent: returns 204 even if the experiment does not exist. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.delete("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the experiment to delete. + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.experiments.update(id, { ...params }) -> Management.UpdateExperimentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Partially update an experiment by ID. Only provided fields are updated. Providing allocations replaces the entire allocations set. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.update("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the experiment to update. + +
+
+ +
+
+ +**request:** `Management.UpdateExperimentRequestParameters` + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+
client.experimentation.experiments.advanceRamp(id, { ...params }) -> Management.AdvanceRampResponseContent
@@ -19392,7 +19745,1270 @@ await client.eventStreams.redeliveries.createById("id", "event_id");
-Increments the current ramp index to the requested target level. Up-only: the target must be the immediate next level in the schedule. Idempotent: calling with the current level returns success without writing anything. +Increments the current ramp index to the requested target level. Up-only: the target must be the immediate next level in the schedule. Idempotent: calling with the current level returns success without writing anything. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.advanceRamp("id", { + target_level: 1, +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the experiment to advance. + +
+
+ +
+
+ +**request:** `Management.AdvanceRampRequestContent` + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ + + +
+ +
client.experimentation.experiments.updateStatus(id, { ...params }) -> Management.UpdateExperimentStatusResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Transitions an experiment through its lifecycle: draft → active, active → paused, paused → active, active/paused → completed. Activation runs full readiness validation. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.updateStatus("id", { + status: "active", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the experiment to transition. + +
+
+ +
+
+ +**request:** `Management.UpdateExperimentStatusRequestContent` + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.experiments.validate(id) -> Management.ValidateExperimentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Checks whether an experiment is ready to be activated. Returns is_valid boolean and an errors array describing any blockers. Read-only; no state is modified. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.experiments.validate("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the experiment to validate. + +
+
+ +
+
+ +**requestOptions:** `ExperimentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Experimentation FeatureFlags + +
client.experimentation.featureFlags.list({ ...params }) -> core.Page<Management.FeatureFlag, Management.ListFeatureFlagsResponseContent> +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a paginated list of feature flags for the tenant. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +const pageableResponse = await client.experimentation.featureFlags.list({ + from: "from", + take: 1, + type: "auth0", + status: "draft", +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.experimentation.featureFlags.list({ + from: "from", + take: 1, + type: "auth0", + status: "draft", +}); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Management.ListFeatureFlagsRequestParameters` + +
+
+ +
+
+ +**requestOptions:** `FeatureFlagsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.create({ ...params }) -> Management.CreateFeatureFlagResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a new feature flag with parameters for use in experiments. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.create({ + name: "name", + parameters: {}, +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Management.CreateFeatureFlagRequestContent` + +
+
+ +
+
+ +**requestOptions:** `FeatureFlagsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.get(id) -> Management.GetFeatureFlagResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a single feature flag by its ID. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.get("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the feature flag to retrieve. + +
+
+ +
+
+ +**requestOptions:** `FeatureFlagsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.delete(id) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a feature flag by ID. Idempotent: returns 204 even if flag does not exist. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.delete("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the feature flag to delete. + +
+
+ +
+
+ +**requestOptions:** `FeatureFlagsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.update(id, { ...params }) -> Management.UpdateFeatureFlagResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Partially update a feature flag by ID. Only provided fields are updated. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.update("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the feature flag to update. + +
+
+ +
+
+ +**request:** `Management.UpdateFeatureFlagRequestContent` + +
+
+ +
+
+ +**requestOptions:** `FeatureFlagsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.updateStatus(id, { ...params }) -> Management.UpdateFeatureFlagStatusResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Transitions a feature flag through its lifecycle states: draft → active, draft → archived, active → archived. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the feature flag to transition. + +
+
+ +
+
+ +**request:** `Management.UpdateFeatureFlagStatusRequestContent` + +
+
+ +
+
+ +**requestOptions:** `FeatureFlagsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Experimentation Segments + +
client.experimentation.segments.list({ ...params }) -> core.Page<Management.Segment, Management.ListSegmentsResponseContent> +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a paginated list of segments for the tenant. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +const pageableResponse = await client.experimentation.segments.list({ + from: "from", + take: 1, + type: "auth0", +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.experimentation.segments.list({ + from: "from", + take: 1, + type: "auth0", +}); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Management.ListSegmentsRequestParameters` + +
+
+ +
+
+ +**requestOptions:** `SegmentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.segments.create({ ...params }) -> Management.CreateSegmentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a new segment with rule-based membership criteria for use in experiments. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.segments.create({ + name: "name", + rules: [{}], +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Management.CreateSegmentRequestContent` + +
+
+ +
+
+ +**requestOptions:** `SegmentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.segments.get(id) -> Management.GetSegmentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a single segment by its ID. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.segments.get("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the segment to retrieve. + +
+
+ +
+
+ +**requestOptions:** `SegmentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.segments.delete(id) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a segment by ID. Idempotent: returns 204 even if segment does not exist. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.segments.delete("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the segment to delete. + +
+
+ +
+
+ +**requestOptions:** `SegmentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.segments.update(id, { ...params }) -> Management.UpdateSegmentResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Partially update a segment by ID. Only provided fields are updated. Sending rules replaces the entire rules array. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.segments.update("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the segment to update. + +
+
+ +
+
+ +**request:** `Management.UpdateSegmentRequestContent` + +
+
+ +
+
+ +**requestOptions:** `SegmentsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +## Experimentation FeatureFlags Variations + +
client.experimentation.featureFlags.variations.list(id) -> Management.ListVariationsResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve all variations defined for a specific feature flag. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.variations.list("id"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the parent feature flag. + +
+
+ +
+
+ +**requestOptions:** `VariationsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.variations.create(id, { ...params }) -> Management.CreateVariationResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a new variation with parameter overrides for a specific feature flag. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.variations.create("id", { + name: "name", + overrides: { + key: "value", + }, +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the parent feature flag. + +
+
+ +
+
+ +**request:** `Management.CreateVariationRequestContent` + +
+
+ +
+
+ +**requestOptions:** `VariationsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.variations.get(id, vid) -> Management.GetVariationResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a single variation by its ID. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.variations.get("id", "vid"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the parent feature flag. + +
+
+ +
+
+ +**vid:** `string` — The ID of the variation to retrieve. + +
+
+ +
+
+ +**requestOptions:** `VariationsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.variations.delete(id, vid) -> void +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a variation by ID. Returns 204 if the variation does not exist. Returns 404 if the parent feature flag does not exist. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.experimentation.featureFlags.variations.delete("id", "vid"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `string` — The ID of the parent feature flag. + +
+
+ +
+
+ +**vid:** `string` — The ID of the variation to delete. + +
+
+ +
+
+ +**requestOptions:** `VariationsClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.experimentation.featureFlags.variations.update(id, vid, { ...params }) -> Management.UpdateVariationResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Partially update a variation by ID. Only provided fields are updated.
@@ -19408,9 +21024,7 @@ Increments the current ramp index to the requested target level. Up-only: the ta
```typescript -await client.experimentation.experiments.advanceRamp("id", { - target_level: 1, -}); +await client.experimentation.featureFlags.variations.update("id", "vid"); ```
@@ -19426,7 +21040,7 @@ await client.experimentation.experiments.advanceRamp("id", {
-**id:** `string` — The ID of the experiment to advance. +**id:** `string` — The ID of the parent feature flag.
@@ -19434,7 +21048,7 @@ await client.experimentation.experiments.advanceRamp("id", {
-**request:** `Management.AdvanceRampRequestContent` +**vid:** `string` — The ID of the variation to update.
@@ -19442,7 +21056,15 @@ await client.experimentation.experiments.advanceRamp("id", {
-**requestOptions:** `ExperimentsClient.RequestOptions` +**request:** `Management.UpdateVariationRequestContent` + +
+
+ +
+
+ +**requestOptions:** `VariationsClient.RequestOptions`
@@ -26697,213 +28319,6 @@ await client.organizations.members.delete("id", {
-## Organizations OrganizationTemplate - -
client.organizations.organizationTemplate.get(id) -> Management.OrganizationTemplate -
-
- -#### 📝 Description - -
-
- -
-
- -Retrieve the organization template assigned to a specific organization. Returns the template object if one is explicitly assigned, or a 404 if no template is assigned. - -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.organizations.organizationTemplate.get("id"); -``` - -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `string` — ID of the organization. - -
-
- -
-
- -**requestOptions:** `OrganizationTemplateClient.RequestOptions` - -
-
-
-
- -
-
-
- -
client.organizations.organizationTemplate.assignOrganizationTemplate(id, template_id) -> void -
-
- -#### 📝 Description - -
-
- -
-
- -Assign an Organization Template to an organization. - -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.organizations.organizationTemplate.assignOrganizationTemplate("id", "template_id"); -``` - -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `string` — The ID of the organization. - -
-
- -
-
- -**template_id:** `string` — The ID of the organization template to assign. - -
-
- -
-
- -**requestOptions:** `OrganizationTemplateClient.RequestOptions` - -
-
-
-
- -
-
-
- -
client.organizations.organizationTemplate.unassignOrganizationTemplate(id, template_id) -> void -
-
- -#### 📝 Description - -
-
- -
-
- -Remove an Organization Template assignment from an organization. - -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.organizations.organizationTemplate.unassignOrganizationTemplate("id", "template_id"); -``` - -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `string` — The ID of the organization. - -
-
- -
-
- -**template_id:** `string` — The ID of the organization template to unassign. - -
-
- -
-
- -**requestOptions:** `OrganizationTemplateClient.RequestOptions` - -
-
-
-
- -
-
-
- ## Organizations Groups
client.organizations.groups.list(organization_id, { ...params }) -> core.Page<Management.Group, Management.ListOrganizationGroupsResponseContent> diff --git a/src/management/api/requests/requests.ts b/src/management/api/requests/requests.ts index 6aa4b57cad..d29889f4fa 100644 --- a/src/management/api/requests/requests.ts +++ b/src/management/api/requests/requests.ts @@ -364,6 +364,7 @@ export interface CreateClientRequestContent { b2b_integration_configuration?: Management.B2BIntegrationConfiguration; my_organization_configuration?: Management.ClientMyOrganizationPostConfiguration; async_approval_notification_channels?: Management.ClientAsyncApprovalNotificationsChannelsApiPostConfiguration; + oidc_support?: Management.ClientOidcSupportPost; } /** @@ -1558,6 +1559,8 @@ export interface CreateResourceServerRequestContent { access_token?: Management.ResourceServerAccessToken | null; token_encryption?: Management.ResourceServerTokenEncryption | null; consent_policy?: Management.ResourceServerConsentPolicyEnum | null; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean; authorization_details?: unknown[] | null; proof_of_possession?: Management.ResourceServerProofOfPossession | null; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization; @@ -1634,6 +1637,8 @@ export interface UpdateResourceServerRequestContent { access_token?: Management.ResourceServerAccessToken | null; token_encryption?: Management.ResourceServerTokenEncryption | null; consent_policy?: Management.ResourceServerConsentPolicyEnum | null; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean; authorization_details?: unknown[] | null; proof_of_possession?: Management.ResourceServerProofOfPossession | null; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization; @@ -2012,8 +2017,8 @@ export interface ListUserBlocksByIdentifierRequestParameters { /** Should be any of a username, phone number, or email. */ identifier: string; /** - * If true and Brute Force Protection is enabled and configured to block logins, will return a list of blocked IP addresses. - * If true and Brute Force Protection is disabled, will return an empty list. + * If true, returns only blocks that are currently enforced (e.g. subject to protection status, IP allowlist, etc.). + * If false or omitted, returns all blocks regardless of enforcement state. * */ consider_brute_force_enablement?: boolean | null; @@ -2038,8 +2043,8 @@ export interface DeleteUserBlocksByIdentifierRequestParameters { */ export interface ListUserBlocksRequestParameters { /** - * If true and Brute Force Protection is enabled and configured to block logins, will return a list of blocked IP addresses. - * If true and Brute Force Protection is disabled, will return an empty list. + * If true, returns only blocks that are currently enforced (e.g. subject to protection status, IP allowlist, etc.). + * If false or omitted, returns all blocks regardless of enforcement state. * */ consider_brute_force_enablement?: boolean | null; @@ -2991,6 +2996,74 @@ export interface CreateEventStreamRedeliveryRequestContent { event_types?: Management.EventStreamEventTypeEnum[]; } +/** + * @example + * { + * from: "from", + * take: 1, + * status: "draft", + * authentication_flow: "authentication_flow", + * feature_flag_id: "feature_flag_id" + * } + */ +export interface ListExperimentsRequestParameters { + /** Optional Id from which to start selection. */ + from?: string | null; + /** Number of experiments to return per page. Defaults to 25, maximum 50. */ + take?: number | null; + /** Filter by status. Exact match. */ + status?: Management.ExperimentStatusEnum | null; + /** Filter by authentication flow. Exact match. */ + authentication_flow?: string | null; + /** Filter by feature flag ID. Exact match. */ + feature_flag_id?: string | null; +} + +/** + * @example + * { + * name: "name", + * feature_flag_id: "feature_flag_id", + * authentication_flow: "authentication" + * } + */ +export interface CreateExperimentRequestContent { + /** A human-readable name for the experiment */ + name: string; + /** A description of the experiment */ + description?: string; + /** The ID of the feature flag this experiment is based on */ + feature_flag_id: string; + authentication_flow: Management.AuthenticationFlowEnum; + /** Applies only to Auth0-managed flags. Controls where non-overridden config keys resolve from: 'tenant' inherits the tenant's live config so the experiment overlays only its changes, 'flag' uses the flag's frozen defaults for a complete config. Optional; defaults to 'tenant' when omitted. Rejected for customer-defined flags. */ + default_config?: Management.DefaultConfigEnum; + /** The traffic allocation strategy for this experiment */ + allocation_strategy?: Management.AllocationStrategyEnum; + /** Traffic allocations mapping variations to weights or segments */ + allocations?: Management.AllocationRequestItem[]; + /** Ramp experiment levels configuration. A strictly-increasing sequence of exposure percentages, each an integer in [0, 100]. */ + levels?: number[]; +} + +/** + * @example + * {} + */ +export interface UpdateExperimentRequestParameters { + /** A human-readable name for the experiment */ + name?: string; + /** A description of the experiment */ + description?: string | null; + /** Specifies the target authentication flow for this experiment. This field can only be modified on draft experiments. Must be one of: authentication, mfa_enrollment, mfa_challenge, password_reset, passkey_enrollment, or all. Note that the all value targets every flow at once, but requires that this is the only active experiment. */ + authentication_flow?: Management.AuthenticationFlowEnum; + /** Replaces all traffic allocations. Cannot be modified while the experiment is active. */ + allocations?: Management.AllocationRequestItem[]; + /** Applies only to Auth0-managed flags. Controls where non-overridden config keys resolve from: 'tenant' inherits the tenant's live config, 'flag' uses the flag's frozen defaults. Can only be modified on draft experiments. Rejected for customer-defined flags. */ + default_config?: Management.DefaultConfigEnum; + /** Ramp experiment levels configuration. A strictly-increasing sequence of exposure percentages, each an integer in [0, 100]. Can only be modified on draft experiments. */ + levels?: number[]; +} + /** * @example * { @@ -3002,6 +3075,150 @@ export interface AdvanceRampRequestContent { target_level: number; } +/** + * @example + * { + * status: "active" + * } + */ +export interface UpdateExperimentStatusRequestContent { + status: Management.ExperimentTransitionStatusEnum; +} + +/** + * @example + * { + * from: "from", + * take: 1, + * type: "auth0", + * status: "draft" + * } + */ +export interface ListFeatureFlagsRequestParameters { + /** Optional Id from which to start selection. */ + from?: string | null; + /** Number of feature flags to return per page. Defaults to 25, maximum 50. */ + take?: number | null; + /** Filter by type. Exact match. */ + type?: Management.FeatureFlagTypeEnum | null; + /** Filter by status. Exact match. */ + status?: Management.FeatureFlagStatusEnum | null; +} + +/** + * @example + * { + * name: "name", + * parameters: {} + * } + */ +export interface CreateFeatureFlagRequestContent { + /** A human-readable name for the feature flag */ + name: string; + /** A description of what this feature flag controls */ + description?: string; + parameters: Management.CreateFeatureFlagParameters; +} + +/** + * @example + * {} + */ +export interface UpdateFeatureFlagRequestContent { + /** A human-readable name for the feature flag */ + name?: string; + /** A description of what this feature flag controls */ + description?: string | null; + parameters?: Management.UpdateFeatureFlagParameters; +} + +/** + * @example + * { + * status: "draft" + * } + */ +export interface UpdateFeatureFlagStatusRequestContent { + /** The target status to transition the feature flag to. */ + status: Management.FeatureFlagStatusEnum; +} + +/** + * @example + * { + * from: "from", + * take: 1, + * type: "auth0" + * } + */ +export interface ListSegmentsRequestParameters { + /** Optional Id from which to start selection. */ + from?: string | null; + /** Number of segments to return per page. Defaults to 25, maximum 50. */ + take?: number | null; + /** Filter by type. Exact match. */ + type?: Management.SegmentTypeFilterEnum | null; +} + +/** + * @example + * { + * name: "name", + * rules: [{}] + * } + */ +export interface CreateSegmentRequestContent { + /** A human-readable name for the segment */ + name: string; + /** A description of the segment */ + description?: string; + /** An ordered list of rules. A segment matches if any rule matches. Each rule is limited to 4KB and the whole segment to 10KB (serialized). */ + rules: Management.SegmentRule[]; +} + +/** + * @example + * {} + */ +export interface UpdateSegmentRequestContent { + /** A human-readable name for the segment */ + name?: string; + /** A description of the segment */ + description?: string | null; + /** Replaces the entire rules array. Each rule is limited to 4KB and the whole segment to 10KB (serialized). */ + rules?: Management.SegmentRule[]; +} + +/** + * @example + * { + * name: "name", + * overrides: { + * "key": "value" + * } + * } + */ +export interface CreateVariationRequestContent { + /** A human-readable name for the variation */ + name: string; + /** A description of what this variation controls */ + description?: string; + /** Configuration overrides for this variation; keys must exist in the parent flag parameters. Empty {} is the baseline (control) variation that overrides nothing. */ + overrides: Management.VariationOverridesMap; +} + +/** + * @example + * {} + */ +export interface UpdateVariationRequestContent { + /** A human-readable name for the variation */ + name?: string; + /** A description of what this variation controls */ + description?: string | null; + overrides?: Management.UpdateVariationOverridesMap; +} + /** * @example * { diff --git a/src/management/api/resources/experimentation/client/Client.ts b/src/management/api/resources/experimentation/client/Client.ts index 6c6c08027f..7ffd84778c 100644 --- a/src/management/api/resources/experimentation/client/Client.ts +++ b/src/management/api/resources/experimentation/client/Client.ts @@ -5,6 +5,8 @@ import { normalizeClientOptionsWithAuth, type NormalizedClientOptionsWithAuth } import * as core from "../../../../core/index.js"; import * as environments from "../../../../environments.js"; import { ExperimentsClient } from "../resources/experiments/client/Client.js"; +import { FeatureFlagsClient } from "../resources/featureFlags/client/Client.js"; +import { SegmentsClient } from "../resources/segments/client/Client.js"; export declare namespace ExperimentationClient { export type Options = BaseClientOptions; @@ -13,6 +15,8 @@ export declare namespace ExperimentationClient { export class ExperimentationClient { protected readonly _options: NormalizedClientOptionsWithAuth; protected _experiments: ExperimentsClient | undefined; + protected _featureFlags: FeatureFlagsClient | undefined; + protected _segments: SegmentsClient | undefined; constructor(options: ExperimentationClient.Options) { this._options = normalizeClientOptionsWithAuth(options); @@ -21,4 +25,12 @@ export class ExperimentationClient { public get experiments(): ExperimentsClient { return (this._experiments ??= new ExperimentsClient(this._options)); } + + public get featureFlags(): FeatureFlagsClient { + return (this._featureFlags ??= new FeatureFlagsClient(this._options)); + } + + public get segments(): SegmentsClient { + return (this._segments ??= new SegmentsClient(this._options)); + } } diff --git a/src/management/api/resources/experimentation/resources/experiments/client/Client.ts b/src/management/api/resources/experimentation/resources/experiments/client/Client.ts index fd6aec06fc..4af362d47e 100644 --- a/src/management/api/resources/experimentation/resources/experiments/client/Client.ts +++ b/src/management/api/resources/experimentation/resources/experiments/client/Client.ts @@ -22,6 +22,475 @@ export class ExperimentsClient { this._options = normalizeClientOptionsWithAuth(options); } + /** + * Retrieve a paginated list of experiments for the tenant, with optional filters. + * + * @param {Management.ListExperimentsRequestParameters} request + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.list({ + * from: "from", + * take: 1, + * status: "draft", + * authentication_flow: "authentication_flow", + * feature_flag_id: "feature_flag_id" + * }) + */ + public async list( + request: Management.ListExperimentsRequestParameters = {}, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const list = core.HttpResponsePromise.interceptFunction( + async ( + request: Management.ListExperimentsRequestParameters, + ): Promise> => { + const { + from: from_, + take = 50, + status, + authentication_flow: authenticationFlow, + feature_flag_id: featureFlagId, + } = request; + const _queryParams: Record = { + from: from_, + take, + status: status !== undefined ? status : undefined, + authentication_flow: authenticationFlow, + feature_flag_id: featureFlagId, + }; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + "experimentation/experiments", + ), + method: "GET", + headers: _headers, + queryString: core.url + .queryBuilder() + .addMany(_queryParams) + .mergeAdditional(requestOptions?.queryParams) + .build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.ListExperimentsResponseContent, + rawResponse: _response.rawResponse, + }; + } + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 401: + throw new Management.UnauthorizedError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError( + _response.error.body as unknown, + _response.rawResponse, + ); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/experiments", + ); + }, + ); + const dataWithRawResponse = await list(request).withRawResponse(); + return new core.Page({ + response: dataWithRawResponse.data, + rawResponse: dataWithRawResponse.rawResponse, + hasNextPage: (response) => + response?.next != null && !(typeof response?.next === "string" && response?.next === ""), + getItems: (response) => response?.experiments ?? [], + loadPage: (response) => { + return list(core.setObjectProperty(request, "from", response?.next)); + }, + }); + } + + /** + * Create a new experiment for A/B testing. + * + * @param {Management.CreateExperimentRequestContent} request + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.UnprocessableEntityError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.create({ + * name: "name", + * feature_flag_id: "feature_flag_id", + * authentication_flow: "authentication" + * }) + */ + public create( + request: Management.CreateExperimentRequestContent, + requestOptions?: ExperimentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: Management.CreateExperimentRequestContent, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + "experimentation/experiments", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.CreateExperimentResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 422: + throw new Management.UnprocessableEntityError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/experimentation/experiments"); + } + + /** + * Retrieve a single experiment with its allocations by ID. + * + * @param {string} id - The ID of the experiment to retrieve. + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.get("id") + */ + public get( + id: string, + requestOptions?: ExperimentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(id, requestOptions)); + } + + private async __get( + id: string, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/experiments/${core.url.encodePathParam(id)}`, + ), + method: "GET", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.GetExperimentResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/experiments/{id}", + ); + } + + /** + * Permanently delete an experiment and its allocations by ID. Active experiments cannot be deleted; pause or complete first. Idempotent: returns 204 even if the experiment does not exist. + * + * @param {string} id - The ID of the experiment to delete. + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.delete("id") + */ + public delete(id: string, requestOptions?: ExperimentsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(id, requestOptions)); + } + + private async __delete( + id: string, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/experiments/${core.url.encodePathParam(id)}`, + ), + method: "DELETE", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "DELETE", + "/experimentation/experiments/{id}", + ); + } + + /** + * Partially update an experiment by ID. Only provided fields are updated. Providing allocations replaces the entire allocations set. + * + * @param {string} id - The ID of the experiment to update. + * @param {Management.UpdateExperimentRequestParameters} request + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.UnprocessableEntityError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.update("id") + */ + public update( + id: string, + request: Management.UpdateExperimentRequestParameters = {}, + requestOptions?: ExperimentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, request, requestOptions)); + } + + private async __update( + id: string, + request: Management.UpdateExperimentRequestParameters = {}, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/experiments/${core.url.encodePathParam(id)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.UpdateExperimentResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 422: + throw new Management.UnprocessableEntityError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "PATCH", + "/experimentation/experiments/{id}", + ); + } + /** * Increments the current ramp index to the requested target level. Up-only: the target must be the immediate next level in the schedule. Idempotent: calling with the current level returns success without writing anything. * @@ -116,4 +585,186 @@ export class ExperimentsClient { "/experimentation/experiments/{id}/advance-ramp", ); } + + /** + * Transitions an experiment through its lifecycle: draft → active, active → paused, paused → active, active/paused → completed. Activation runs full readiness validation. + * + * @param {string} id - The ID of the experiment to transition. + * @param {Management.UpdateExperimentStatusRequestContent} request + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.UnprocessableEntityError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.updateStatus("id", { + * status: "active" + * }) + */ + public updateStatus( + id: string, + request: Management.UpdateExperimentStatusRequestContent, + requestOptions?: ExperimentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__updateStatus(id, request, requestOptions)); + } + + private async __updateStatus( + id: string, + request: Management.UpdateExperimentStatusRequestContent, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/experiments/${core.url.encodePathParam(id)}/status`, + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.UpdateExperimentStatusResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 422: + throw new Management.UnprocessableEntityError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/experimentation/experiments/{id}/status", + ); + } + + /** + * Checks whether an experiment is ready to be activated. Returns is_valid boolean and an errors array describing any blockers. Read-only; no state is modified. + * + * @param {string} id - The ID of the experiment to validate. + * @param {ExperimentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.experiments.validate("id") + */ + public validate( + id: string, + requestOptions?: ExperimentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__validate(id, requestOptions)); + } + + private async __validate( + id: string, + requestOptions?: ExperimentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/experiments/${core.url.encodePathParam(id)}/validate`, + ), + method: "POST", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.ValidateExperimentResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/experimentation/experiments/{id}/validate", + ); + } } diff --git a/src/management/api/resources/experimentation/resources/featureFlags/client/Client.ts b/src/management/api/resources/experimentation/resources/featureFlags/client/Client.ts new file mode 100644 index 0000000000..c6063b6dac --- /dev/null +++ b/src/management/api/resources/experimentation/resources/featureFlags/client/Client.ts @@ -0,0 +1,572 @@ +// This file was auto-generated by Fern from our API Definition. + +import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; +import { normalizeClientOptionsWithAuth, type NormalizedClientOptionsWithAuth } from "../../../../../../BaseClient.js"; +import * as core from "../../../../../../core/index.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; +import * as environments from "../../../../../../environments.js"; +import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; +import * as errors from "../../../../../../errors/index.js"; +import * as Management from "../../../../../index.js"; +import { VariationsClient } from "../resources/variations/client/Client.js"; + +export declare namespace FeatureFlagsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class FeatureFlagsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _variations: VariationsClient | undefined; + + constructor(options: FeatureFlagsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + public get variations(): VariationsClient { + return (this._variations ??= new VariationsClient(this._options)); + } + + /** + * Retrieve a paginated list of feature flags for the tenant. + * + * @param {Management.ListFeatureFlagsRequestParameters} request + * @param {FeatureFlagsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.list({ + * from: "from", + * take: 1, + * type: "auth0", + * status: "draft" + * }) + */ + public async list( + request: Management.ListFeatureFlagsRequestParameters = {}, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): Promise> { + const list = core.HttpResponsePromise.interceptFunction( + async ( + request: Management.ListFeatureFlagsRequestParameters, + ): Promise> => { + const { from: from_, take = 50, type: type_, status } = request; + const _queryParams: Record = { + from: from_, + take, + type: type_ !== undefined ? type_ : undefined, + status: status !== undefined ? status : undefined, + }; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + "experimentation/feature-flags", + ), + method: "GET", + headers: _headers, + queryString: core.url + .queryBuilder() + .addMany(_queryParams) + .mergeAdditional(requestOptions?.queryParams) + .build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.ListFeatureFlagsResponseContent, + rawResponse: _response.rawResponse, + }; + } + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 401: + throw new Management.UnauthorizedError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError( + _response.error.body as unknown, + _response.rawResponse, + ); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/feature-flags", + ); + }, + ); + const dataWithRawResponse = await list(request).withRawResponse(); + return new core.Page({ + response: dataWithRawResponse.data, + rawResponse: dataWithRawResponse.rawResponse, + hasNextPage: (response) => + response?.next != null && !(typeof response?.next === "string" && response?.next === ""), + getItems: (response) => response?.feature_flags ?? [], + loadPage: (response) => { + return list(core.setObjectProperty(request, "from", response?.next)); + }, + }); + } + + /** + * Create a new feature flag with parameters for use in experiments. + * + * @param {Management.CreateFeatureFlagRequestContent} request + * @param {FeatureFlagsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.create({ + * name: "name", + * parameters: {} + * }) + */ + public create( + request: Management.CreateFeatureFlagRequestContent, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: Management.CreateFeatureFlagRequestContent, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + "experimentation/feature-flags", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.CreateFeatureFlagResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/experimentation/feature-flags", + ); + } + + /** + * Retrieve a single feature flag by its ID. + * + * @param {string} id - The ID of the feature flag to retrieve. + * @param {FeatureFlagsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.get("id") + */ + public get( + id: string, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(id, requestOptions)); + } + + private async __get( + id: string, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}`, + ), + method: "GET", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.GetFeatureFlagResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/feature-flags/{id}", + ); + } + + /** + * Delete a feature flag by ID. Idempotent: returns 204 even if flag does not exist. + * + * @param {string} id - The ID of the feature flag to delete. + * @param {FeatureFlagsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.delete("id") + */ + public delete(id: string, requestOptions?: FeatureFlagsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(id, requestOptions)); + } + + private async __delete( + id: string, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}`, + ), + method: "DELETE", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "DELETE", + "/experimentation/feature-flags/{id}", + ); + } + + /** + * Partially update a feature flag by ID. Only provided fields are updated. + * + * @param {string} id - The ID of the feature flag to update. + * @param {Management.UpdateFeatureFlagRequestContent} request + * @param {FeatureFlagsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.update("id") + */ + public update( + id: string, + request: Management.UpdateFeatureFlagRequestContent = {}, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, request, requestOptions)); + } + + private async __update( + id: string, + request: Management.UpdateFeatureFlagRequestContent = {}, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.UpdateFeatureFlagResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "PATCH", + "/experimentation/feature-flags/{id}", + ); + } + + /** + * Transitions a feature flag through its lifecycle states: draft → active, draft → archived, active → archived. + * + * @param {string} id - The ID of the feature flag to transition. + * @param {Management.UpdateFeatureFlagStatusRequestContent} request + * @param {FeatureFlagsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.updateStatus("id", { + * status: "draft" + * }) + */ + public updateStatus( + id: string, + request: Management.UpdateFeatureFlagStatusRequestContent, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__updateStatus(id, request, requestOptions)); + } + + private async __updateStatus( + id: string, + request: Management.UpdateFeatureFlagStatusRequestContent, + requestOptions?: FeatureFlagsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}/status`, + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.UpdateFeatureFlagStatusResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/experimentation/feature-flags/{id}/status", + ); + } +} diff --git a/src/management/api/resources/organizations/resources/organizationTemplate/client/index.ts b/src/management/api/resources/experimentation/resources/featureFlags/client/index.ts similarity index 100% rename from src/management/api/resources/organizations/resources/organizationTemplate/client/index.ts rename to src/management/api/resources/experimentation/resources/featureFlags/client/index.ts diff --git a/src/management/api/resources/experimentation/resources/featureFlags/exports.ts b/src/management/api/resources/experimentation/resources/featureFlags/exports.ts new file mode 100644 index 0000000000..efc7a6c7b2 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/featureFlags/exports.ts @@ -0,0 +1,5 @@ +// This file was auto-generated by Fern from our API Definition. + +export { FeatureFlagsClient } from "./client/Client.js"; +export * from "./client/index.js"; +export * from "./resources/index.js"; diff --git a/src/management/api/resources/experimentation/resources/featureFlags/index.ts b/src/management/api/resources/experimentation/resources/featureFlags/index.ts new file mode 100644 index 0000000000..9eb1192dcc --- /dev/null +++ b/src/management/api/resources/experimentation/resources/featureFlags/index.ts @@ -0,0 +1,2 @@ +export * from "./client/index.js"; +export * from "./resources/index.js"; diff --git a/src/management/api/resources/experimentation/resources/featureFlags/resources/index.ts b/src/management/api/resources/experimentation/resources/featureFlags/resources/index.ts new file mode 100644 index 0000000000..36bfcf8457 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/featureFlags/resources/index.ts @@ -0,0 +1 @@ +export * as variations from "./variations/index.js"; diff --git a/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/Client.ts b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/Client.ts new file mode 100644 index 0000000000..bf6327c711 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/Client.ts @@ -0,0 +1,473 @@ +// This file was auto-generated by Fern from our API Definition. + +import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../../../BaseClient.js"; +import { + normalizeClientOptionsWithAuth, + type NormalizedClientOptionsWithAuth, +} from "../../../../../../../../BaseClient.js"; +import * as core from "../../../../../../../../core/index.js"; +import { mergeHeaders } from "../../../../../../../../core/headers.js"; +import * as environments from "../../../../../../../../environments.js"; +import { handleNonStatusCodeError } from "../../../../../../../../errors/handleNonStatusCodeError.js"; +import * as errors from "../../../../../../../../errors/index.js"; +import * as Management from "../../../../../../../index.js"; + +export declare namespace VariationsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class VariationsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: VariationsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * Retrieve all variations defined for a specific feature flag. + * + * @param {string} id - The ID of the parent feature flag. + * @param {VariationsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.variations.list("id") + */ + public list( + id: string, + requestOptions?: VariationsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__list(id, requestOptions)); + } + + private async __list( + id: string, + requestOptions?: VariationsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}/variations`, + ), + method: "GET", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.ListVariationsResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/feature-flags/{id}/variations", + ); + } + + /** + * Create a new variation with parameter overrides for a specific feature flag. + * + * @param {string} id - The ID of the parent feature flag. + * @param {Management.CreateVariationRequestContent} request + * @param {VariationsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.variations.create("id", { + * name: "name", + * overrides: { + * "key": "value" + * } + * }) + */ + public create( + id: string, + request: Management.CreateVariationRequestContent, + requestOptions?: VariationsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(id, request, requestOptions)); + } + + private async __create( + id: string, + request: Management.CreateVariationRequestContent, + requestOptions?: VariationsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}/variations`, + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.CreateVariationResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "POST", + "/experimentation/feature-flags/{id}/variations", + ); + } + + /** + * Retrieve a single variation by its ID. + * + * @param {string} id - The ID of the parent feature flag. + * @param {string} vid - The ID of the variation to retrieve. + * @param {VariationsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.variations.get("id", "vid") + */ + public get( + id: string, + vid: string, + requestOptions?: VariationsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(id, vid, requestOptions)); + } + + private async __get( + id: string, + vid: string, + requestOptions?: VariationsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}/variations/${core.url.encodePathParam(vid)}`, + ), + method: "GET", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.GetVariationResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/feature-flags/{id}/variations/{vid}", + ); + } + + /** + * Delete a variation by ID. Returns 204 if the variation does not exist. Returns 404 if the parent feature flag does not exist. + * + * @param {string} id - The ID of the parent feature flag. + * @param {string} vid - The ID of the variation to delete. + * @param {VariationsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.variations.delete("id", "vid") + */ + public delete( + id: string, + vid: string, + requestOptions?: VariationsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(id, vid, requestOptions)); + } + + private async __delete( + id: string, + vid: string, + requestOptions?: VariationsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}/variations/${core.url.encodePathParam(vid)}`, + ), + method: "DELETE", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "DELETE", + "/experimentation/feature-flags/{id}/variations/{vid}", + ); + } + + /** + * Partially update a variation by ID. Only provided fields are updated. + * + * @param {string} id - The ID of the parent feature flag. + * @param {string} vid - The ID of the variation to update. + * @param {Management.UpdateVariationRequestContent} request + * @param {VariationsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.featureFlags.variations.update("id", "vid") + */ + public update( + id: string, + vid: string, + request: Management.UpdateVariationRequestContent = {}, + requestOptions?: VariationsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, vid, request, requestOptions)); + } + + private async __update( + id: string, + vid: string, + request: Management.UpdateVariationRequestContent = {}, + requestOptions?: VariationsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/feature-flags/${core.url.encodePathParam(id)}/variations/${core.url.encodePathParam(vid)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.UpdateVariationResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "PATCH", + "/experimentation/feature-flags/{id}/variations/{vid}", + ); + } +} diff --git a/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/index.ts b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/management/api/resources/organizations/resources/organizationTemplate/exports.ts b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.ts similarity index 60% rename from src/management/api/resources/organizations/resources/organizationTemplate/exports.ts rename to src/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.ts index 17a5c91705..2dd89fe384 100644 --- a/src/management/api/resources/organizations/resources/organizationTemplate/exports.ts +++ b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/exports.ts @@ -1,4 +1,4 @@ // This file was auto-generated by Fern from our API Definition. -export { OrganizationTemplateClient } from "./client/Client.js"; +export { VariationsClient } from "./client/Client.js"; export * from "./client/index.js"; diff --git a/src/management/api/resources/organizations/resources/organizationTemplate/index.ts b/src/management/api/resources/experimentation/resources/featureFlags/resources/variations/index.ts similarity index 100% rename from src/management/api/resources/organizations/resources/organizationTemplate/index.ts rename to src/management/api/resources/experimentation/resources/featureFlags/resources/variations/index.ts diff --git a/src/management/api/resources/experimentation/resources/index.ts b/src/management/api/resources/experimentation/resources/index.ts index 83a24e6336..ae6d559ab1 100644 --- a/src/management/api/resources/experimentation/resources/index.ts +++ b/src/management/api/resources/experimentation/resources/index.ts @@ -1 +1,3 @@ export * as experiments from "./experiments/index.js"; +export * as featureFlags from "./featureFlags/index.js"; +export * as segments from "./segments/index.js"; diff --git a/src/management/api/resources/experimentation/resources/segments/client/Client.ts b/src/management/api/resources/experimentation/resources/segments/client/Client.ts new file mode 100644 index 0000000000..2d7154adf8 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/segments/client/Client.ts @@ -0,0 +1,476 @@ +// This file was auto-generated by Fern from our API Definition. + +import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; +import { normalizeClientOptionsWithAuth, type NormalizedClientOptionsWithAuth } from "../../../../../../BaseClient.js"; +import * as core from "../../../../../../core/index.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; +import * as environments from "../../../../../../environments.js"; +import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; +import * as errors from "../../../../../../errors/index.js"; +import * as Management from "../../../../../index.js"; + +export declare namespace SegmentsClient { + export type Options = BaseClientOptions; + + export interface RequestOptions extends BaseRequestOptions {} +} + +export class SegmentsClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + + constructor(options: SegmentsClient.Options) { + this._options = normalizeClientOptionsWithAuth(options); + } + + /** + * Retrieve a paginated list of segments for the tenant. + * + * @param {Management.ListSegmentsRequestParameters} request + * @param {SegmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.segments.list({ + * from: "from", + * take: 1, + * type: "auth0" + * }) + */ + public async list( + request: Management.ListSegmentsRequestParameters = {}, + requestOptions?: SegmentsClient.RequestOptions, + ): Promise> { + const list = core.HttpResponsePromise.interceptFunction( + async ( + request: Management.ListSegmentsRequestParameters, + ): Promise> => { + const { from: from_, take = 50, type: type_ } = request; + const _queryParams: Record = { + from: from_, + take, + type: type_ !== undefined ? type_ : undefined, + }; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + "experimentation/segments", + ), + method: "GET", + headers: _headers, + queryString: core.url + .queryBuilder() + .addMany(_queryParams) + .mergeAdditional(requestOptions?.queryParams) + .build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.ListSegmentsResponseContent, + rawResponse: _response.rawResponse, + }; + } + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 401: + throw new Management.UnauthorizedError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError( + _response.error.body as unknown, + _response.rawResponse, + ); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/segments", + ); + }, + ); + const dataWithRawResponse = await list(request).withRawResponse(); + return new core.Page({ + response: dataWithRawResponse.data, + rawResponse: dataWithRawResponse.rawResponse, + hasNextPage: (response) => + response?.next != null && !(typeof response?.next === "string" && response?.next === ""), + getItems: (response) => response?.segments ?? [], + loadPage: (response) => { + return list(core.setObjectProperty(request, "from", response?.next)); + }, + }); + } + + /** + * Create a new segment with rule-based membership criteria for use in experiments. + * + * @param {Management.CreateSegmentRequestContent} request + * @param {SegmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.UnprocessableEntityError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.segments.create({ + * name: "name", + * rules: [{}] + * }) + */ + public create( + request: Management.CreateSegmentRequestContent, + requestOptions?: SegmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); + } + + private async __create( + request: Management.CreateSegmentRequestContent, + requestOptions?: SegmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + "experimentation/segments", + ), + method: "POST", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.CreateSegmentResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 422: + throw new Management.UnprocessableEntityError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/experimentation/segments"); + } + + /** + * Retrieve a single segment by its ID. + * + * @param {string} id - The ID of the segment to retrieve. + * @param {SegmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.segments.get("id") + */ + public get( + id: string, + requestOptions?: SegmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__get(id, requestOptions)); + } + + private async __get( + id: string, + requestOptions?: SegmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/segments/${core.url.encodePathParam(id)}`, + ), + method: "GET", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: _response.body as Management.GetSegmentResponseContent, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "GET", + "/experimentation/segments/{id}", + ); + } + + /** + * Delete a segment by ID. Idempotent: returns 204 even if segment does not exist. + * + * @param {string} id - The ID of the segment to delete. + * @param {SegmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.segments.delete("id") + */ + public delete(id: string, requestOptions?: SegmentsClient.RequestOptions): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__delete(id, requestOptions)); + } + + private async __delete( + id: string, + requestOptions?: SegmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/segments/${core.url.encodePathParam(id)}`, + ), + method: "DELETE", + headers: _headers, + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { data: undefined, rawResponse: _response.rawResponse }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "DELETE", + "/experimentation/segments/{id}", + ); + } + + /** + * Partially update a segment by ID. Only provided fields are updated. Sending rules replaces the entire rules array. + * + * @param {string} id - The ID of the segment to update. + * @param {Management.UpdateSegmentRequestContent} request + * @param {SegmentsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @throws {@link Management.BadRequestError} + * @throws {@link Management.UnauthorizedError} + * @throws {@link Management.ForbiddenError} + * @throws {@link Management.NotFoundError} + * @throws {@link Management.ConflictError} + * @throws {@link Management.UnprocessableEntityError} + * @throws {@link Management.TooManyRequestsError} + * + * @example + * await client.experimentation.segments.update("id") + */ + public update( + id: string, + request: Management.UpdateSegmentRequestContent = {}, + requestOptions?: SegmentsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__update(id, request, requestOptions)); + } + + private async __update( + id: string, + request: Management.UpdateSegmentRequestContent = {}, + requestOptions?: SegmentsClient.RequestOptions, + ): Promise> { + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + requestOptions?.headers, + ); + const _response = await (this._options.fetcher ?? core.fetcher)({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)) ?? + environments.ManagementEnvironment.Default, + `experimentation/segments/${core.url.encodePathParam(id)}`, + ), + method: "PATCH", + headers: _headers, + contentType: "application/json", + queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), + requestType: "json", + body: request, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as Management.UpdateSegmentResponseContent, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + switch (_response.error.statusCode) { + case 400: + throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); + case 401: + throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); + case 403: + throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); + case 404: + throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); + case 409: + throw new Management.ConflictError(_response.error.body as unknown, _response.rawResponse); + case 422: + throw new Management.UnprocessableEntityError( + _response.error.body as unknown, + _response.rawResponse, + ); + case 429: + throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); + default: + throw new errors.ManagementError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + } + + return handleNonStatusCodeError( + _response.error, + _response.rawResponse, + "PATCH", + "/experimentation/segments/{id}", + ); + } +} diff --git a/src/management/api/resources/experimentation/resources/segments/client/index.ts b/src/management/api/resources/experimentation/resources/segments/client/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/segments/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/management/api/resources/experimentation/resources/segments/exports.ts b/src/management/api/resources/experimentation/resources/segments/exports.ts new file mode 100644 index 0000000000..c07143877f --- /dev/null +++ b/src/management/api/resources/experimentation/resources/segments/exports.ts @@ -0,0 +1,4 @@ +// This file was auto-generated by Fern from our API Definition. + +export { SegmentsClient } from "./client/Client.js"; +export * from "./client/index.js"; diff --git a/src/management/api/resources/experimentation/resources/segments/index.ts b/src/management/api/resources/experimentation/resources/segments/index.ts new file mode 100644 index 0000000000..914b8c3c72 --- /dev/null +++ b/src/management/api/resources/experimentation/resources/segments/index.ts @@ -0,0 +1 @@ +export * from "./client/index.js"; diff --git a/src/management/api/resources/organizations/client/Client.ts b/src/management/api/resources/organizations/client/Client.ts index b5f6bbc97f..e775ddbfad 100644 --- a/src/management/api/resources/organizations/client/Client.ts +++ b/src/management/api/resources/organizations/client/Client.ts @@ -16,7 +16,6 @@ import { EnabledConnectionsClient } from "../resources/enabledConnections/client import { GroupsClient } from "../resources/groups/client/Client.js"; import { InvitationsClient } from "../resources/invitations/client/Client.js"; import { MembersClient } from "../resources/members/client/Client.js"; -import { OrganizationTemplateClient } from "../resources/organizationTemplate/client/Client.js"; import { RolesClient } from "../resources/roles/client/Client.js"; export declare namespace OrganizationsClient { @@ -34,7 +33,6 @@ export class OrganizationsClient { protected _enabledConnections: EnabledConnectionsClient | undefined; protected _invitations: InvitationsClient | undefined; protected _members: MembersClient | undefined; - protected _organizationTemplate: OrganizationTemplateClient | undefined; protected _groups: GroupsClient | undefined; protected _roles: RolesClient | undefined; @@ -70,10 +68,6 @@ export class OrganizationsClient { return (this._members ??= new MembersClient(this._options)); } - public get organizationTemplate(): OrganizationTemplateClient { - return (this._organizationTemplate ??= new OrganizationTemplateClient(this._options)); - } - public get groups(): GroupsClient { return (this._groups ??= new GroupsClient(this._options)); } diff --git a/src/management/api/resources/organizations/resources/index.ts b/src/management/api/resources/organizations/resources/index.ts index 7e407d3306..20d8278b80 100644 --- a/src/management/api/resources/organizations/resources/index.ts +++ b/src/management/api/resources/organizations/resources/index.ts @@ -6,5 +6,4 @@ export * as enabledConnections from "./enabledConnections/index.js"; export * as groups from "./groups/index.js"; export * as invitations from "./invitations/index.js"; export * as members from "./members/index.js"; -export * as organizationTemplate from "./organizationTemplate/index.js"; export * as roles from "./roles/index.js"; diff --git a/src/management/api/resources/organizations/resources/organizationTemplate/client/Client.ts b/src/management/api/resources/organizations/resources/organizationTemplate/client/Client.ts deleted file mode 100644 index 8637117343..0000000000 --- a/src/management/api/resources/organizations/resources/organizationTemplate/client/Client.ts +++ /dev/null @@ -1,266 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { normalizeClientOptionsWithAuth, type NormalizedClientOptionsWithAuth } from "../../../../../../BaseClient.js"; -import * as core from "../../../../../../core/index.js"; -import { mergeHeaders } from "../../../../../../core/headers.js"; -import * as environments from "../../../../../../environments.js"; -import { handleNonStatusCodeError } from "../../../../../../errors/handleNonStatusCodeError.js"; -import * as errors from "../../../../../../errors/index.js"; -import * as Management from "../../../../../index.js"; - -export declare namespace OrganizationTemplateClient { - export type Options = BaseClientOptions; - - export interface RequestOptions extends BaseRequestOptions {} -} - -export class OrganizationTemplateClient { - protected readonly _options: NormalizedClientOptionsWithAuth; - - constructor(options: OrganizationTemplateClient.Options) { - this._options = normalizeClientOptionsWithAuth(options); - } - - /** - * Retrieve the organization template assigned to a specific organization. Returns the template object if one is explicitly assigned, or a 404 if no template is assigned. - * - * @param {string} id - ID of the organization. - * @param {OrganizationTemplateClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @throws {@link Management.UnauthorizedError} - * @throws {@link Management.ForbiddenError} - * @throws {@link Management.NotFoundError} - * @throws {@link Management.TooManyRequestsError} - * - * @example - * await client.organizations.organizationTemplate.get("id") - */ - public get( - id: string, - requestOptions?: OrganizationTemplateClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__get(id, requestOptions)); - } - - private async __get( - id: string, - requestOptions?: OrganizationTemplateClient.RequestOptions, - ): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - let _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await (this._options.fetcher ?? core.fetcher)({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)) ?? - environments.ManagementEnvironment.Default, - `organizations/${core.url.encodePathParam(id)}/organization-templates`, - ), - method: "GET", - headers: _headers, - queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: _response.body as Management.OrganizationTemplate, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - switch (_response.error.statusCode) { - case 401: - throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); - case 403: - throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); - case 404: - throw new Management.NotFoundError(_response.error.body as unknown, _response.rawResponse); - case 429: - throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); - default: - throw new errors.ManagementError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - } - - return handleNonStatusCodeError( - _response.error, - _response.rawResponse, - "GET", - "/organizations/{id}/organization-templates", - ); - } - - /** - * Assign an Organization Template to an organization. - * - * @param {string} id - The ID of the organization. - * @param {string} template_id - The ID of the organization template to assign. - * @param {OrganizationTemplateClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @throws {@link Management.BadRequestError} - * @throws {@link Management.UnauthorizedError} - * @throws {@link Management.ForbiddenError} - * @throws {@link Management.TooManyRequestsError} - * - * @example - * await client.organizations.organizationTemplate.assignOrganizationTemplate("id", "template_id") - */ - public assignOrganizationTemplate( - id: string, - template_id: string, - requestOptions?: OrganizationTemplateClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise(this.__assignOrganizationTemplate(id, template_id, requestOptions)); - } - - private async __assignOrganizationTemplate( - id: string, - template_id: string, - requestOptions?: OrganizationTemplateClient.RequestOptions, - ): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - let _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await (this._options.fetcher ?? core.fetcher)({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)) ?? - environments.ManagementEnvironment.Default, - `organizations/${core.url.encodePathParam(id)}/organization-templates/${core.url.encodePathParam(template_id)}`, - ), - method: "PUT", - headers: _headers, - queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - switch (_response.error.statusCode) { - case 400: - throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); - case 401: - throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); - case 403: - throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); - case 429: - throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); - default: - throw new errors.ManagementError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - } - - return handleNonStatusCodeError( - _response.error, - _response.rawResponse, - "PUT", - "/organizations/{id}/organization-templates/{template_id}", - ); - } - - /** - * Remove an Organization Template assignment from an organization. - * - * @param {string} id - The ID of the organization. - * @param {string} template_id - The ID of the organization template to unassign. - * @param {OrganizationTemplateClient.RequestOptions} requestOptions - Request-specific configuration. - * - * @throws {@link Management.BadRequestError} - * @throws {@link Management.UnauthorizedError} - * @throws {@link Management.ForbiddenError} - * @throws {@link Management.TooManyRequestsError} - * - * @example - * await client.organizations.organizationTemplate.unassignOrganizationTemplate("id", "template_id") - */ - public unassignOrganizationTemplate( - id: string, - template_id: string, - requestOptions?: OrganizationTemplateClient.RequestOptions, - ): core.HttpResponsePromise { - return core.HttpResponsePromise.fromPromise( - this.__unassignOrganizationTemplate(id, template_id, requestOptions), - ); - } - - private async __unassignOrganizationTemplate( - id: string, - template_id: string, - requestOptions?: OrganizationTemplateClient.RequestOptions, - ): Promise> { - const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); - let _headers: core.Fetcher.Args["headers"] = mergeHeaders( - _authRequest.headers, - this._options?.headers, - requestOptions?.headers, - ); - const _response = await (this._options.fetcher ?? core.fetcher)({ - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - (await core.Supplier.get(this._options.environment)) ?? - environments.ManagementEnvironment.Default, - `organizations/${core.url.encodePathParam(id)}/organization-templates/${core.url.encodePathParam(template_id)}`, - ), - method: "DELETE", - headers: _headers, - queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), - timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, - abortSignal: requestOptions?.abortSignal, - fetchFn: this._options?.fetch, - logging: this._options.logging, - }); - if (_response.ok) { - return { data: undefined, rawResponse: _response.rawResponse }; - } - - if (_response.error.reason === "status-code") { - switch (_response.error.statusCode) { - case 400: - throw new Management.BadRequestError(_response.error.body as unknown, _response.rawResponse); - case 401: - throw new Management.UnauthorizedError(_response.error.body as unknown, _response.rawResponse); - case 403: - throw new Management.ForbiddenError(_response.error.body as unknown, _response.rawResponse); - case 429: - throw new Management.TooManyRequestsError(_response.error.body as unknown, _response.rawResponse); - default: - throw new errors.ManagementError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - rawResponse: _response.rawResponse, - }); - } - } - - return handleNonStatusCodeError( - _response.error, - _response.rawResponse, - "DELETE", - "/organizations/{id}/organization-templates/{template_id}", - ); - } -} diff --git a/src/management/api/resources/users/client/Client.ts b/src/management/api/resources/users/client/Client.ts index 4c69f0e2fa..32f324b385 100644 --- a/src/management/api/resources/users/client/Client.ts +++ b/src/management/api/resources/users/client/Client.ts @@ -119,24 +119,22 @@ export class UsersClient { } /** - * Retrieve details of users. It is possible to: + * This endpoint retrieves details of users. It's best suited to interactive, best-effort search and lookups where slightly stale results are acceptable. With it, you can: * - * - Specify a search criteria for users + * - Specify search criteria for users * - Sort the users to be returned * - Select the fields to be returned * - Specify the number of users to retrieve per page and the page index * + * This endpoint is **not suited for use in critical paths**. It is eventually consistent and runs under a short (~2 second) query time limit, so results can be stale and heavy queries can return a 503. * + * - Do not use this endpoint for authentication, account linking, or logic inside login-flow Actions. Instead, [look users up directly by ID or email](https://auth0.com/docs/manage-users/user-search/get-users-by-id-or-email#management-api) to get their current state. + * - Do not use this endpoint to keep an external system in sync with user data. Instead, subscribe to [Event Streams](https://auth0.com/docs/customize/events/sync-data-across-systems) to receive every change as it happens. + * - Do not use this endpoint to enumerate or export your entire user base. Instead, run a [bulk user export](https://auth0.com/docs/manage-users/user-migration/bulk-user-exports) to retrieve the full set. * - * The `q` query parameter can be used to get users that match the specified criteria [using query string syntax.](https://auth0.com/docs/users/search/v3/query-syntax) + * Use the `q` query parameter to match users with [query string syntax](https://auth0.com/docs/manage-users/user-search/user-search-query-syntax). For full instructions and guidance, see [How to List and Search Users](https://auth0.com/docs/manage-users/user-search/list-and-search-users). * - * [Learn more about searching for users.](https://auth0.com/docs/users/search/v3) - * - * Read about [best practices](https://auth0.com/docs/users/search/best-practices) when working with the API endpoints for retrieving users. - * - * - * - * Auth0 limits the number of users you can return. If you exceed this threshold, please redefine your search, use the [export job](https://auth0.com/docs/api/management/v2#!/Jobs/post_users_exports), or the [User Import / Export](https://auth0.com/docs/extensions/user-import-export) extension. + * For efficient queries, prefer indexed top-level fields and exact matches. Certain kinds of queries can be slow and may time out, such as filtering on freeform or multi-value fields (like user-defined attributes in `app_metadata` or `user_metadata`) or using leading wildcards. * * @param {Management.ListUsersRequestParameters} request * @param {UsersClient.RequestOptions} requestOptions - Request-specific configuration. diff --git a/src/management/api/types/types.ts b/src/management/api/types/types.ts index 99ac7edcbc..b7c82acdc0 100644 --- a/src/management/api/types/types.ts +++ b/src/management/api/types/types.ts @@ -255,9 +255,18 @@ export const OauthScope = { /** * Read Events */ ReadEvents: "read:events", + /** + * Create Experimentation */ + CreateExperimentation: "create:experimentation", + /** + * Read Experimentation */ + ReadExperimentation: "read:experimentation", /** * Update Experimentation */ UpdateExperimentation: "update:experimentation", + /** + * Delete Experimentation */ + DeleteExperimentation: "delete:experimentation", /** * Create Flows */ CreateFlows: "create:flows", @@ -738,9 +747,6 @@ export const OauthScope = { /** * Delete Organization Client Associations */ DeleteOrganizationClients: "delete:organization_clients", - /** - * Read Organization Templates */ - ReadOrganizationTemplates: "read:organization_templates", /** * Create Network ACL Keys */ CreateNetworkAclKeys: "create:network_acl_keys", @@ -1391,6 +1397,40 @@ export interface AgentResponseContent { metadata: Management.AgentMetadata; } +export interface AllocationItem { + variation_id?: string | undefined; + variation_name?: string | undefined; + segment_id?: string | undefined; + segment_name?: string | undefined; + weight?: number | undefined; + priority?: number | undefined; + is_control?: boolean | undefined; + is_fallback?: boolean | undefined; + variation_snapshot?: (Record | null) | undefined; + segment_snapshot?: (Record | null) | undefined; +} + +export interface AllocationRequestItem { + /** The ID of the variation to allocate */ + variation_id: string; + /** Percentage weight for this allocation (percentage strategy only) */ + weight?: number | undefined; + /** The segment this allocation targets (segment strategy only) */ + segment_id?: string | undefined; + /** Evaluation order; 1 = highest priority (segment strategy only) */ + priority?: number | undefined; + /** Whether this allocation is the control group */ + is_control: boolean; + /** Whether this allocation is the default fallback (segment strategy only) */ + is_fallback?: boolean | undefined; +} + +export const AllocationStrategyEnum = { + Percentage: "percentage", + Segment: "segment", +} as const; +export type AllocationStrategyEnum = (typeof AllocationStrategyEnum)[keyof typeof AllocationStrategyEnum]; + /** * IP address to check. */ @@ -1432,6 +1472,7 @@ export interface AssociateOrganizationClientGrantResponseContent { export const AsyncApprovalNotificationsChannelsEnum = { GuardianPush: "guardian-push", Email: "email", + MyAccount: "my-account", } as const; export type AsyncApprovalNotificationsChannelsEnum = (typeof AsyncApprovalNotificationsChannelsEnum)[keyof typeof AsyncApprovalNotificationsChannelsEnum]; @@ -1549,6 +1590,17 @@ export interface AttackProtectionUpdateCaptchaRecaptchaV2 { secret: string; } +/** Specifies the target authentication flow for this experiment. Must be one of: authentication, mfa_enrollment, mfa_challenge, password_reset, passkey_enrollment, or all. Note that the all value targets every flow at once, but requires that this is the only active experiment. */ +export const AuthenticationFlowEnum = { + Authentication: "authentication", + MfaEnrollment: "mfa_enrollment", + MfaChallenge: "mfa_challenge", + PasswordReset: "password_reset", + PasskeyEnrollment: "passkey_enrollment", + All: "all", +} as const; +export type AuthenticationFlowEnum = (typeof AuthenticationFlowEnum)[keyof typeof AuthenticationFlowEnum]; + export const AuthenticationMethodTypeEnum = { RecoveryCode: "recovery-code", Totp: "totp", @@ -2715,14 +2767,14 @@ export const ClientAppTypeEnum = { export type ClientAppTypeEnum = (typeof ClientAppTypeEnum)[keyof typeof ClientAppTypeEnum]; /** - * Array of notification channels for contacting the user when their approval is required. Valid values are `guardian-push`, `email`. + * Array of notification channels for contacting the user when their approval is required. Valid values are `guardian-push`, `email`, `my-account`. */ export type ClientAsyncApprovalNotificationsChannelsApiPatchConfiguration = | (Management.AsyncApprovalNotificationsChannelsEnum[] | null) | undefined; /** - * Array of notification channels for contacting the user when their approval is required. Valid values are `guardian-push`, `email`. + * Array of notification channels for contacting the user when their approval is required. Valid values are `guardian-push`, `email`, `my-account`. */ export type ClientAsyncApprovalNotificationsChannelsApiPostConfiguration = Management.AsyncApprovalNotificationsChannelsEnum[]; @@ -3144,6 +3196,7 @@ export const ClientOidcBackchannelLogoutInitiatorsEnum = { EmailIdentifierChanged: "email-identifier-changed", MfaPhoneUnenrolled: "mfa-phone-unenrolled", AccountDeactivated: "account-deactivated", + ProfileChanged: "profile-changed", } as const; export type ClientOidcBackchannelLogoutInitiatorsEnum = (typeof ClientOidcBackchannelLogoutInitiatorsEnum)[keyof typeof ClientOidcBackchannelLogoutInitiatorsEnum]; @@ -3178,6 +3231,24 @@ export interface ClientOidcBackchannelLogoutSettings { [key: string]: any; } +export const ClientOidcSupportAllowedScopesEnum = { + Profile: "profile", + Email: "email", + Address: "address", + Phone: "phone", +} as const; +export type ClientOidcSupportAllowedScopesEnum = + (typeof ClientOidcSupportAllowedScopesEnum)[keyof typeof ClientOidcSupportAllowedScopesEnum]; + +/** + * OIDC support configuration for a client. Controls whether OIDC flows are allowed and which scopes the client may request. + */ +export interface ClientOidcSupportPost { + is_allowed: boolean; + allow_all_scopes?: boolean | undefined; + allowed_scopes?: Management.ClientOidcSupportAllowedScopesEnum[] | undefined; +} + /** Method for discovering organizations during the `pre_login_prompt`. `email` allows users to find their organization by entering their email address and performing domain matching, while `organization_name` requires users to enter the organization name directly. These methods can be combined. */ export const ClientOrganizationDiscoveryEnum = { Email: "email", @@ -3929,14 +4000,6 @@ export type ConnectionDecryptionKeySaml = * Private key in PEM format. */ | string; -/** Controls whether connections created from this template can be deleted. */ -export const ConnectionDeletionBehaviorEnum = { - Allow: "allow", - AllowIfEmpty: "allow_if_empty", -} as const; -export type ConnectionDeletionBehaviorEnum = - (typeof ConnectionDeletionBehaviorEnum)[keyof typeof ConnectionDeletionBehaviorEnum]; - /** * The URL where Auth0 will send SAML authentication requests (the Identity Provider's SSO URL). Must be a valid HTTPS URL. */ @@ -4674,6 +4737,7 @@ export interface ConnectionOptionsAd extends Management.ConnectionOptionsCommon signInEndpoint?: Management.ConnectionSignInEndpointAd | undefined; tenant_domain?: Management.ConnectionTenantDomainAd | undefined; thumbprints?: Management.ConnectionThumbprintsAd | undefined; + thumbprints_sha384?: Management.ConnectionThumbprintsSha384Ad | undefined; upstream_params?: ((Management.ConnectionUpstreamParams | undefined) | null) | undefined; /** Accepts any additional properties */ [key: string]: any; @@ -4696,6 +4760,7 @@ export interface ConnectionOptionsAdfs extends Management.ConnectionOptionsCommo signInEndpoint?: Management.ConnectionSignInEndpointAdfs | undefined; tenant_domain?: Management.ConnectionTenantDomain | undefined; thumbprints?: Management.ConnectionThumbprints | undefined; + thumbprints_sha384?: Management.ConnectionThumbprintsSha384 | undefined; upstream_params?: ((Management.ConnectionUpstreamParams | undefined) | null) | undefined; /** Custom ADFS claim to use as the unique user identifier. When provided, this attribute is prepended to the default user_id mapping list with highest priority. Accepts a string (single ADFS claim name). */ user_id_attribute?: string | undefined; @@ -4894,6 +4959,7 @@ export interface ConnectionOptionsAzureAd extends Management.ConnectionOptionsCo tenant_domain?: Management.ConnectionTenantDomainAzureAdOne | undefined; tenantId?: Management.ConnectionTenantIdAzureAd | undefined; thumbprints?: Management.ConnectionThumbprints | undefined; + thumbprints_sha384?: Management.ConnectionThumbprintsSha384 | undefined; upstream_params?: ((Management.ConnectionUpstreamParams | undefined) | null) | undefined; /** Indicates WS-Federation protocol usage. When true, uses WS-Federation; when false, uses OpenID Connect. */ use_wsfed?: boolean | undefined; @@ -5021,6 +5087,7 @@ export interface ConnectionOptionsCommonSaml { signatureAlgorithm?: Management.ConnectionSignatureAlgorithmSaml | undefined; tenant_domain?: Management.ConnectionTenantDomainSaml | undefined; thumbprints?: Management.ConnectionThumbprintsSaml | undefined; + thumbprints_sha384?: Management.ConnectionThumbprintsSha384Saml | undefined; upstream_params?: ((Management.ConnectionUpstreamParams | undefined) | null) | undefined; } @@ -6465,6 +6532,8 @@ export interface ConnectionPropertiesOptions { useOauthSpecScope?: Management.ConnectionUseOauthSpecScope | undefined; discovery_url?: ((Management.ConnectionsDiscoveryUrl | undefined) | null) | undefined; oidc_metadata?: (Management.ConnectionsOidcMetadata | null) | undefined; + thumbprints?: Management.ConnectionThumbprints | undefined; + thumbprints_sha384?: Management.ConnectionThumbprintsSha384 | undefined; /** Accepts any additional properties */ [key: string]: any; } @@ -7840,7 +7909,7 @@ export type ConnectionTenantDomainSaml = string; export type ConnectionTenantIdAzureAd = string; /** - * Array of certificate thumbprints (SHA-128/SHA-256/SHA-512 hex hashes) for validating SAML signatures. Used with WS-Federation protocol. Maximum 20 thumbprints. Each thumbprint must be a hexadecimal string. + * Array of certificate thumbprints (SHA-128 hex hashes) for validating SAML signatures. Used with WS-Federation protocol. Maximum 20 thumbprints. Each thumbprint must be a hexadecimal string. */ export type ConnectionThumbprints = string[]; @@ -7854,6 +7923,21 @@ export type ConnectionThumbprintsAd = Management.ConnectionSha1Thumbprint[]; */ export type ConnectionThumbprintsSaml = Management.ConnectionSha1Thumbprint[]; +/** + * Array of certificate thumbprints (SHA-384 hex hashes) for validating SAML signatures. Used with WS-Federation protocol. Maximum 20 thumbprints. Each thumbprint must be a 96-character hexadecimal string. + */ +export type ConnectionThumbprintsSha384 = string[]; + +/** + * Array of certificate SHA-384 thumbprints for validating signatures. Managed by Auth0 when using the AD Connector agent. + */ +export type ConnectionThumbprintsSha384Ad = string[]; + +/** + * SHA-384 thumbprints (fingerprints) of the identity provider's signing certificates. Automatically computed from signingCert during connection creation. Each thumbprint must be a 96-character hexadecimal string. + */ +export type ConnectionThumbprintsSha384Saml = string[]; + /** * URL of the identity provider's OAuth 2.0 token endpoint where authorization codes are exchanged for access tokens. Must be a valid HTTPS URL. Required for authorization code flow but optional for implicit flow. */ @@ -9542,6 +9626,31 @@ export interface CreateEventStreamWebHookRequestContent { status?: Management.EventStreamStatusEnum | undefined; } +export interface CreateExperimentResponseContent { + id: string; + name: string; + description?: string | undefined; + feature_flag_id: string; + feature_flag_name?: string | undefined; + authentication_flow: string; + allocation_strategy: Management.AllocationStrategyEnum; + status: Management.ExperimentStatusEnum; + is_valid: boolean; + default_config?: Management.DefaultConfigEnum | undefined; + feature_flag_snapshot?: (Record | null) | undefined; + allocations: Management.AllocationItem[]; + /** Fields that may be mutated given the experiment's current status. Computed at response time; always current with the API's enforcement logic. */ + editable_fields: string[]; + /** Ramp experiment levels configuration. */ + levels?: number[] | undefined; + /** Read-only. The active exposure percentage for the current ramp step. Null when no ramp schedule is active. */ + current_level?: (number | null) | undefined; + started_at?: string | undefined; + ended_at?: string | undefined; + created_at: string; + updated_at: string; +} + export interface CreateExportUsersFields { /** Name of the field in the profile. */ name: string; @@ -9569,6 +9678,22 @@ export interface CreateExportUsersResponseContent { [key: string]: any; } +/** + * Configuration parameters for this feature flag + */ +export type CreateFeatureFlagParameters = Record; + +export interface CreateFeatureFlagResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.FeatureFlagTypeEnum; + status: Management.FeatureFlagStatusEnum; + parameters?: Management.FeatureFlagConfigParams | undefined; + created_at: string; + updated_at: string; +} + export interface CreateFlowResponseContent { id: string; name: string; @@ -10423,6 +10548,8 @@ export interface CreateResourceServerResponseContent { access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean | undefined; authorization_details?: (unknown[] | null) | undefined; proof_of_possession?: (Management.ResourceServerProofOfPossession | null) | undefined; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization | undefined; @@ -10497,6 +10624,16 @@ export interface CreateScimTokenResponseContent { valid_until?: string | undefined; } +export interface CreateSegmentResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.SegmentTypeEnum; + rules: Management.SegmentRule[]; + created_at: string; + updated_at: string; +} + export interface CreateSelfServiceProfileResponseContent { /** The unique ID of the self-service Profile. */ id?: string | undefined; @@ -10641,6 +10778,16 @@ export interface CreateUserResponseContent { [key: string]: any; } +export interface CreateVariationResponseContent { + id: string; + feature_flag_id: string; + name: string; + description?: string | undefined; + overrides: Management.VariationOverridesMap; + created_at: string; + updated_at: string; +} + export interface CreateVerifiableCredentialTemplateResponseContent { /** The id of the template. */ id?: string | undefined; @@ -10991,6 +11138,13 @@ export interface DailyStats { [key: string]: any; } +/** For Auth0-managed flags, where non-overridden config keys resolve from: 'tenant' inherits the tenant's live config, 'flag' uses the flag's frozen defaults. Omitted when unset (defaults to 'tenant' at resolution). Not applicable to customer-defined flags. */ +export const DefaultConfigEnum = { + Tenant: "tenant", + Flag: "flag", +} as const; +export type DefaultConfigEnum = (typeof DefaultConfigEnum)[keyof typeof DefaultConfigEnum]; + /** Default authentication method for email identifier */ export const DefaultMethodEmailIdentifierEnum = { Password: "password", @@ -28039,6 +28193,58 @@ export interface EventStreamWebhookResponseContent { updated_at?: string | undefined; } +export interface ExperimentListItem { + id: string; + name: string; + description?: string | undefined; + feature_flag_id: string; + feature_flag_name?: string | undefined; + authentication_flow: string; + allocation_strategy: Management.AllocationStrategyEnum; + status: Management.ExperimentStatusEnum; + is_valid: boolean; + default_config?: Management.DefaultConfigEnum | undefined; + feature_flag_snapshot?: (Record | null) | undefined; + allocations: Management.AllocationItem[]; + /** Fields that may be mutated given the experiment's current status. Computed at response time; always current with the API's enforcement logic. */ + editable_fields: string[]; + /** Ramp experiment levels configuration. */ + levels?: number[] | undefined; + /** Read-only. The active exposure percentage for the current ramp step. Null when no ramp schedule is active. */ + current_level?: (number | null) | undefined; + started_at?: string | undefined; + ended_at?: string | undefined; + created_at: string; + updated_at: string; +} + +/** Filter by status. Exact match. */ +export const ExperimentStatusEnum = { + Draft: "draft", + Active: "active", + Paused: "paused", + Completed: "completed", + Archived: "archived", +} as const; +export type ExperimentStatusEnum = (typeof ExperimentStatusEnum)[keyof typeof ExperimentStatusEnum]; + +/** The target status to transition the experiment to. */ +export const ExperimentTransitionStatusEnum = { + Active: "active", + Paused: "paused", + Completed: "completed", + Archived: "archived", +} as const; +export type ExperimentTransitionStatusEnum = + (typeof ExperimentTransitionStatusEnum)[keyof typeof ExperimentTransitionStatusEnum]; + +export interface ExperimentValidationError { + /** Machine-readable error code identifying the validation failure. */ + code: string; + /** Human-readable description of the validation failure. */ + message: string; +} + /** * Application specific configuration for use with the OIN Express Configuration feature. */ @@ -28089,6 +28295,55 @@ export interface ExpressConfigurationOrNull { export interface ExtensibilityEmailProviderCredentials {} +export interface FeatureFlag { + id: string; + name: string; + description?: string | undefined; + type: Management.FeatureFlagTypeEnum; + status: Management.FeatureFlagStatusEnum; + parameters?: Management.FeatureFlagConfigParams | undefined; + created_at: string; + updated_at: string; +} + +export interface FeatureFlagConfigParam { + type: Management.FeatureFlagConfigParamTypeEnum; + value?: unknown | undefined; + /** A human-readable description of the parameter */ + description?: string | undefined; +} + +/** The data type of the parameter value */ +export const FeatureFlagConfigParamTypeEnum = { + String: "string", + Boolean: "boolean", + Number: "number", + Array: "array", + Object: "object", +} as const; +export type FeatureFlagConfigParamTypeEnum = + (typeof FeatureFlagConfigParamTypeEnum)[keyof typeof FeatureFlagConfigParamTypeEnum]; + +/** + * Configuration parameters for this feature flag + */ +export type FeatureFlagConfigParams = Record; + +/** Filter by status. Exact match. */ +export const FeatureFlagStatusEnum = { + Draft: "draft", + Active: "active", + Archived: "archived", +} as const; +export type FeatureFlagStatusEnum = (typeof FeatureFlagStatusEnum)[keyof typeof FeatureFlagStatusEnum]; + +/** Filter by type. Exact match. */ +export const FeatureFlagTypeEnum = { + Auth0: "auth0", + Self: "self", +} as const; +export type FeatureFlagTypeEnum = (typeof FeatureFlagTypeEnum)[keyof typeof FeatureFlagTypeEnum]; + /** * Configure FedCM login settings for New Universal Login */ @@ -32239,6 +32494,42 @@ export type GetEventStreamResponseContent = | Management.EventStreamEventBridgeResponseContent | Management.EventStreamActionResponseContent; +export interface GetExperimentResponseContent { + id: string; + name: string; + description?: string | undefined; + feature_flag_id: string; + feature_flag_name?: string | undefined; + authentication_flow: string; + allocation_strategy: Management.AllocationStrategyEnum; + status: Management.ExperimentStatusEnum; + is_valid: boolean; + default_config?: Management.DefaultConfigEnum | undefined; + feature_flag_snapshot?: (Record | null) | undefined; + allocations: Management.AllocationItem[]; + /** Fields that may be mutated given the experiment's current status. Computed at response time; always current with the API's enforcement logic. */ + editable_fields: string[]; + /** Ramp experiment levels configuration. */ + levels?: number[] | undefined; + /** Read-only. The active exposure percentage for the current ramp step. Null when no ramp schedule is active. */ + current_level?: (number | null) | undefined; + started_at?: string | undefined; + ended_at?: string | undefined; + created_at: string; + updated_at: string; +} + +export interface GetFeatureFlagResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.FeatureFlagTypeEnum; + status: Management.FeatureFlagStatusEnum; + parameters?: Management.FeatureFlagConfigParams | undefined; + created_at: string; + updated_at: string; +} + export const GetFlowExecutionRequestParametersHydrateEnum = { Debug: "debug", } as const; @@ -32845,6 +33136,8 @@ export interface GetResourceServerResponseContent { access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean | undefined; authorization_details?: (unknown[] | null) | undefined; proof_of_possession?: (Management.ResourceServerProofOfPossession | null) | undefined; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization | undefined; @@ -32919,6 +33212,16 @@ export interface GetScimConfigurationResponseContent { */ export type GetScimTokensResponseContent = Management.ScimTokenItem[]; +export interface GetSegmentResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.SegmentTypeEnum; + rules: Management.SegmentRule[]; + created_at: string; + updated_at: string; +} + export interface GetSelfServiceProfileResponseContent { /** The unique ID of the self-service Profile. */ id?: string | undefined; @@ -33236,6 +33539,16 @@ export interface GetUserResponseContent { [key: string]: any; } +export interface GetVariationResponseContent { + id: string; + feature_flag_id: string; + name: string; + description?: string | undefined; + overrides: Management.VariationOverridesMap; + created_at: string; + updated_at: string; +} + export interface GetVerifiableCredentialTemplateResponseContent { /** The id of the template. */ id?: string | undefined; @@ -33834,6 +34147,18 @@ export interface ListEventStreamsResponseContent { [key: string]: any; } +export interface ListExperimentsResponseContent { + experiments: Management.ExperimentListItem[]; + /** Checkpoint token for the next page. Omitted when there are no further results. */ + next?: string | undefined; +} + +export interface ListFeatureFlagsResponseContent { + feature_flags: Management.FeatureFlag[]; + /** Checkpoint token for the next page. Omitted when there are no further results. */ + next?: string | undefined; +} + export interface ListFlowExecutionsPaginatedResponseContent { /** Opaque identifier for use with the from query parameter for the next page of results.
This identifier is valid for 24 hours. */ next?: string | undefined; @@ -34068,6 +34393,12 @@ export interface ListScimConfigurationsResponseContent { next?: string | undefined; } +export interface ListSegmentsResponseContent { + segments: Management.Segment[]; + /** Checkpoint token for the next page. Omitted when there are no further results. */ + next?: string | undefined; +} + /** * The list of custom text keys and values. */ @@ -34200,6 +34531,10 @@ export interface ListUsersOffsetPaginatedResponseContent { users?: Management.UserResponseSchema[] | undefined; } +export interface ListVariationsResponseContent { + variations: Management.Variation[]; +} + export interface ListVerifiableCredentialTemplatesPaginatedResponseContent { /** Opaque identifier for use with the from query parameter for the next page of results.
This identifier is valid for 24 hours. */ next?: (string | null) | undefined; @@ -35183,14 +35518,6 @@ export interface OrganizationConnectionInformation { [key: string]: any; } -/** Controls whether organizations using this template can be deleted. */ -export const OrganizationDeletionBehaviorEnum = { - Allow: "allow", - AllowIfEmpty: "allow_if_empty", -} as const; -export type OrganizationDeletionBehaviorEnum = - (typeof OrganizationDeletionBehaviorEnum)[keyof typeof OrganizationDeletionBehaviorEnum]; - export interface OrganizationDiscoveryDomain { /** Organization discovery domain identifier. */ id: string; @@ -35332,87 +35659,6 @@ export const OrganizationSortFieldEnum = { } as const; export type OrganizationSortFieldEnum = (typeof OrganizationSortFieldEnum)[keyof typeof OrganizationSortFieldEnum]; -export interface OrganizationTemplate { - /** Organization Template identifier. */ - id?: string | undefined; - /** The name of the organization template. */ - name?: string | undefined; - /** Whether this is the default template applied to new organizations. */ - is_default?: boolean | undefined; - organization_deletion_behavior?: Management.OrganizationDeletionBehaviorEnum | undefined; - connection_deletion_behavior?: Management.ConnectionDeletionBehaviorEnum | undefined; - /** Whether to enforce permission ceiling for organizations using this template. */ - enforce_permission_ceiling?: boolean | undefined; - /** Whether to enforce self-assignment restrictions for organizations using this template. */ - enforce_self_assignment_restriction?: boolean | undefined; - /** The connection profile to apply to new connections. */ - connection_profile_id?: string | undefined; - /** The user attribute profile to apply to organizations. */ - user_attribute_profile_id?: string | undefined; - /** List of allowed connection strategies for this template. */ - allowed_strategies?: Management.OrganizationTemplateAllowedStrategyEnum[] | undefined; - /** The client ID for the invitation landing page. */ - invitation_landing_client_id?: string | undefined; - /** Default admin roles to assign to organization creators. */ - admin_roles_assignment?: string[] | undefined; - use_for_organization_discovery?: (Management.OrganizationTemplateUseForOrganizationDiscovery | null) | undefined; - role_visibility_policy?: (Management.OrganizationTemplateRoleVisibilityPolicy | null) | undefined; - /** The ISO 8601 formatted timestamp representing when the template was created. */ - created_at?: string | undefined; - /** The ISO 8601 formatted timestamp representing when the template was last updated. */ - updated_at?: string | undefined; -} - -/** An allowed enterprise connection strategy. */ -export const OrganizationTemplateAllowedStrategyEnum = { - Adfs: "adfs", - GoogleApps: "google-apps", - Oidc: "oidc", - Okta: "okta", - Pingfederate: "pingfederate", - Samlp: "samlp", - Waad: "waad", -} as const; -export type OrganizationTemplateAllowedStrategyEnum = - (typeof OrganizationTemplateAllowedStrategyEnum)[keyof typeof OrganizationTemplateAllowedStrategyEnum]; - -/** The role visibility level. */ -export const OrganizationTemplateRoleVisibilityEnum = { - Write: "write", - ReadOnly: "read_only", - Hidden: "hidden", -} as const; -export type OrganizationTemplateRoleVisibilityEnum = - (typeof OrganizationTemplateRoleVisibilityEnum)[keyof typeof OrganizationTemplateRoleVisibilityEnum]; - -/** - * A role visibility override. - */ -export interface OrganizationTemplateRoleVisibilityOverride { - /** The role identifier. */ - role_id: string; - access: Management.OrganizationTemplateRoleVisibilityEnum; -} - -/** - * Controls role visibility for organization administrators. - */ -export interface OrganizationTemplateRoleVisibilityPolicy { - default_value: Management.OrganizationTemplateRoleVisibilityEnum; - /** Role-specific visibility overrides. */ - overrides?: Management.OrganizationTemplateRoleVisibilityOverride[] | undefined; -} - -/** - * Controls whether connections from this template are used for organization discovery. - */ -export interface OrganizationTemplateUseForOrganizationDiscovery { - /** The default value for organization discovery. */ - default_value: boolean; - /** The allowed values for organization discovery. */ - allowed_values?: boolean[] | undefined; -} - /** Controls whether this organization can be used in user flows with third-party clients. Defaults to `block`. */ export const OrganizationThirdPartyClientAccessEnum = { Block: "block", @@ -36116,6 +36362,8 @@ export interface ResourceServer { access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean | undefined; authorization_details?: (unknown[] | null) | undefined; proof_of_possession?: (Management.ResourceServerProofOfPossession | null) | undefined; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization | undefined; @@ -36230,6 +36478,8 @@ export interface ResourceServerSearchResponse { access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean | undefined; authorization_details?: (unknown[] | null) | undefined; proof_of_possession?: (Management.ResourceServerProofOfPossession | null) | undefined; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization | undefined; @@ -36830,6 +37080,95 @@ export interface SearchResourceServersResponseContent { next?: string | undefined; } +export interface Segment { + id: string; + name: string; + description?: string | undefined; + type: Management.SegmentTypeEnum; + rules: Management.SegmentRule[]; + created_at: string; + updated_at: string; +} + +export interface SegmentContainsExpression { + contains: string[]; +} + +export interface SegmentEndsWithExpression { + ends_with: string[]; +} + +export interface SegmentExistsExpression { + exists: boolean; +} + +/** + * Attribute conditions that must match. + */ +export interface SegmentMatchConditions { + client_id?: Management.SegmentMatchExpression | undefined; + connection?: Management.SegmentMatchExpression | undefined; + connection_type?: Management.SegmentMatchExpression | undefined; + organization_id?: Management.SegmentMatchExpression | undefined; + domain?: Management.SegmentMatchExpression | undefined; + device_type?: Management.SegmentMatchExpression | undefined; + browser?: Management.SegmentMatchExpression | undefined; + platform?: Management.SegmentMatchExpression | undefined; + user_agent?: Management.SegmentMatchExpression | undefined; + country?: Management.SegmentMatchExpression | undefined; + region?: Management.SegmentMatchExpression | undefined; + /** Accepts any additional properties */ + [key: string]: any; +} + +export type SegmentMatchExpression = + | string[] + | Management.SegmentContainsExpression + | Management.SegmentStartsWithExpression + | Management.SegmentEndsWithExpression + | Management.SegmentExistsExpression; + +/** + * Attribute conditions that must not match. + */ +export interface SegmentNotMatchConditions { + client_id?: Management.SegmentMatchExpression | undefined; + connection?: Management.SegmentMatchExpression | undefined; + connection_type?: Management.SegmentMatchExpression | undefined; + organization_id?: Management.SegmentMatchExpression | undefined; + domain?: Management.SegmentMatchExpression | undefined; + device_type?: Management.SegmentMatchExpression | undefined; + browser?: Management.SegmentMatchExpression | undefined; + platform?: Management.SegmentMatchExpression | undefined; + user_agent?: Management.SegmentMatchExpression | undefined; + country?: Management.SegmentMatchExpression | undefined; + region?: Management.SegmentMatchExpression | undefined; + /** Accepts any additional properties */ + [key: string]: any; +} + +export interface SegmentRule { + match?: Management.SegmentMatchConditions | undefined; + not_match?: Management.SegmentNotMatchConditions | undefined; +} + +export interface SegmentStartsWithExpression { + starts_with: string[]; +} + +export const SegmentTypeEnum = { + Self: "self", + Auth0: "auth0", +} as const; +export type SegmentTypeEnum = (typeof SegmentTypeEnum)[keyof typeof SegmentTypeEnum]; + +/** Filter by type. Exact match. */ +export const SegmentTypeFilterEnum = { + Auth0: "auth0", + Self: "self", +} as const; +export type SegmentTypeFilterEnum = (typeof SegmentTypeFilterEnum)[keyof typeof SegmentTypeFilterEnum]; + export interface SelfServiceProfile { /** The unique ID of the self-service Profile. */ id?: string | undefined; @@ -38465,6 +38804,8 @@ export interface UpdateConnectionOptions { useOauthSpecScope?: Management.ConnectionUseOauthSpecScope | undefined; discovery_url?: ((Management.ConnectionsDiscoveryUrl | undefined) | null) | undefined; oidc_metadata?: (Management.ConnectionsOidcMetadata | null) | undefined; + thumbprints?: Management.ConnectionThumbprints | undefined; + thumbprints_sha384?: Management.ConnectionThumbprintsSha384 | undefined; /** Accepts any additional properties */ [key: string]: any; } @@ -39052,6 +39393,83 @@ export type UpdateEventStreamResponseContent = | Management.EventStreamEventBridgeResponseContent | Management.EventStreamActionResponseContent; +export interface UpdateExperimentResponseContent { + id: string; + name: string; + description?: string | undefined; + feature_flag_id: string; + feature_flag_name?: string | undefined; + authentication_flow: string; + allocation_strategy: Management.AllocationStrategyEnum; + status: Management.ExperimentStatusEnum; + is_valid: boolean; + default_config?: Management.DefaultConfigEnum | undefined; + feature_flag_snapshot?: (Record | null) | undefined; + allocations: Management.AllocationItem[]; + /** Fields that may be mutated given the experiment's current status. Computed at response time; always current with the API's enforcement logic. */ + editable_fields: string[]; + /** Ramp experiment levels configuration. */ + levels?: number[] | undefined; + /** Read-only. The active exposure percentage for the current ramp step. Null when no ramp schedule is active. */ + current_level?: (number | null) | undefined; + started_at?: string | undefined; + ended_at?: string | undefined; + created_at: string; + updated_at: string; +} + +export interface UpdateExperimentStatusResponseContent { + id: string; + name: string; + description?: string | undefined; + feature_flag_id: string; + feature_flag_name?: string | undefined; + authentication_flow: string; + allocation_strategy: Management.AllocationStrategyEnum; + status: Management.ExperimentStatusEnum; + is_valid: boolean; + default_config?: Management.DefaultConfigEnum | undefined; + feature_flag_snapshot?: (Record | null) | undefined; + allocations: Management.AllocationItem[]; + /** Fields that may be mutated given the experiment's current status. Computed at response time; always current with the API's enforcement logic. */ + editable_fields: string[]; + /** Ramp experiment levels configuration. */ + levels?: number[] | undefined; + /** Read-only. The active exposure percentage for the current ramp step. Null when no ramp schedule is active. */ + current_level?: (number | null) | undefined; + started_at?: string | undefined; + ended_at?: string | undefined; + created_at: string; + updated_at: string; +} + +/** + * Configuration parameters for this feature flag + */ +export type UpdateFeatureFlagParameters = Record; + +export interface UpdateFeatureFlagResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.FeatureFlagTypeEnum; + status: Management.FeatureFlagStatusEnum; + parameters?: Management.FeatureFlagConfigParams | undefined; + created_at: string; + updated_at: string; +} + +export interface UpdateFeatureFlagStatusResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.FeatureFlagTypeEnum; + status: Management.FeatureFlagStatusEnum; + parameters?: Management.FeatureFlagConfigParams | undefined; + created_at: string; + updated_at: string; +} + export interface UpdateFlowResponseContent { id: string; name: string; @@ -39345,6 +39763,8 @@ export interface UpdateResourceServerResponseContent { access_token?: (Management.ResourceServerAccessToken | null) | undefined; token_encryption?: (Management.ResourceServerTokenEncryption | null) | undefined; consent_policy?: (Management.ResourceServerConsentPolicyEnum | null) | undefined; + /** When true, the resource server requires every consent approval to be digitally signed, so the approver cannot later deny a consent they granted. When false, consent decisions do not need a signature. Defaults to false. A configured value is still returned even after the related entitlement is disabled. */ + require_consent_non_repudiation?: boolean | undefined; authorization_details?: (unknown[] | null) | undefined; proof_of_possession?: (Management.ResourceServerProofOfPossession | null) | undefined; subject_type_authorization?: Management.ResourceServerSubjectTypeAuthorization | undefined; @@ -39409,6 +39829,16 @@ export interface UpdateScimConfigurationResponseContent { updated_on: string; } +export interface UpdateSegmentResponseContent { + id: string; + name: string; + description?: string | undefined; + type: Management.SegmentTypeEnum; + rules: Management.SegmentRule[]; + created_at: string; + updated_at: string; +} + export interface UpdateSelfServiceProfileResponseContent { /** The unique ID of the self-service Profile. */ id?: string | undefined; @@ -39646,6 +40076,21 @@ export interface UpdateUserResponseContent { [key: string]: any; } +/** + * Configuration overrides for this variation; keys must exist in the parent flag parameters. Empty {} is the baseline (control) variation that overrides nothing. + */ +export type UpdateVariationOverridesMap = Record; + +export interface UpdateVariationResponseContent { + id: string; + feature_flag_id: string; + name: string; + description?: string | undefined; + overrides: Management.VariationOverridesMap; + created_at: string; + updated_at: string; +} + export interface UpdateVerifiableCredentialTemplateResponseContent { /** The id of the template. */ id?: string | undefined; @@ -40249,6 +40694,28 @@ export interface UsersEnrollment { [key: string]: any; } +export interface ValidateExperimentResponseContent { + /** Whether the experiment is ready to be activated. */ + is_valid: boolean; + /** List of validation errors preventing activation. Empty when is_valid is true. */ + errors: Management.ExperimentValidationError[]; +} + +export interface Variation { + id: string; + feature_flag_id: string; + name: string; + description?: string | undefined; + overrides: Management.VariationOverridesMap; + created_at: string; + updated_at: string; +} + +/** + * Configuration overrides for this variation + */ +export type VariationOverridesMap = Record; + export interface VerifiableCredentialTemplateResponse { /** The id of the template. */ id?: string | undefined; diff --git a/src/management/tests/wire/experimentation/experiments.test.ts b/src/management/tests/wire/experimentation/experiments.test.ts index 7b61a5ab71..e019d200db 100644 --- a/src/management/tests/wire/experimentation/experiments.test.ts +++ b/src/management/tests/wire/experimentation/experiments.test.ts @@ -5,6 +5,778 @@ import { ManagementClient } from "../../../Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; describe("ExperimentsClient", () => { + test("list (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + experiments: [ + { + id: "id", + name: "name", + description: "description", + feature_flag_id: "feature_flag_id", + feature_flag_name: "feature_flag_name", + authentication_flow: "authentication_flow", + allocation_strategy: "percentage", + status: "draft", + is_valid: true, + default_config: "tenant", + feature_flag_snapshot: { key: "value" }, + allocations: [{}], + editable_fields: ["editable_fields"], + levels: [1], + current_level: 1, + started_at: "2024-01-15T09:30:00Z", + ended_at: "2024-01-15T09:30:00Z", + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }, + ], + next: "next", + }; + + server + .mockEndpoint({ once: false }) + .get("/experimentation/experiments") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const expected = rawResponseBody; + const page = await client.experimentation.experiments.list({ + from: "from", + take: 1, + status: "draft", + authentication_flow: "authentication_flow", + feature_flag_id: "feature_flag_id", + }); + + expect(expected.experiments).toEqual(page.data); + expect(page.hasNextPage()).toBe(true); + const nextPage = await page.getNextPage(); + expect(expected.experiments).toEqual(nextPage.data); + }); + + test("list (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.list(); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("list (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.list(); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("list (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.list(); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("list (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.list(); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("create (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "name", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + feature_flag_id: "feature_flag_id", + feature_flag_name: "feature_flag_name", + authentication_flow: "authentication_flow", + allocation_strategy: "percentage", + status: "draft", + is_valid: true, + default_config: "tenant", + feature_flag_snapshot: { key: "value" }, + allocations: [ + { + variation_id: "variation_id", + variation_name: "variation_name", + segment_id: "segment_id", + segment_name: "segment_name", + weight: 1, + priority: 1, + is_control: true, + is_fallback: true, + variation_snapshot: { key: "value" }, + segment_snapshot: { key: "value" }, + }, + ], + editable_fields: ["editable_fields"], + levels: [1], + current_level: 1, + started_at: "2024-01-15T09:30:00Z", + ended_at: "2024-01-15T09:30:00Z", + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.experiments.create({ + name: "name", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + expect(response).toEqual(rawResponseBody); + }); + + test("create (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("create (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("create (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("create (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("create (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.ConflictError); + }); + + test("create (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.UnprocessableEntityError); + }); + + test("create (8)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.create({ + name: "foo", + feature_flag_id: "feature_flag_id", + authentication_flow: "authentication", + }); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("get (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + feature_flag_id: "feature_flag_id", + feature_flag_name: "feature_flag_name", + authentication_flow: "authentication_flow", + allocation_strategy: "percentage", + status: "draft", + is_valid: true, + default_config: "tenant", + feature_flag_snapshot: { key: "value" }, + allocations: [ + { + variation_id: "variation_id", + variation_name: "variation_name", + segment_id: "segment_id", + segment_name: "segment_name", + weight: 1, + priority: 1, + is_control: true, + is_fallback: true, + variation_snapshot: { key: "value" }, + segment_snapshot: { key: "value" }, + }, + ], + editable_fields: ["editable_fields"], + levels: [1], + current_level: 1, + started_at: "2024-01-15T09:30:00Z", + ended_at: "2024-01-15T09:30:00Z", + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .get("/experimentation/experiments/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.experiments.get("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("get (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments/id") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.get("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("get (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments/id") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.get("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("get (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments/id") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.get("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("get (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/experiments/id") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.get("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("delete (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().delete("/experimentation/experiments/id").respondWith().statusCode(200).build(); + + const response = await client.experimentation.experiments.delete("id"); + expect(response).toEqual(undefined); + }); + + test("delete (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/experiments/id") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.delete("id"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("delete (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/experiments/id") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.delete("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("delete (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/experiments/id") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.delete("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("update (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + feature_flag_id: "feature_flag_id", + feature_flag_name: "feature_flag_name", + authentication_flow: "authentication_flow", + allocation_strategy: "percentage", + status: "draft", + is_valid: true, + default_config: "tenant", + feature_flag_snapshot: { key: "value" }, + allocations: [ + { + variation_id: "variation_id", + variation_name: "variation_name", + segment_id: "segment_id", + segment_name: "segment_name", + weight: 1, + priority: 1, + is_control: true, + is_fallback: true, + variation_snapshot: { key: "value" }, + segment_snapshot: { key: "value" }, + }, + ], + editable_fields: ["editable_fields"], + levels: [1], + current_level: 1, + started_at: "2024-01-15T09:30:00Z", + ended_at: "2024-01-15T09:30:00Z", + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.experiments.update("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("update (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("update (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("update (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("update (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("update (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("update (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.UnprocessableEntityError); + }); + + test("update (8)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/experiments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.update("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + test("advanceRamp (1)", async () => { const server = mockServerPool.createServer(); const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); @@ -157,4 +929,306 @@ describe("ExperimentsClient", () => { }); }).rejects.toThrow(Management.TooManyRequestsError); }); + + test("updateStatus (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + feature_flag_id: "feature_flag_id", + feature_flag_name: "feature_flag_name", + authentication_flow: "authentication_flow", + allocation_strategy: "percentage", + status: "draft", + is_valid: true, + default_config: "tenant", + feature_flag_snapshot: { key: "value" }, + allocations: [ + { + variation_id: "variation_id", + variation_name: "variation_name", + segment_id: "segment_id", + segment_name: "segment_name", + weight: 1, + priority: 1, + is_control: true, + is_fallback: true, + variation_snapshot: { key: "value" }, + segment_snapshot: { key: "value" }, + }, + ], + editable_fields: ["editable_fields"], + levels: [1], + current_level: 1, + started_at: "2024-01-15T09:30:00Z", + ended_at: "2024-01-15T09:30:00Z", + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + expect(response).toEqual(rawResponseBody); + }); + + test("updateStatus (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("updateStatus (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("updateStatus (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("updateStatus (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("updateStatus (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.ConflictError); + }); + + test("updateStatus (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.UnprocessableEntityError); + }); + + test("updateStatus (8)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "active" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.updateStatus("id", { + status: "active", + }); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("validate (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { is_valid: true, errors: [{ code: "code", message: "message" }] }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/validate") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.experiments.validate("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("validate (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/validate") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.validate("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("validate (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/validate") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.validate("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("validate (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/validate") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.validate("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("validate (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/experiments/id/validate") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.experiments.validate("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); }); diff --git a/src/management/tests/wire/experimentation/featureFlags.test.ts b/src/management/tests/wire/experimentation/featureFlags.test.ts new file mode 100644 index 0000000000..f3090a337c --- /dev/null +++ b/src/management/tests/wire/experimentation/featureFlags.test.ts @@ -0,0 +1,763 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as Management from "../../../api/index"; +import { ManagementClient } from "../../../Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("FeatureFlagsClient", () => { + test("list (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + feature_flags: [ + { + id: "id", + name: "name", + description: "description", + type: "auth0", + status: "draft", + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }, + ], + next: "next", + }; + + server + .mockEndpoint({ once: false }) + .get("/experimentation/feature-flags") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const expected = rawResponseBody; + const page = await client.experimentation.featureFlags.list({ + from: "from", + take: 1, + type: "auth0", + status: "draft", + }); + + expect(expected.feature_flags).toEqual(page.data); + expect(page.hasNextPage()).toBe(true); + const nextPage = await page.getNextPage(); + expect(expected.feature_flags).toEqual(nextPage.data); + }); + + test("list (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.list(); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("list (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.list(); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("list (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.list(); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("list (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.list(); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("create (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "name", parameters: {} }; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "auth0", + status: "draft", + parameters: { key: { type: "string", value: { key: "value" }, description: "description" } }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.create({ + name: "name", + parameters: {}, + }); + expect(response).toEqual(rawResponseBody); + }); + + test("create (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", parameters: { parameters: { type: "string", value: { key: "value" } } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.create({ + name: "foo", + parameters: { + parameters: { + type: "string", + value: { + key: "value", + }, + }, + }, + }); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("create (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", parameters: { parameters: { type: "string", value: { key: "value" } } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.create({ + name: "foo", + parameters: { + parameters: { + type: "string", + value: { + key: "value", + }, + }, + }, + }); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("create (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", parameters: { parameters: { type: "string", value: { key: "value" } } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.create({ + name: "foo", + parameters: { + parameters: { + type: "string", + value: { + key: "value", + }, + }, + }, + }); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("create (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", parameters: { parameters: { type: "string", value: { key: "value" } } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.create({ + name: "foo", + parameters: { + parameters: { + type: "string", + value: { + key: "value", + }, + }, + }, + }); + }).rejects.toThrow(Management.ConflictError); + }); + + test("create (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", parameters: { parameters: { type: "string", value: { key: "value" } } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.create({ + name: "foo", + parameters: { + parameters: { + type: "string", + value: { + key: "value", + }, + }, + }, + }); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("get (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "auth0", + status: "draft", + parameters: { key: { type: "string", value: { key: "value" }, description: "description" } }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.get("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("get (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.get("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("get (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.get("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("get (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.get("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("get (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.get("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("delete (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().delete("/experimentation/feature-flags/id").respondWith().statusCode(200).build(); + + const response = await client.experimentation.featureFlags.delete("id"); + expect(response).toEqual(undefined); + }); + + test("delete (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.delete("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("delete (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id") + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.delete("id"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("delete (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.delete("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("update (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "auth0", + status: "draft", + parameters: { key: { type: "string", value: { key: "value" }, description: "description" } }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.update("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("update (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.update("id"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("update (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.update("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("update (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.update("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("update (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.update("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("update (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.update("id"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("update (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.update("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("updateStatus (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "draft" }; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "auth0", + status: "draft", + parameters: { key: { type: "string", value: { key: "value" }, description: "description" } }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", + }); + expect(response).toEqual(rawResponseBody); + }); + + test("updateStatus (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "draft" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", + }); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("updateStatus (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "draft" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", + }); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("updateStatus (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "draft" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", + }); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("updateStatus (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "draft" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", + }); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("updateStatus (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { status: "draft" }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.updateStatus("id", { + status: "draft", + }); + }).rejects.toThrow(Management.TooManyRequestsError); + }); +}); diff --git a/src/management/tests/wire/experimentation/featureFlags/variations.test.ts b/src/management/tests/wire/experimentation/featureFlags/variations.test.ts new file mode 100644 index 0000000000..4c7bab24f5 --- /dev/null +++ b/src/management/tests/wire/experimentation/featureFlags/variations.test.ts @@ -0,0 +1,685 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as Management from "../../../../api/index"; +import { ManagementClient } from "../../../../Client"; +import { mockServerPool } from "../../../mock-server/MockServerPool"; + +describe("VariationsClient", () => { + test("list (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + variations: [ + { + id: "id", + feature_flag_id: "feature_flag_id", + name: "name", + description: "description", + overrides: { key: "value" }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }, + ], + }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.variations.list("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("list (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.list("id"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("list (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.list("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("list (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.list("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("list (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.list("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("list (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.list("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("create (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "name", overrides: { key: "value" } }; + const rawResponseBody = { + id: "id", + feature_flag_id: "feature_flag_id", + name: "name", + description: "description", + overrides: { key: "value" }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.variations.create("id", { + name: "name", + overrides: { + key: "value", + }, + }); + expect(response).toEqual(rawResponseBody); + }); + + test("create (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", overrides: { overrides: { key: "value" } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.create("id", { + name: "foo", + overrides: { + overrides: { + key: "value", + }, + }, + }); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("create (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", overrides: { overrides: { key: "value" } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.create("id", { + name: "foo", + overrides: { + overrides: { + key: "value", + }, + }, + }); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("create (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", overrides: { overrides: { key: "value" } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.create("id", { + name: "foo", + overrides: { + overrides: { + key: "value", + }, + }, + }); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("create (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", overrides: { overrides: { key: "value" } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.create("id", { + name: "foo", + overrides: { + overrides: { + key: "value", + }, + }, + }); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("create (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", overrides: { overrides: { key: "value" } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.create("id", { + name: "foo", + overrides: { + overrides: { + key: "value", + }, + }, + }); + }).rejects.toThrow(Management.ConflictError); + }); + + test("create (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", overrides: { overrides: { key: "value" } } }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/feature-flags/id/variations") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.create("id", { + name: "foo", + overrides: { + overrides: { + key: "value", + }, + }, + }); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("get (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + id: "id", + feature_flag_id: "feature_flag_id", + name: "name", + description: "description", + overrides: { key: "value" }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.variations.get("id", "vid"); + expect(response).toEqual(rawResponseBody); + }); + + test("get (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.get("id", "vid"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("get (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.get("id", "vid"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("get (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.get("id", "vid"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("get (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.get("id", "vid"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("delete (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(200) + .build(); + + const response = await client.experimentation.featureFlags.variations.delete("id", "vid"); + expect(response).toEqual(undefined); + }); + + test("delete (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.delete("id", "vid"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("delete (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.delete("id", "vid"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("delete (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.delete("id", "vid"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("delete (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.delete("id", "vid"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("delete (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/feature-flags/id/variations/vid") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.delete("id", "vid"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("update (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { + id: "id", + feature_flag_id: "feature_flag_id", + name: "name", + description: "description", + overrides: { key: "value" }, + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.featureFlags.variations.update("id", "vid"); + expect(response).toEqual(rawResponseBody); + }); + + test("update (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.update("id", "vid"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("update (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.update("id", "vid"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("update (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.update("id", "vid"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("update (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.update("id", "vid"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("update (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.update("id", "vid"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("update (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/feature-flags/id/variations/vid") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.featureFlags.variations.update("id", "vid"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); +}); diff --git a/src/management/tests/wire/experimentation/segments.test.ts b/src/management/tests/wire/experimentation/segments.test.ts new file mode 100644 index 0000000000..d68faadc56 --- /dev/null +++ b/src/management/tests/wire/experimentation/segments.test.ts @@ -0,0 +1,627 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as Management from "../../../api/index"; +import { ManagementClient } from "../../../Client"; +import { mockServerPool } from "../../mock-server/MockServerPool"; + +describe("SegmentsClient", () => { + test("list (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + segments: [ + { + id: "id", + name: "name", + description: "description", + type: "self", + rules: [{}], + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }, + ], + next: "next", + }; + + server + .mockEndpoint({ once: false }) + .get("/experimentation/segments") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const expected = rawResponseBody; + const page = await client.experimentation.segments.list({ + from: "from", + take: 1, + type: "auth0", + }); + + expect(expected.segments).toEqual(page.data); + expect(page.hasNextPage()).toBe(true); + const nextPage = await page.getNextPage(); + expect(expected.segments).toEqual(nextPage.data); + }); + + test("list (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.list(); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("list (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.list(); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("list (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.list(); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("list (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.list(); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("create (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "name", rules: [{}] }; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "self", + rules: [{}], + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.segments.create({ + name: "name", + rules: [{}], + }); + expect(response).toEqual(rawResponseBody); + }); + + test("create (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", rules: [{}, {}] }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.create({ + name: "foo", + rules: [{}, {}], + }); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("create (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", rules: [{}, {}] }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.create({ + name: "foo", + rules: [{}, {}], + }); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("create (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", rules: [{}, {}] }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.create({ + name: "foo", + rules: [{}, {}], + }); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("create (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", rules: [{}, {}] }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.create({ + name: "foo", + rules: [{}, {}], + }); + }).rejects.toThrow(Management.ConflictError); + }); + + test("create (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", rules: [{}, {}] }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.create({ + name: "foo", + rules: [{}, {}], + }); + }).rejects.toThrow(Management.UnprocessableEntityError); + }); + + test("create (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = { name: "foo", rules: [{}, {}] }; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .post("/experimentation/segments") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.create({ + name: "foo", + rules: [{}, {}], + }); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("get (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "self", + rules: [{}], + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .get("/experimentation/segments/id") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.segments.get("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("get (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments/id") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.get("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("get (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments/id") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.get("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("get (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments/id") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.get("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("get (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .get("/experimentation/segments/id") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.get("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("delete (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + server.mockEndpoint().delete("/experimentation/segments/id").respondWith().statusCode(200).build(); + + const response = await client.experimentation.segments.delete("id"); + expect(response).toEqual(undefined); + }); + + test("delete (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/segments/id") + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.delete("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("delete (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/segments/id") + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.delete("id"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("delete (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .delete("/experimentation/segments/id") + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.delete("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); + + test("update (1)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { + id: "id", + name: "name", + description: "description", + type: "self", + rules: [{}], + created_at: "2024-01-15T09:30:00Z", + updated_at: "2024-01-15T09:30:00Z", + }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); + + const response = await client.experimentation.segments.update("id"); + expect(response).toEqual(rawResponseBody); + }); + + test("update (2)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.BadRequestError); + }); + + test("update (3)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.UnauthorizedError); + }); + + test("update (4)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(403) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.ForbiddenError); + }); + + test("update (5)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.NotFoundError); + }); + + test("update (6)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(409) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.ConflictError); + }); + + test("update (7)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(422) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.UnprocessableEntityError); + }); + + test("update (8)", async () => { + const server = mockServerPool.createServer(); + const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); + const rawRequestBody = {}; + const rawResponseBody = { key: "value" }; + + server + .mockEndpoint() + .patch("/experimentation/segments/id") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.experimentation.segments.update("id"); + }).rejects.toThrow(Management.TooManyRequestsError); + }); +}); diff --git a/src/management/tests/wire/organizations/organizationTemplate.test.ts b/src/management/tests/wire/organizations/organizationTemplate.test.ts deleted file mode 100644 index 628d4b60b6..0000000000 --- a/src/management/tests/wire/organizations/organizationTemplate.test.ts +++ /dev/null @@ -1,306 +0,0 @@ -// This file was auto-generated by Fern from our API Definition. - -import * as Management from "../../../api/index"; -import { ManagementClient } from "../../../Client"; -import { mockServerPool } from "../../mock-server/MockServerPool"; - -describe("OrganizationTemplateClient", () => { - test("get (1)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { - id: "id", - name: "name", - is_default: true, - organization_deletion_behavior: "allow", - connection_deletion_behavior: "allow", - enforce_permission_ceiling: true, - enforce_self_assignment_restriction: true, - connection_profile_id: "connection_profile_id", - user_attribute_profile_id: "user_attribute_profile_id", - allowed_strategies: ["adfs"], - invitation_landing_client_id: "invitation_landing_client_id", - admin_roles_assignment: ["admin_roles_assignment"], - use_for_organization_discovery: { default_value: true, allowed_values: [true] }, - role_visibility_policy: { default_value: "write", overrides: [{ role_id: "role_id", access: "write" }] }, - created_at: "2024-01-15T09:30:00Z", - updated_at: "2024-01-15T09:30:00Z", - }; - - server - .mockEndpoint() - .get("/organizations/id/organization-templates") - .respondWith() - .statusCode(200) - .jsonBody(rawResponseBody) - .build(); - - const response = await client.organizations.organizationTemplate.get("id"); - expect(response).toEqual(rawResponseBody); - }); - - test("get (2)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .get("/organizations/id/organization-templates") - .respondWith() - .statusCode(401) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.get("id"); - }).rejects.toThrow(Management.UnauthorizedError); - }); - - test("get (3)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .get("/organizations/id/organization-templates") - .respondWith() - .statusCode(403) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.get("id"); - }).rejects.toThrow(Management.ForbiddenError); - }); - - test("get (4)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .get("/organizations/id/organization-templates") - .respondWith() - .statusCode(404) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.get("id"); - }).rejects.toThrow(Management.NotFoundError); - }); - - test("get (5)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .get("/organizations/id/organization-templates") - .respondWith() - .statusCode(429) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.get("id"); - }).rejects.toThrow(Management.TooManyRequestsError); - }); - - test("assignOrganizationTemplate (1)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - server - .mockEndpoint() - .put("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(200) - .build(); - - const response = await client.organizations.organizationTemplate.assignOrganizationTemplate( - "id", - "template_id", - ); - expect(response).toEqual(undefined); - }); - - test("assignOrganizationTemplate (2)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .put("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(400) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.assignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.BadRequestError); - }); - - test("assignOrganizationTemplate (3)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .put("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(401) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.assignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.UnauthorizedError); - }); - - test("assignOrganizationTemplate (4)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .put("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(403) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.assignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.ForbiddenError); - }); - - test("assignOrganizationTemplate (5)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .put("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(429) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.assignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.TooManyRequestsError); - }); - - test("unassignOrganizationTemplate (1)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - server - .mockEndpoint() - .delete("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(200) - .build(); - - const response = await client.organizations.organizationTemplate.unassignOrganizationTemplate( - "id", - "template_id", - ); - expect(response).toEqual(undefined); - }); - - test("unassignOrganizationTemplate (2)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .delete("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(400) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.unassignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.BadRequestError); - }); - - test("unassignOrganizationTemplate (3)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .delete("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(401) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.unassignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.UnauthorizedError); - }); - - test("unassignOrganizationTemplate (4)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .delete("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(403) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.unassignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.ForbiddenError); - }); - - test("unassignOrganizationTemplate (5)", async () => { - const server = mockServerPool.createServer(); - const client = new ManagementClient({ maxRetries: 0, token: "test", environment: server.baseUrl }); - - const rawResponseBody = { key: "value" }; - - server - .mockEndpoint() - .delete("/organizations/id/organization-templates/template_id") - .respondWith() - .statusCode(429) - .jsonBody(rawResponseBody) - .build(); - - await expect(async () => { - return await client.organizations.organizationTemplate.unassignOrganizationTemplate("id", "template_id"); - }).rejects.toThrow(Management.TooManyRequestsError); - }); -}); diff --git a/src/management/tests/wire/resourceServers.test.ts b/src/management/tests/wire/resourceServers.test.ts index 2efd98c292..0d95c70b66 100644 --- a/src/management/tests/wire/resourceServers.test.ts +++ b/src/management/tests/wire/resourceServers.test.ts @@ -36,6 +36,7 @@ describe("ResourceServersClient", () => { encryption_key: { alg: "RSA-OAEP-256", pem: "pem" }, }, consent_policy: "transactional-authorization-with-mfa", + require_consent_non_repudiation: true, proof_of_possession: { mechanism: "mtls", required: true }, authorization_policy: { policy_id: "policy_id" }, client_id: "client_id", @@ -169,6 +170,7 @@ describe("ResourceServersClient", () => { encryption_key: { name: "name", alg: "RSA-OAEP-256", kid: "kid", pem: "pem" }, }, consent_policy: "transactional-authorization-with-mfa", + require_consent_non_repudiation: true, authorization_details: [{ key: "value" }], proof_of_possession: { mechanism: "mtls", required: true, required_for: "public_clients" }, subject_type_authorization: { @@ -332,6 +334,7 @@ describe("ResourceServersClient", () => { encryption_key: { alg: "RSA-OAEP-256", pem: "pem" }, }, consent_policy: "transactional-authorization-with-mfa", + require_consent_non_repudiation: true, proof_of_possession: { mechanism: "mtls", required: true }, authorization_policy: { policy_id: "policy_id" }, client_id: "client_id", @@ -525,6 +528,7 @@ describe("ResourceServersClient", () => { encryption_key: { name: "name", alg: "RSA-OAEP-256", kid: "kid", pem: "pem" }, }, consent_policy: "transactional-authorization-with-mfa", + require_consent_non_repudiation: true, authorization_details: [{ key: "value" }], proof_of_possession: { mechanism: "mtls", required: true, required_for: "public_clients" }, subject_type_authorization: { @@ -758,6 +762,7 @@ describe("ResourceServersClient", () => { encryption_key: { name: "name", alg: "RSA-OAEP-256", kid: "kid", pem: "pem" }, }, consent_policy: "transactional-authorization-with-mfa", + require_consent_non_repudiation: true, authorization_details: [{ key: "value" }], proof_of_possession: { mechanism: "mtls", required: true, required_for: "public_clients" }, subject_type_authorization: { 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 a60c68bce0..ad4d5eed04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -110,9 +110,9 @@ "@babel/types" "^7.29.7" "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.29.7", "@babel/parser@^7.29.8": - version "7.29.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.8.tgz#9653716a2f10c677b98fbc63d4bfb000c302cf17" - integrity sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA== + version "7.29.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.9.tgz#e7ee0a24f752c5296c6455855ccb72662e67ea96" + integrity sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA== dependencies: "@babel/types" "^7.29.8" @@ -893,9 +893,9 @@ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@*": - version "26.5.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-26.5.1.tgz#b19c390e15813f402a94b86e3af9042f792138be" - integrity sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g== + version "26.6.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-26.6.3.tgz#411dccb5fc25687c958fe35b6ca3741d48a8d13c" + integrity sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg== dependencies: undici-types "~8.9.0" @@ -939,99 +939,99 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^8.38.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz#59f635c74dd1e2bffb6aecbda557b24dadffd3dc" - integrity sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA== + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.0.tgz#99f474391c3e54bfcb73b1934ea2dba6d6a59bef" + integrity sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.70.0" - "@typescript-eslint/type-utils" "8.70.0" - "@typescript-eslint/utils" "8.70.0" - "@typescript-eslint/visitor-keys" "8.70.0" + "@typescript-eslint/scope-manager" "8.71.0" + "@typescript-eslint/type-utils" "8.71.0" + "@typescript-eslint/utils" "8.71.0" + "@typescript-eslint/visitor-keys" "8.71.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.5.0" "@typescript-eslint/parser@^8.38.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.70.0.tgz#96ce2de96c06c8442fea1a8f7b07855a56b3c5e3" - integrity sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q== - dependencies: - "@typescript-eslint/scope-manager" "8.70.0" - "@typescript-eslint/types" "8.70.0" - "@typescript-eslint/typescript-estree" "8.70.0" - "@typescript-eslint/visitor-keys" "8.70.0" + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.71.0.tgz#e1ecf4ff47f91c90fa11866b2190ee072d5a14af" + integrity sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A== + dependencies: + "@typescript-eslint/scope-manager" "8.71.0" + "@typescript-eslint/types" "8.71.0" + "@typescript-eslint/typescript-estree" "8.71.0" + "@typescript-eslint/visitor-keys" "8.71.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.70.0.tgz#a62e837362f26c604ad15b20bacce1c3f4d6b552" - integrity sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ== +"@typescript-eslint/project-service@8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.71.0.tgz#8408b60210e2f49ddf3cdb25c985df272a35461f" + integrity sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.70.0" - "@typescript-eslint/types" "^8.70.0" + "@typescript-eslint/tsconfig-utils" "^8.71.0" + "@typescript-eslint/types" "^8.71.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.70.0.tgz#b77628b03c9c56ef21fb5a6bc2f4a4335163b999" - integrity sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ== +"@typescript-eslint/scope-manager@8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.71.0.tgz#4a5a637b272d13d8755d5b5024099826eb4fc688" + integrity sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw== dependencies: - "@typescript-eslint/types" "8.70.0" - "@typescript-eslint/visitor-keys" "8.70.0" + "@typescript-eslint/types" "8.71.0" + "@typescript-eslint/visitor-keys" "8.71.0" -"@typescript-eslint/tsconfig-utils@8.70.0", "@typescript-eslint/tsconfig-utils@^8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.0.tgz#f583ca72159c4fd8e775c153da3241de6b77974f" - integrity sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw== +"@typescript-eslint/tsconfig-utils@8.71.0", "@typescript-eslint/tsconfig-utils@^8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.0.tgz#83a69c9a514af8f5ff099cc6c0d609c273e8f039" + integrity sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg== -"@typescript-eslint/type-utils@8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.70.0.tgz#7f9c01c24e56bfad2a089167926c7cdded9de5f6" - integrity sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw== +"@typescript-eslint/type-utils@8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.71.0.tgz#eab342370b18ae8ad39c031e3878bddabc1d254b" + integrity sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw== dependencies: - "@typescript-eslint/types" "8.70.0" - "@typescript-eslint/typescript-estree" "8.70.0" - "@typescript-eslint/utils" "8.70.0" + "@typescript-eslint/types" "8.71.0" + "@typescript-eslint/typescript-estree" "8.71.0" + "@typescript-eslint/utils" "8.71.0" debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@8.70.0", "@typescript-eslint/types@^8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.70.0.tgz#9ee52888cdeca604fe9436935219b967fa7f6053" - integrity sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ== +"@typescript-eslint/types@8.71.0", "@typescript-eslint/types@^8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.71.0.tgz#a230a121e71665060afe27d1f1ad110371f9451e" + integrity sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ== -"@typescript-eslint/typescript-estree@8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.0.tgz#79cfcd9678ee28ea69cc13032c0f89bb1d298917" - integrity sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA== +"@typescript-eslint/typescript-estree@8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.0.tgz#78127ef7d5dad7f036ad065235a0fb92a3e02340" + integrity sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw== dependencies: - "@typescript-eslint/project-service" "8.70.0" - "@typescript-eslint/tsconfig-utils" "8.70.0" - "@typescript-eslint/types" "8.70.0" - "@typescript-eslint/visitor-keys" "8.70.0" + "@typescript-eslint/project-service" "8.71.0" + "@typescript-eslint/tsconfig-utils" "8.71.0" + "@typescript-eslint/types" "8.71.0" + "@typescript-eslint/visitor-keys" "8.71.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.70.0.tgz#78a78b4c52dd8523e5321993cb46ebe6d7934510" - integrity sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA== +"@typescript-eslint/utils@8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.71.0.tgz#4e7923ae1083e941c6fefabf4b978ffecc48ecb0" + integrity sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.70.0" - "@typescript-eslint/types" "8.70.0" - "@typescript-eslint/typescript-estree" "8.70.0" + "@typescript-eslint/scope-manager" "8.71.0" + "@typescript-eslint/types" "8.71.0" + "@typescript-eslint/typescript-estree" "8.71.0" -"@typescript-eslint/visitor-keys@8.70.0": - version "8.70.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.0.tgz#b451c8aea76dc97fc768b8d9d7f61019bf789628" - integrity sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ== +"@typescript-eslint/visitor-keys@8.71.0": + version "8.71.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.0.tgz#8ca9a12bc5083b9a090c3ea4e5d9ef97f6558199" + integrity sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A== dependencies: - "@typescript-eslint/types" "8.70.0" + "@typescript-eslint/types" "8.71.0" eslint-visitor-keys "^5.0.0" "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": @@ -1256,9 +1256,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.2.2: - version "6.3.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.3.0.tgz#247c8e7b70a1a43b10ce14c0226fcbf58e8815d5" - integrity sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ== + version "6.4.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.4.0.tgz#69093d6436d30dbb9b04fe7bdcbcfe16c697d8a1" + integrity sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" @@ -1384,10 +1384,10 @@ balanced-match@^4.0.2: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== -baseline-browser-mapping@^2.11.20: - 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== +baseline-browser-mapping@^2.11.26: + version "2.11.26" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.26.tgz#01bf6774efb3fbb9de20afc6b923ab85c7d230a6" + integrity sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ== brace-expansion@^1.1.7: version "1.1.21" @@ -1412,15 +1412,15 @@ braces@^3.0.3: fill-range "^7.1.1" browserslist@^4.24.0, browserslist@^4.28.1: - version "4.28.9" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.9.tgz#07ce6b449b90af880eb9bfb7cd39372cc4f71c8c" - integrity sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg== + version "4.29.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.29.2.tgz#652374dd6ad03dfcc66d63dad56aaaba9705b075" + integrity sha512-/u9r8k8ue4ZhHCw9ovDtltpWdXeXzjhdxCGj6vm1RGLPXb8lu33EMdoCHF5Sx8oYBS+Q/FYYNmAqlHncxR3RmA== dependencies: - baseline-browser-mapping "^2.11.20" - caniuse-lite "^1.0.30001810" - electron-to-chromium "^1.5.420" - node-releases "^2.0.54" - update-browserslist-db "^1.3.2" + baseline-browser-mapping "^2.11.26" + caniuse-lite "^1.0.30001812" + electron-to-chromium "^1.5.439" + node-releases "^2.0.57" + update-browserslist-db "^1.3.3" bs-logger@^0.2.6: version "0.2.6" @@ -1464,10 +1464,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001810: - version "1.0.30001810" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz#4970b477dea3278374de9bc43aa8f5d39fc3cda2" - integrity sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg== +caniuse-lite@^1.0.30001812: + version "1.0.30001813" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001813.tgz#acc16d52a011b87873b14e3c62040bb273c270eb" + integrity sha512-zfjJo4rM0+fUomGDBW/xcDjhIwz/210DGvip2MAMDZ8KHcRPnOHmEgHPZP0UHlZoxr8fYKVJOqcORhYQcG4FKQ== chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" @@ -1691,10 +1691,10 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -electron-to-chromium@^1.5.420: - version "1.5.428" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz#36bdb39055d71ab08a54855508ef33940bc467e6" - integrity sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw== +electron-to-chromium@^1.5.439: + version "1.5.440" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.440.tgz#c669c8a6ac7416de937632a895f4a9feca7b8c97" + integrity sha512-SghDzqdJokdz8zP8YNlvS74+CwLRoUPDGvP/gFA+HrKVw+pOshUAgx8pXR37xl/u16zGae746rFNWvZ/22kU2Q== emittery@^0.13.1: version "0.13.1" @@ -2071,9 +2071,9 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.1, get-east-asian-width@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz#216900f91df11a8b2c198c3e1d93d6c035a776b9" - integrity sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz#7ee99f0bd5ab7c622002deeabba232923726d1d4" + integrity sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q== get-intrinsic@^1.2.6: version "1.3.0" @@ -2177,7 +2177,7 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: +hasown@^2.0.2, hasown@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== @@ -2241,9 +2241,9 @@ ignore@^5.2.0: integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== ignore@^7.0.5: - version "7.0.9" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.9.tgz#475b2197ade916edab05ade35c691518e8543382" - integrity sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw== + version "7.0.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.10.tgz#c578b9e3624f77e2298876f6516c9ed976c84fdd" + integrity sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q== import-fresh@^3.2.1: version "3.3.1" @@ -2285,11 +2285,11 @@ is-arrayish@^0.2.1: integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-core-module@^2.16.1: - version "2.16.2" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.2.tgz#3e07450a8080ebce3fbf0cac494f4d2ab324e082" - integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA== + version "2.17.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.17.0.tgz#75c1c76236c9f7e38ddbfff292e127dc13b21027" + integrity sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA== dependencies: - hasown "^2.0.3" + hasown "^2.0.4" is-extglob@^2.1.1: version "2.1.1" @@ -3090,9 +3090,9 @@ minimist@^1.2.5: integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minimizer-webpack-plugin@^5.7.0: - version "5.10.1" - resolved "https://registry.yarnpkg.com/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.10.1.tgz#333ad27a53fcd51bbd868252a4733ee08480b021" - integrity sha512-+dsZEyTcy1lkq41lxdiOqvb26gXbYGgwY+30w37f9PqUVkuEBejBLDotsHOTjuga9xJyGLW2DqzKDUyJ4W/PMQ== + version "5.12.0" + resolved "https://registry.yarnpkg.com/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.12.0.tgz#acef4a0bd3c5ee405f767a9d90e0173b55b16458" + integrity sha512-PCN1x1OzFeFB2GlP91s46q7Gz8Jf16Z7M45bzpoqrYZlh12QeY6tYVM5h299wbwy1DxnQ69VFmmh8Hb2Ff8mbw== dependencies: "@jridgewell/trace-mapping" "^0.3.31" jest-worker "^27.4.5" @@ -3163,10 +3163,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.54: - version "2.0.55" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.55.tgz#c52faedb68001dcfe77495b5ed1ac5827a1788fc" - integrity sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ== +node-releases@^2.0.57: + version "2.0.57" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.57.tgz#47827d34b86a98b4b7afacc1222878c286b6ef2d" + integrity sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw== normalize-path@^3.0.0: version "3.0.0" @@ -3181,9 +3181,9 @@ npm-run-path@^4.0.1: path-key "^3.0.0" nwsapi@^2.2.2: - version "2.2.27" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.27.tgz#a7c71fc7d401546ad94a026c30e868c078a70e87" - integrity sha512-gQPNF78qebCQ6tvVFBYrvJdBNOrYZm90ZlXgpIFm06p6qHDHq/XC4TnJftN6OMbxVE0UTBAoRgcsDeJBBooITw== + version "2.2.28" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.28.tgz#f958de0c1ad3fc7c2575a3adb4a802e64587b094" + integrity sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg== once@^1.3.0: version "1.4.0" @@ -3650,9 +3650,9 @@ string-width@^7.0.0: strip-ansi "^7.1.0" string-width@^8.2.0: - version "8.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.2.2.tgz#7310516493df575742fe98af6fae87d85d5ed0ac" - integrity sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg== + version "8.3.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.3.0.tgz#f1e85ca6d276ed9226c979c3651fb7abf548fbbe" + integrity sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ== dependencies: get-east-asian-width "^1.5.0" strip-ansi "^7.1.2" @@ -3754,17 +3754,17 @@ tinyglobby@^0.2.15: fdir "^6.5.0" picomatch "^4.0.4" -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-core@^7.4.16: + version "7.4.16" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.16.tgz#f4b1711fd30e5d9a742d94f6b391d8649acc8ae1" + integrity sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA== tldts@^7.0.5: - version "7.4.13" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.13.tgz#50e9a884162e6091844dadf0696df9353eb8b2be" - integrity sha512-iHtaIWWIbMDkCeJdTBzZFGgbluE5J+oHlb2g7+oAz1S1gpuVpabRZdQyd471Vl8UUkcz2vXSL8xZH2kyCe8tfA== + version "7.4.16" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.16.tgz#0bb8768d14c7f540dbf38256ae88e637b08937d9" + integrity sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg== dependencies: - tldts-core "^7.4.13" + tldts-core "^7.4.16" tmpl@1.0.5: version "1.0.5" @@ -3808,9 +3808,9 @@ ts-api-utils@^2.5.0: integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== ts-jest@^29.3.4: - version "29.4.12" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.12.tgz#c34cd91f02d364e9286d2c016843315fd0efff76" - integrity sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw== + version "29.4.14" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.14.tgz#cb36c043df0fada861c8b604a5d514f953126180" + integrity sha512-+943G6wCX5E3VpzTQhBGBFntCmdT+QAkTvUlp03edTONFBvqskuXPhpyX16xdUotO4P5HQKyppldSWjocoltLQ== dependencies: bs-logger "^0.2.6" fast-json-stable-stringify "^2.1.0" @@ -3885,9 +3885,9 @@ uglify-js@^3.1.4: integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== undici-types@^6.15.0: - version "6.28.1" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.28.1.tgz#d665fea6640ffdecff913bc2408c733bc34d6d91" - integrity sha512-8sc9COfigHECtZwvbJdkTu3zy+U4HHmJVceTSl8T+RSWBCupR+6SGzWf+hUoklc3DxBUBzGbTQzYBFLpafwLOw== + version "6.29.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.29.0.tgz#0d27a99a7d6ebdb87ffa891f6f7096804556516e" + integrity sha512-yzE4aF6qGj7fE8lu8fE8LonY07kmMXs20QR1bG3JGJ+jXrVTLtgUBieEr74AQRChYyqHrhJm9IktT337qOQhng== undici-types@~6.21.0: version "6.21.0" @@ -3900,16 +3900,16 @@ undici-types@~8.9.0: integrity sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg== undici@^7.12.0: - version "7.29.1" - resolved "https://registry.yarnpkg.com/undici/-/undici-7.29.1.tgz#7741c6fc8b3e1a48e30323833642bfbe841443ad" - integrity sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q== + version "7.30.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-7.30.0.tgz#64a947266eb7e21dbeac8b99f8720feef308bbe0" + integrity sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ== universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -update-browserslist-db@^1.3.2: +update-browserslist-db@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz#197e21fb2561fa89f8b94fad605a2b296a18993a" integrity sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ== @@ -3973,14 +3973,14 @@ webidl-conversions@^7.0.0: integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== webpack-sources@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.5.1.tgz#76c2418486dcc02b2aa0694c104176c2858fe84a" - integrity sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw== + version "3.6.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.6.0.tgz#b2e4a4f1aef1a0d60f3011eb47736c5bf7f1ed7f" + integrity sha512-EIMmPVvNI0CYXyRPp34F2Qk7W37/BZVZIofS6LAUnrNjCVahS+hO4mxwFJDjQjODTGZDtVzLYoN2+1dYkhk9qA== webpack@^5.105.4: - version "5.111.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.111.0.tgz#65e7e0044d69373ea0129ef401c0299aaba52764" - integrity sha512-A2R74kfE6b3eLKS91iZiol03Ebx7avmJJBOyiP+4LePW9NNeNJGQkKeSQos4dk6R4H0YAMYlv+aOrs1CyWT3eA== + version "5.111.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.111.1.tgz#7c12cd014fbb9bfd8509a6cfc6f1c79f4e06cade" + integrity sha512-cNypaz0RP+S4cvQVi1M/3bRUkvNP0xZpL0TjFx+c6jg64VbxD+2weWDgY9UnjRI6dhZgtaj8DU76GGFcJ79xPQ== dependencies: "@types/estree" "^1.0.8" "@types/json-schema" "^7.0.15" @@ -4078,9 +4078,9 @@ write-file-atomic@^4.0.2: signal-exit "^3.0.7" ws@^8.11.0: - version "8.21.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.3.tgz#660b4faddb6a3e575c86e078126919961f4de4fc" - integrity sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw== + version "8.22.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.22.0.tgz#40f16e1d7588ac0575041be5c49b8eb261c28e73" + integrity sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg== xml-name-validator@^4.0.0: version "4.0.0" From c26637b6f59a0dd9f7093a7f96b321ab22e8f848 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 06:03:13 +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 (8): - 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) - patch-567042f4: Release v7.2.0 (#1409) 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 c169b26f15..26238c0938 100644 --- a/.fern/replay.lock +++ b/.fern/replay.lock @@ -48,14 +48,20 @@ generations: cli_version: unknown generator_versions: fernapi/fern-typescript-sdk: 3.72.5 -current_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 + - commit_sha: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe + tree_hash: 17d491519ff83b4accfda1f46e85e5ec90d64a2b + timestamp: 2026-09-29T06:02:58.290Z + cli_version: unknown + generator_versions: + fernapi/fern-typescript-sdk: 3.72.5 +current_generation: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe 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: b96b8d7eee204c8a279672038c146c2fb309a6b1 + base_generation: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe files: - v6_MIGRATION_GUIDE.md patch_content: | @@ -389,7 +395,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: b96b8d7eee204c8a279672038c146c2fb309a6b1 + base_generation: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe files: - v6_MIGRATION_GUIDE.md patch_content: | @@ -8231,7 +8237,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: b96b8d7eee204c8a279672038c146c2fb309a6b1 + base_generation: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe files: - .shiprc patch_content: | @@ -13708,7 +13714,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: b96b8d7eee204c8a279672038c146c2fb309a6b1 + base_generation: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe files: - AUTH_MIGRATION_GUIDE.md - auth-migration/authentication-flows.md @@ -16108,7 +16114,7 @@ patches: original_commit: 842d2f5189493b78e420c17c968c80bc239e143a original_message: "docs: add v7 migration guide (v6 → v7) (#1396)" original_author: tusharpandey13 - base_generation: b96b8d7eee204c8a279672038c146c2fb309a6b1 + base_generation: 4cd9e8326a37a8aba892eb12cd32659915b0fbfe files: - v7_MIGRATION_GUIDE.md patch_content: | @@ -20102,3 +20108,1859 @@ patches: src/management/version.ts: | export const SDK_VERSION = "7.1.0"; status: unresolved + - id: patch-567042f4 + content_hash: sha256:ea65cb0a887a1af1882b5825cd5250e225453c71f3dbe0f3cc0f65d71053fc78 + original_commit: 567042f4cd8cf15e1e9496a1f92c6b74181998a7 + original_message: Release v7.2.0 (#1409) + original_author: Ankita Tripathi <51994119+ankita10119@users.noreply.github.com> + base_generation: b52dd3f4c6848d07a1c7070009a9653a78642497 + files: + - package.json + - src/management/version.ts + patch_content: | + diff --git a/package.json b/package.json + index 85bc016c6..165459dd0 100644 + --- a/package.json + +++ b/package.json + @@ -1,6 +1,6 @@ + { + "name": "auth0", + - "version": "7.1.0", + + "version": "7.2.0", + "private": false, + "repository": { + "type": "git", + diff --git a/src/management/version.ts b/src/management/version.ts + index 07bfe918b..bce628b46 100644 + --- a/src/management/version.ts + +++ b/src/management/version.ts + @@ -1 +1 @@ + -export const SDK_VERSION = "7.1.0"; + +export const SDK_VERSION = "7.2.0"; + theirs_snapshot: + package.json: | + { + "name": "auth0", + "version": "7.2.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.2.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.