@descope/node-sdk

中文文档
2.22.0发布于 2026/11/11MITTypeScriptESM + CJS构建来源已验证近 30 天下载 187.8万

Node.js library used to integrate with Descope

原始使用文档
文档目录40 节
  1. Requirements
  2. Installing the SDK
  3. Authentication Functions
  4. Setup
  5. Management Functions
  6. Setup
  7. Authentication Management Key
  8. Key Differences
  9. Usage Examples
  10. Error Handling
  11. OTP Authentication
  12. Magic Link
  13. Enchanted Link
  14. OAuth
  15. SSO/SAML
  16. TOTP Authentication
  17. Passwords
  18. Session Validation
  19. Roles & Permission Validation
  20. Logging Out
  21. Management Functions
  22. Setup
  23. Manage Tenants
  24. Manage Families
  25. Manage Password
  26. Manage SSO applications
  27. Manage Users
  28. Manage Project
  29. Manage Access Keys
  30. Manage SSO Setting
  31. Manage Permissions
  32. Manage Roles
  33. Query SSO Groups
  34. Manage Flows
  35. Manage JWTs
  36. Impersonate
  37. Embedded Links
  38. Audit
  39. Manage FGA (Fine-grained Authorization)
  40. Manage Outbound Applications

Descope SDK for Node.js

The Descope SDK for Node.js provides convenient access to the Descope user management and authentication API for a backend written in Node.js. You can read more on the Descope Website.

Requirements

The SDK supports Node version 18 and above.

The SDK uses the runtime's native fetch instead of a Node HTTP client, so it also runs on Cloudflare Workers and other edge runtimes (Workers require the nodejs_compat flag for crypto/Buffer).

Installing the SDK

Install the package with:

npm i --save @descope/node-sdk

Authentication Functions

Setup

Before you can use authentication functions listed below, you must initialize descopeClient to use all of the built-in SDK functions.

You'll need your Descope Project ID to create this, and you can find it on the project page in the Descope Console.

import DescopeClient from '@descope/node-sdk';

const descopeClient = DescopeClient({ projectId: 'my-project-ID' });

Once you've created a descopeClient, you can use that to work with the following functions:

  1. OTP Authentication
  2. Magic Link
  3. Enchanted Link
  4. OAuth
  5. SSO/SAML
  6. TOTP Authentication
  7. Passwords
  8. Session Validation
  9. Roles & Permission Validation
  10. Logging Out

Management Functions

Setup

Before you can use management functions listed below, you must initialize descopeClient.

If you wish to also use management functions, you will need to initialize a new version of your descopeClient, but this time with a ManagementKey as well as your Project ID. Create a management key in the Descope Console.

import DescopeClient from '@descope/node-sdk';

const descopeClient = DescopeClient({
  projectId: 'my-project-ID',
  managementKey: 'management-key',
});

Then, you can use that to work with the following functions:

  1. Manage Tenants
  2. Manage Families
  3. Manage Users
  4. Manage Access Keys
  5. Manage SSO Setting
  6. Manage Permissions
  7. Manage Roles
  8. Query SSO Groups
  9. Manage Flows
  10. Manage JWTs
  11. Impersonate
  12. Embedded Links
  13. Audit
  14. Manage FGA (Fine-grained Authorization)
  15. Manage Project
  16. Manage SSO applications
  17. Manage Management Keys
  18. Manage Descopers
  19. Manage Engines

If you wish to run any of our code samples and play with them, check out our Code Examples section.

If you're performing end-to-end testing, check out the Utils for your end to end (e2e) tests and integration tests section. You will need to use the descopeClient you created under the setup of Management Functions.

Authentication Management Key

The authManagementKey is an alternative to the managementKey that provides a way to perform management operations while maintaining separation between authentication and management clients.

Key Differences

  • Purpose: Use authManagementKey for authentication-related management operations, while managementKey is for general management operations
  • Client Separation: You can have one client for management operations and another for authentication operations
  • Mutual Exclusivity: You cannot pass both authManagementKey and managementKey together - choose one based on your use case

Usage Examples

Using authManagementKey for authentication operations:

import DescopeClient from '@descope/node-sdk';

const authClient = DescopeClient({
  projectId: 'my-project-ID',
  authManagementKey: 'auth-management-key',
});

// This client can be used for authentication-related management operations

Separate clients for different operations:

import DescopeClient from '@descope/node-sdk';

// Client for general management operations
const managementClient = DescopeClient({
  projectId: 'my-project-ID',
  managementKey: 'management-key',
});

// Client for authentication operations
const authClient = DescopeClient({
  projectId: 'my-project-ID',
  authManagementKey: 'auth-management-key',
});

// Use managementClient for user management, tenant management, etc.
// Use authClient for authentication-related operations

Note: Create your authentication management key in the Descope Console, similar to how you create a regular management key.


Error Handling

Every async operation may fail. In case it does, there will be information regarding what happened on the response object. A typical case of error handling might look something like:

import DescopeClient, { SdkResponse } from '@descope/node-sdk';

const { DescopeErrors } = DescopeClient;

// ...

try {
  const resp = await sdk.otp.signIn.email(loginId);
  if (resp.error) {
    switch (resp.error.errorCode) {
      case DescopeErrors.userNotFound:
        // Handle specifically
        break;
      default:
      // Handle generally
      // `resp.error` will contain `errorCode`, `errorDescription` and sometimes `errorMessage` to
      // help understand what went wrong. See SdkResponse for more information.
    }
  }
} catch (e) {
  // Handle technical error
}

OTP Authentication

Send a user a one-time password (OTP) using your preferred delivery method (Email / SMS / Voice call / WhatsApp). An email address or phone number must be provided accordingly.

The user can either sign up, sign in or sign up or in

// Every user must have a login ID. All other user information is optional
const loginId = '[email protected]';
const user = {
  name: 'Desmond Copland',
  phone: '212-555-1234',
  email: loginId,
};
await descopeClient.otp.signUp['email'](loginId, user);

The user will receive a code using the selected delivery method. Verify that code using:

const jwtResponse = await descopeClient.otp.verify['email'](loginId, 'code');
// jwtResponse.data.sessionJwt
// jwtResponse.data.refreshJwt

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

Send a user a Magic Link using your preferred delivery method (email / SMS). The Magic Link will redirect the user to page where the its token needs to be verified. This redirection can be configured in code, or globally in the Descope Console

The user can either sign up, sign in or sign up or in

// If configured globally, the redirect URI is optional. If provided however, it will be used
// instead of any global configuration
const URI = 'http://myapp.com/verify-magic-link';
await descopeClient.magicLink.signUpOrIn['email']('[email protected]', URI);

To verify a magic link, your redirect page must call the validation function on the token (t) parameter (https://your-redirect-address.com/verify?t=<token>):

const jwtResponse = await descopeClient.magicLink.verify('token');
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

Using the Enchanted Link APIs enables users to sign in by clicking a link delivered to their email address. The email will include 3 different links, and the user will have to click the right one, based on the 2-digit number that is displayed when initiating the authentication process.

This method is similar to Magic Link but differs in two major ways:

  • The user must choose the correct link out of the three, instead of having just one single link.
  • This supports cross-device clicking, meaning the user can try to log in on one device, like a computer, while clicking the link on another device, for instance a mobile phone.

The Enchanted Link will redirect the user to page where the its token needs to be verified. This redirection can be configured in code per request, or set globally in the Descope Console.

The user can either sign up, sign in or sign up or in

// If configured globally, the redirect URI is optional. If provided however, it will be used
// instead of any global configuration.
const URI = 'http://myapp.com/verify-enchanted-link';
const enchantedLinkRes = await descopeClient.enchantedLink.signIn('[email protected]', URI);
enchantedLinkRes.data.linkId; // Should be displayed to the user so they can click the corresponding link in the email
enchantedLinkRes.data.pendingRef; // Used to poll for a valid session

After sending the link, you must poll to receive a valid session using the pendingRef from the previous step. A valid session will be returned only after the user clicks the right link.

// Poll for a certain number of tries / time frame. You can control the polling interval and time frame
// with the optional WaitForSessionConfig
const jwtResponse = await descopeClient.enchantedLink.waitForSession(
  enchantedLinkRes.data.pendingRef,
);
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

To verify an enchanted link, your redirect page must call the validation function on the token (t) parameter (https://your-redirect-address.com/verify?t=<token>). Once the token is verified, the session polling will receive a valid response.

try {
  await descopeClient.enchantedLink.verify('token');
  // token is invalid
} catch (error) {
  // token is valid
}

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

OAuth

Users can authenticate using their social logins, via the OAuth protocol. Configure your OAuth settings on the Descope console. To start an OAuth flow call:

// Choose an oauth provider out of the supported providers
// If configured globally, the return URL is optional. If provided however, it will be used
// instead of any global configuration.

const urlRes = await descopeClient.oauth.start['google'](redirectUrl);
urlRes.data.url; // Redirect the user to the returned URL to start the OAuth redirect chain

The user will authenticate with the authentication provider, and will be redirected back to the redirect URL, with an appended code HTTP URL parameter. Exchange it to validate the user:

const jwtResponse = await descopeClient.oauth.exchange('token');
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

SSO/SAML

Users can authenticate to a specific tenant using SAML or Single Sign On. Configure your SSO/SAML settings on the Descope console. To start a flow call:

// If configured globally, the return URL is optional. If provided however, it will be used
// instead of any global configuration.
const redirectUrl = 'https://my-app.com/handle-saml';
const urlRes = await descopeClient.saml.start('tenant'); // Choose which tenant to log into. An email can also be provided here and the domain will be extracted from it
urlRes.data.url; // Redirect the user to the given returned URL to start the SSO/SAML redirect chain

The user will authenticate with the authentication provider configured for that tenant, and will be redirected back to the redirect URL, with an appended code HTTP URL parameter. Exchange it to validate the user:

const jwtResponse = await descopeClient.saml.exchange('token');
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

TOTP Authentication

The user can authenticate using an authenticator app, such as Google Authenticator. Sign up like you would using any other authentication method. The sign up response will then contain a QR code image that can be displayed to the user to scan using their mobile device camera app, or the user can enter the key manually or click on the link provided by the provisioningURL.

Existing users can add TOTP using the update function.

// Every user must have a login ID. All other user information is optional
const loginId = '[email protected]';
const user = {
  name: 'Desmond Copland',
  phone: '212-555-1234',
  email: loginId,
};
const totpRes = await descopeClient.totp.signUp(loginId, user);
// Use one of the provided options to have the user add their credentials to the authenticator
totpRes.data.provisioningURL;
totpRes.data.image;
totpRes.data.key;

There are 3 different ways to allow the user to save their credentials in their authenticator app - either by clicking the provisioning URL, scanning the QR image or inserting the key manually. After that, signing in is done using the code the app produces.

const jwtResponse = await descopeClient.totp.verify(loginId, 'code');
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

Deleting the TOTP Seed

Provide the loginId to the function to remove the user's TOTP seed.

const response = await descopeClient.management.user.removeTOTPSeed(loginId);

Deleting Recovery Codes

Provide a login ID or user ID to the function to remove all of the user's recovery codes.

const response = await descopeClient.management.user.removeRecoveryCodes(loginIdOrUserId);

Passwords

The user can also authenticate with a password, though it's recommended to prefer passwordless authentication methods if possible. Sign up requires the caller to provide a valid password that meets all the requirements configured for the password authentication method in the Descope console.

// Every user must have a loginId. All other user information is optional
const loginId = '[email protected]';
const password = 'qYlvi65KaX';
const user = {
  name: 'Desmond Copeland',
  email: loginId,
};
const jwtResponse = await descopeClient.password.signUp(loginId, password, user);
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

The user can later sign in using the same loginId and password.

const jwtResponse = await descopeClient.password.signIn(loginId, password);
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

The session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on session validation

In case the user needs to update their password, one of two methods are available: Resetting their password or replacing their password

Changing Passwords

NOTE: sendReset will only work if the user has a validated email address. Otherwise password reset prompts cannot be sent.

In the password authentication method in the Descope console, it is possible to define which alternative authentication method can be used in order to authenticate the user, in order to reset and update their password.

// Start the reset process by sending a password reset prompt. In this example we'll assume
// that magic link is configured as the reset method. The optional redirect URL is used in the
// same way as in regular magic link authentication.
const loginId = '[email protected]';
const redirectURL = 'https://myapp.com/password-reset';
const passwordResetResponse = await descopeClient.password.sendReset(loginId, redirectURL);

The magic link, in this case, must then be verified like any other magic link (see the magic link section for more details). However, after verifying the user, it is expected to allow them to provide a new password instead of the old one. Since the user is now authenticated, this is possible via:

// The refresh token is required to make sure the user is authenticated.
await descopeClient.password.update(loginId, newPassword, token);

update() can always be called when the user is authenticated and has a valid session.

Alternatively, it is also possible to replace an existing active password with a new one.

// Replaces the user's current password with a new one
const jwtResponse = await descopeClient.password.replace(loginId, oldPassword, newPassword);
// jwtResponse.data.sessionJwt;
// jwtResponse.data.refreshJwt;

Session Validation

Every secure request performed between your client and server needs to be validated. The client sends the session and refresh tokens with every request, and they are validated using one of the following:

// Validate the session. Will throw if expired
const authInfo = await descopeClient.validateSession('sessionToken');

// If validateSession throws an exception, you will need to refresh the session using
const refreshed = await descopeClient.refreshSession('refreshToken');

// Alternatively, you could combine the two and
// have the session validated and automatically refreshed when expired
const combined = await descopeClient.validateAndRefreshSession('sessionToken', 'refreshToken');

// Optional: Validate audience  for backend-only flows
// Provide VerifyOptions with the expected audience(s)
const withAud = await descopeClient.validateSession('sessionToken', { audience: 'my-audience' });
const withAudArray = await descopeClient.validateSession('sessionToken', { audience: ['a', 'b'] });

// Audience is enforced for session JWTs only (including after refresh)
const refreshedWithAud = await descopeClient.refreshSession('refreshToken', { audience: 'api' });
const combinedWithAud = await descopeClient.validateAndRefreshSession(
  'sessionToken',
  'refreshToken',
  { audience: 'api' },
);

For access keys, you can also validate the returned session against an audience:

const authInfo = await descopeClient.exchangeAccessKey('access_key', undefined, {
  audience: 'api',
});

Choose the right session validation and refresh combination that suits your needs. Refreshed sessions return the same response as is returned when users first sign up / log in, containing the session and refresh tokens, as well as all of the JWT claims. Make sure to return the session token from the response to the client if tokens are validated directly.

Usually, the tokens can be passed in and out via HTTP headers or via a cookie. The implementation can defer according to your implementation. See our examples for a few examples.

If Roles & Permissions are used, validate them immediately after validating the session. See the next section for more information.

Note: if refresh token rotation is enabled in Descope - refreshSession / validateAndRefreshSession will return a new refresh token, and the old one will be invalidated.

DPoP Sender-Constrained Tokens (RFC 9449)

When a session token is DPoP-bound (has a cnf.jkt claim), pass the request's DPoP details to validateSession or validateAndRefreshSession. When the dpop option is passed, a missing or invalid proof is rejected. Without it, a DPoP-bound token logs a warning and is accepted; the next major version will reject it. Unbound tokens are not affected. validateAndRefreshSession checks the proof after refreshing, so a rejected proof does not undo a refresh token rotation.

const [authorizationScheme, sessionToken] = (req.headers.authorization ?? '').split(' ');

const authInfo = await descopeClient.validateSession(sessionToken, {
  dpop: {
    proof: req.headers['dpop'] as string | undefined, // value of the DPoP header
    method: req.method,
    url: `${req.protocol}://${req.get('host')}${req.path}`, // must match the proof's htu
    authorizationScheme, // "Bearer" or "DPoP"
  },
});

Behind a proxy: url must match the proof's htu (scheme, host, path). Node.js may see http when the client used https, so enable Express trust proxy or rebuild the URL yourself.

Replay protection: a proof whose jti was accepted by the same SDK instance in the last 120 seconds is rejected. The store is in memory (max 100,000 entries, new proofs are rejected when full), so replays across server instances are not detected.

Session Validation Using Middleware

Alternatively, you can create a simple middleware function that internally uses the validateSession function. This middleware will automatically parse the cookies from the request. On failure, it will respond with 401 Unauthorized.

const authMiddleware = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const cookies = parseCookies(req);
    const out = await clientAuth.auth.validateSession(
      cookies[DescopeClient.SessionTokenCookieName],
      cookies[DescopeClient.RefreshTokenCookieName],
    );
    if (out?.cookies) {
      res.set('Set-Cookie', out.cookies);
    }
    next();
  } catch (e) {
    res.status(401).json({
      error: 'Unauthorized!',
    });
  }
};

Roles & Permission Validation

When using Roles & Permission, it's important to validate the user has the required authorization immediately after making sure the session is valid. Taking the AuthenticationInfo received by the session validation, call the following functions:

For multi-tenant uses:

// You can validate specific permissions
const validTenantPermissions = descopeClient.validateTenantPermissions(authInfo, 'my-tenant-ID', [
  'Permission to validate',
]);
if (!validTenantPermissions) {
  // Deny access
}

// Or validate roles directly
const validTenantRoles = descopeClient.validateTenantRoles(authInfo, 'my-tenant-ID', [
  'Role to validate',
]);
if (!validTenantRoles) {
  // Deny access
}

// Or get the matched roles/permissions
const matchedTenantRoles = descopeClient.getMatchedTenantRoles(authInfo, 'my-tenant-ID', [
  'Role to validate',
  'Another role to validate',
]);

const matchedTenantPermissions = descopeClient.getMatchedTenantPermissions(
  authInfo,
  'my-tenant-ID',
  ['Permission to validate', 'Another permission to validate'],
);

When not using tenants use:

// You can validate specific permissions
const validPermissions = descopeClient.validatePermissions(authInfo, ['Permission to validate']);
if (!validPermissions) {
  // Deny access
}

// Or validate roles directly
const validRoles = descopeClient.validateRoles(authInfo, ['Role to validate']);
if (!validRoles) {
  // Deny access
}

// Or get the matched roles/permissions
const matchedRoles = descopeClient.getMatchedRoles(authInfo, [
  'Role to validate',
  'Another role to validate',
]);

const matchedPermissions = descopeClient.getMatchedPermissions(authInfo, [
  'Permission to validate',
  'Another permission to validate',
]);

Logging Out

You can log out a user from an active session by providing their refreshToken for that session. After calling this function, you must invalidate or remove any cookies you have created.

await descopeClient.logout(refreshToken);

It is also possible to sign the user out of all the devices they are currently signed-in with. Calling logoutAll will invalidate all user's refresh tokens. After calling this function, you must invalidate or remove any cookies you have created.

await descopeClient.logoutAll(refreshToken);

Management Functions

It is very common for some form of management or automation to be required. These can be performed using the management functions. Please note that these actions are more sensitive as they are administrative in nature. Please use responsibly.

Setup

To use the management API you'll need a Management Key along with your Project ID. Create one in the Descope Console.

import DescopeClient from '@descope/node-sdk';

const descopeClient = DescopeClient({
  projectId: 'my-project-ID',
  managementKey: 'management-key',
});

Manage Tenants

You can create, update, delete or load tenants, as well as read and update tenant settings:

// The self provisioning domains or optional. If given they'll be used to associate
// Users logging in to this tenant
await descopeClient.management.tenant.create('My Tenant', ['domain.com'], {
  customAttributeName: 'val',
});

// You can optionally set your own ID when creating a tenant
await descopeClient.management.tenant.createWithId('my-custom-id', 'My Tenant', ['domain.com'], {
  customAttributeName: 'val',
});

// Update will override all fields as is. Use carefully.
await descopeClient.management.tenant.update(
  'my-custom-id',
  'My Tenant',
  ['domain.com', 'another-domain.com'],
  { customAttributeName: 'val' },
);

// Unlike update, patchTenant only changes the fields you provide - everything else
// (domains, customAttributes, enforceSSO, ...) is left untouched.
await descopeClient.management.tenant.patchTenant('my-custom-id', {
  name: 'My Tenant',
  disabled: true,
});

// Update the tenant's default roles by providing role names.
// These are project-level roles that will be automatically assigned to users in this tenant.
await descopeClient.management.tenant.updateDefaultRoles('my-custom-id', ['role1', 'role2']);

// Tenant deletion cannot be undone. Use carefully.
// Pass true to cascade value, in case you want to delete all users/keys associated only with this tenant
await descopeClient.management.tenant.delete('my-custom-id', false);

// Load tenant by id
const tenant = await descopeClient.management.tenant.load('my-custom-id');

// Load all tenants
const tenantsRes = await descopeClient.management.tenant.loadAll();
tenantsRes.data.forEach((tenant) => {
  // do something
});

// Search all tenants according to various parameters
const searchRes = await descopeClient.management.tenant.searchAll(['id']);
searchRes.data.forEach((tenant) => {
  // do something
});

// Load tenant settings by id
const tenantSettings = await descopeClient.management.tenant.getSettings('my-tenant-id');

// Update will override all fields as is. Use carefully.
await descopeClient.management.tenant.configureSettings('my-tenant-id', {
  domains: ['domain1.com'],
  selfProvisioningDomains: ['domain1.com'],
  enabled: true,
  refreshTokenExpiration: 12,
  refreshTokenExpirationUnit: 'days',
  sessionTokenExpiration: 10,
  sessionTokenExpirationUnit: 'minutes',
  enableInactivity: true,
  JITDisabled: false,
  inactivityTime: 10,
  inactivityTimeUnit: 'minutes',
});

// Generate tenant admin self service link for SSO Suite (valid for 24 hours)
// ssoId can be provided for a specific sso configuration
// email can be provided to send the link to (email's templateId can be provided as well)
const res = await descopeClient.management.tenant.generateSSOConfigurationLink(
  'my-tenant-id',
  60 * 60 * 24,
);
console.log(res.adminSSOConfigurationLink);

// Optionally set an actor id, recorded as the audit actor for actions taken inside the SSO
// Suite (instead of the temporary user). It is used as-is for audit attribution and is not validated.
const resWithActor = await descopeClient.management.tenant.generateSSOConfigurationLink(
  'my-tenant-id',
  60 * 60 * 24,
  undefined, // ssoId
  undefined, // email
  undefined, // templateId
  'my-admin-actor-id', // actorId
);
console.log(resWithActor.adminSSOConfigurationLink);

Manage Families

You can create, update, delete or search families, manage the users linked to them, and impersonate a family dependent (a shadow-profile user with no login credentials of their own):

// Create a family. The family ID is generated automatically.
const family = await descopeClient.management.family.create('My Family', {
  customAttributeName: 'val',
});

// Create a family with a caller-supplied ID.
await descopeClient.management.family.createWithId('my-family-id', 'My Other Family');

// Update will override all provided fields as is. Omitted fields are left unchanged.
// customAttributes replaces all of the family's custom attributes, it is not merged.
await descopeClient.management.family.update(family.data.id, 'My Family', {
  customAttributeName: 'val2',
});

// Family deletion cannot be undone. Use carefully.
await descopeClient.management.family.delete(family.data.id);

// Search families according to various parameters. Called with no options, returns all families.
const searchRes = await descopeClient.management.family.searchAll({ text: 'My Family' });
searchRes.data.forEach((f) => {
  // do something
});

// Create a dependent (shadow profile) user in a family - a user with no login credentials of
// their own. When loginId is omitted it is derived from name; email/phone are never used as the
// login ID since they are not unique - a dependent may share them with their guardian.
const dependent = await descopeClient.management.family.createDependent(family.data.id, {
  name: 'My Dependent',
});

// Dependent deletion cannot be undone. The family is inferred from the dependent. Regular
// (non-dependent) family members are removed via user.removeFamilies, not deleted.
await descopeClient.management.family.deleteDependent(dependent.data.userId);

// Add a user to one or more families. Each entry may also set the user's roles and family-scoped
// attributes for that family in the same call; omitting roleNames or familyScopedAttributes on a
// family the user already belongs to leaves them unchanged.
await descopeClient.management.user.addFamilies('user-login-id', [
  { familyId: family.data.id, roleNames: ['role1'] },
]);

// Remove a user from one or more families.
await descopeClient.management.user.removeFamilies('user-login-id', [family.data.id]);

// Impersonate a family dependent. The impersonator (by user ID or login ID) must be a member of
// the dependent's family and hold the family impersonate-dependents permission there.
const impersonateRes = await descopeClient.management.family.impersonateDependent(
  'admin-user-id',
  'dependent-login-id',
  family.data.id, // optional - scopes the impersonated session to this family
);
console.log(impersonateRes.data.jwt);

// Stop impersonating a family dependent and return to the acting admin's own session.
const stopRes = await descopeClient.management.family.stopImpersonation(impersonateRes.data.jwt);
console.log(stopRes.data.jwt);

// Get and configure the project's family account settings. Omitted fields are left unchanged.
const settings = await descopeClient.management.family.getSettings();
await descopeClient.management.family.configureSettings({
  enabled: true,
  maxFamilyMembers: 5,
  allowMultipleFamiliesUsers: false,
});

// Manage custom attribute definitions on the family entity itself.
await descopeClient.management.family.createCustomAttributes([{ name: 'plan', type: 1 }]);
const familyAttrs = await descopeClient.management.family.getCustomAttributes();
await descopeClient.management.family.deleteCustomAttributes(['plan']);

// Manage family-scoped user custom attribute definitions - user attributes whose values are held
// per family membership (AssociatedFamily.familyScopedAttributes) rather than on the user.
await descopeClient.management.user.createFamilyScopedCustomAttributes([
  { name: 'nickname', type: 1 },
]);
const familyScopedAttrs = await descopeClient.management.user.getFamilyScopedCustomAttributes();
await descopeClient.management.user.deleteFamilyScopedCustomAttributes(['nickname']);

Manage Password

You can read and update any tenant password settings and policy:

// Load tenant password settings by id
const passwordSettings = await descopeClient.management.password.getSettings('my-tenant-id');

// Update will override all fields as is. Use carefully.
await descopeClient.management.password.configureSettings('my-tenant-id', {
  enabled: true,
  minLength: 8,
  expiration: true,
  expirationWeeks: 4,
  lock: true,
  lockAttempts: 5,
  reuse: true,
  reuseAmount: 6,
  lowercase: true,
  uppercase: false,
  number: true,
  nonAlphaNumeric: false,
});

Manage SSO applications

You can create, update, delete or load SSO applications:

// Create OIDC SSO application
await descopeClient.management.ssoApplication.createOidcApplication({
  name: 'My OIDC app name',
  loginPageUrl: 'http://dummy.com/login',
  clientType: 'confidential', // 'confidential' or 'public'; if omitted, the account's default is used (see oidcSettings.clientType on load)
  // Optional: send the app's back-channel logout URL a logout token when the user is force logged out
  backChannelLogoutUrl: 'https://dummy.com/backchannel-logout',
  notifyOnForceLogout: true,
});

// Create SAML SSO application
await descopeClient.management.ssoApplication.createSamlApplication({
  name: 'My SAML app name',
  loginPageUrl: 'http://dummy.com/login',
  useMetadataInfo: true,
  metadataUrl: 'http://dummy.com/metadata',
  // Optional SAML Single Logout (SLO): the service provider can log the user out with a signed
  // LogoutRequest, and it is sent one when a logout reaches the user's applications
  singleLogoutEnabled: true,
  singleLogoutPropagation: 'allApps', // 'none' (default) or 'allApps'
  singleLogoutScope: 'session', // 'session' or 'allSessions'
  notifyOnForceLogout: true, // also send it a LogoutRequest on a force logout (requires the 'soap' binding)
});

// Create WS-Fed SSO application
await descopeClient.management.ssoApplication.createWsFedApplication({
  name: 'My WS-Fed app name',
  loginPageUrl: 'http://dummy.com/login',
  realm: 'urn:myapp:realm',
  replyUrl: 'http://dummy.com/reply',
});

// Update OIDC SSO application.
// Update will override all fields as is. Use carefully.
await descopeClient.management.ssoApplication.updateOidcApplication({
  id: 'my-app-id',
  name: 'My OIDC app name',
  loginPageUrl: 'http://dummy.com/login',
  clientType: 'confidential', // pass the app's current client type
  backChannelLogoutUrl: 'https://dummy.com/backchannel-logout', // an update without it clears it
});

// Update SAML SSO application.
// Update will override all fields as is. Use carefully.
await descopeClient.management.ssoApplication.updateSamlApplication({
  id: 'my-app-id',
  name: 'My SAML app name',
  loginPageUrl: 'http://dummy.com/login',
  enabled: true,
  useMetadataInfo: false,
  entityId: 'entity1234',
  aceUrl: 'http://dummy.com/acs',
  certificate: 'certificate',
});

// Update WS-Fed SSO application.
// Update will override all fields as is. Use carefully.
await descopeClient.management.ssoApplication.updateWsFedApplication({
  id: 'my-app-id',
  name: 'My WS-Fed app name',
  loginPageUrl: 'http://dummy.com/login',
  enabled: true,
  realm: 'urn:myapp:realm',
  replyUrl: 'http://dummy.com/reply',
});

// SSO application deletion cannot be undone. Use carefully.
await descopeClient.management.ssoApplication.delete('my-app-id');

// Load SSO application by id
const app = await descopeClient.management.ssoApplication.load('my-app-id');

// Load all SSO applications
const appsRes = await descopeClient.management.ssoApplication.loadAll();
appsRes.data.forEach((app) => {
  // do something
});

Manage Users

You can create, update, delete or load users, as well as search according to filters:

// A user must have a login ID, other fields are optional.
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
await descopeClient.management.user.create('[email protected]', {
  email: '[email protected]',
  displayName: 'Desmond Copeland',
  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
});

// Alternatively, a user can be created and invited via an email / text message.
// Make sure to configure the invite URL in the Descope console prior to using this function,
// and that an email address / phone number is provided in the information.
await descopeClient.management.user.invite('[email protected]', {
  email: '[email protected]',
  displayName: 'Desmond Copeland',
  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
  // You can override the project's User Invitation Redirect URL with this parameter
  inviteUrl: '<invite-url>',
  // You can inject custom data into the template.
  // Note that you first need to configure custom template in Descope Console
  // For example: configure {{options_k1}} in the custom template, and pass { k1: 'v1' } as templateOptions
  templateOptions: { k1: 'v1', k2: 'v2' },
});

// You can invite batch of users via an email / text message.
// Make sure to configure the invite URL in the Descope console prior to using this function,
// and that an email address / phone number is provided in the information. You can also set
// a cleartext password or import a prehashed one from another service.
// Note: This function will send an invitation to each user in the `users` array. If you want to create users without sending invitations, use `createBatch` instead.
await descopeClient.management.user.inviteBatch(
  [
    {
      loginId: '[email protected]',
      email: '[email protected]',
      phone: '+123456789123',
      displayName: 'Desmond Copeland',
      userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
      hashedPassword: {
        bcrypt: {
          hash: '$2a$...',
        },
      },
    },
  ],
  '<invite_url>',
  true,
  false,
);

// Create a batch of users.
// This is useful when you want to create users programmatically without triggering the invitation flow.
// You can set a cleartext password or import a prehashed one from another service.
// Note: This function will NOT send an invitation to the created users. If invitations are required use `inviteBatch` instead.
await descopeClient.management.user.createBatch([
  {
    loginId: '[email protected]',
    email: '[email protected]',
    phone: '+123456789123',
    displayName: 'Desmond Copeland',
    userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
    hashedPassword: {
      bcrypt: {
        hash: '$2a$...',
      },
    },
  },
]);

// Update will override all fields as is, including family memberships: omitting
// familyAssociations removes the user from all of their families, and updating a dependent
// requires familyAssociations with the dependent's family. Use carefully, or use patch instead.
await descopeClient.management.user.update('[email protected]', {
  email: '[email protected]',
  displayName: 'Desmond Copeland',
  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
});

// Update explicit data for a user rather than overriding all fields
await descopeClient.management.user.updatePhone('[email protected]', '+18005551234', true);
await descopeClient.management.user.updateLoginId('[email protected]', '[email protected]');
await descopeClient.management.user.removeTenantRoles(
  '[email protected]',
  'tenant-ID1',
  'role-name2',
);

// Update explicit user's data using patch (will override only provided fields)
const options: PatchUserOptions = { displayName: 'Desmond Copeland Jr.' };
await descopeClient.management.user.patch('[email protected]', options);

// User deletion cannot be undone. Use carefully.
await descopeClient.management.user.delete('[email protected]');
// Delete a batch of users. This requires Descope user IDs.
await descopeClient.management.user.deleteBatch(['<user-ID-1>', '<user-ID-2>']);

// Load specific user
const userRes = await descopeClient.management.user.load('[email protected]');

// If needed, users can be loaded using the user ID as well
const userRes = await descopeClient.management.user.loadByUserId('<user-ID>');

// loadUsers - load users by their user id, optionally you can decide if to return invalid users
const usersRes = await descopeClient.management.user.loadUsers(['<user-ID>']);
usersRes.data.forEach((user) => {
  // do something
});

// Search all users, optionally according to tenant and/or role filter
// Results can be paginated using the limit and page parameters
// Additional filters: verifiedEmail, verifiedPhone, statuses, roles, tenantIds, etc.
const usersRes = await descopeClient.management.user.search({
  tenantIds: ['tenant-ID'],
  verifiedEmail: true, // optional: filter by verified email status
  verifiedPhone: false, // optional: filter by verified phone status
});
console.log('Total users:', usersRes.data.total);
usersRes.data.users.forEach((user) => {
  // do something
});

await descopeClient.management.user.logoutUser('my-custom-id');

await descopeClient.management.user.logoutUserByUserId('<user-ID>');

// Logout sessions by creation time. Each matching session is logged out on its next refresh or validation.
// At least one of `before` or `after` is required. `before` defaults to the server's current time; prefer
// omitting it over passing new Date(), since a `before` in the future is rejected. Optionally limit to
// specific session types with `sessionTypes`. The range stays active for a year. Deleting it stops it from
// logging out matching sessions it hasn't logged out yet; sessions already logged out stay logged out.
// The returned range is what the server stored, which may have a later `after` than requested.
// For example, log out sessions created in the last hour:
const rangeRes = await descopeClient.management.user.logoutSessionsCreatedBetween({
  after: new Date(Date.now() - 3600 * 1000),
});
const rangesRes = await descopeClient.management.user.listLogoutSessionRanges();
await descopeClient.management.user.deleteLogoutSessionRange(rangeRes.data.id);

// Get users' authentication history
const userIds = ['user-id-1', 'user-id-2'];
const usersHistoryRes = await descopeClient.management.user.history(userIds);
usersHistoryRes.forEach((userHistory) => {
  // do something
});

Set or Expire User Password

You can set a new active password for a user that they can sign in with. You can also set a temporary password that they user will be forced to change on the next login. For a user that already has an active password, you can expire their current password, effectively requiring them to change it on the next login.

// Set a user's temporary password
await descopeClient.management.user.setTemporaryPassword('<login-ID>', '<some-password>');

// Set a user's password
await descopeClient.management.user.setActivePassword('<login-ID>', '<some-password>');

// Or alternatively, expire a user password
await descopeClient.management.user.expirePassword('<login-ID>');

Manage Project

You can update project name and tags, as well as clone the current project to a new one:

// Update will override all fields as is. Use carefully.
await descopeClient.management.project.updateName('new-project-name');

// Set will override all fields as is. Use carefully.
await descopeClient.management.project.updateTags(['tag1!', 'new']);

// Clone the current project to a new one
// Note that this action is supported only with a pro license or above.
const cloneRes = await descopeClient.management.project.clone('new-project-name');

With using a company management key you can get a list of all the projects in the company:

const projects = await descopeClient.management.project.listProjects();

You can manage your project's settings and configurations by exporting a snapshot:

// Exports the current state of the project
const exportRes = await descopeClient.management.project.exportSnapshot();

You can also import previously exported snapshots into the same project or a different one:

const validateReq = {
  files: exportRes.data.files,
};

// Validate that an exported snapshot can be imported into the current project
const validateRes = await descopeClient.management.project.import(files);
if (!validateRes.ok) {
  // validation failed, check failures and missingSecrets to fix this
}

// Import the previously exported snapshot into the current project
const importReq = {
  files: exportRes.data.files,
};

await descopeClient.management.project.importSnapshot(files);

Manage Access Keys

You can create, update, delete or load access keys, as well as search according to filters:

// An access key must have a name and expiration, other fields are optional.
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
// If userId is supplied, then authorization will be ignored, and the access key will be bound to the user's authorization.
// If customClaims is supplied, then those claims will be present in the JWT returned by calls to ExchangeAccessKey.
// If description is supplied, then the access key will hold a descriptive text.
// If permittedIps is supplied, then the access key can only be used from that list of IP addresses or CIDR ranges.
await descopeClient.management.accessKey.create(
  'key-name',
  123456789, // expiration time
  null,
  [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
  undefined, // userId
  undefined, // customClaims
  undefined, // description
  undefined, // permittedIps
  { attributeName: 'attributeValue' }, // customAttributes
);

// Load specific user
const accessKeyRes = await descopeClient.management.accessKey.load('key-id');

// Search all users, optionally according to tenant and/or role filter
const accessKeysRes = await descopeClient.management.accessKey.searchAll(['tenant-ID']);
accessKeysRes.data.forEach((accessKey) => {
  // do something
});

// Update will override all fields as is. Use carefully.
await descopeClient.management.accessKey.update('key-id', 'new-key-name', 'new-description');

// Access keys can be deactivated to prevent usage. This can be undone using "activate".
await descopeClient.management.accessKey.deactivate('key-id');

// Disabled access keys can be activated once again.
await descopeClient.management.accessKey.activate('key-id');

// Access key deletion cannot be undone. Use carefully.
await descopeClient.management.accessKey.delete('key-id');

Manage SSO Setting

You can manage SSO settings and map SSO group roles and user attributes.

// You can get SSO settings for a specific tenant ID
// You can pass ssoId in case using multi SSO and you want to load specific SSO configuration
const ssoSettings = await descopeClient.management.sso.loadSettings('tenant-id');

// You can get all configured SSO settings for a specific tenant ID (for multi SSO usage)
const allSSOSettings = await descopeClient.management.sso.loadAllSettings('tenant-id');

// You can configure SSO settings manually by setting the required fields directly
// You can pass ssoId in case using multi SSO and you want to configure specific SSO configuration
const tenantId = 'tenant-id'; // Which tenant this configuration is for
const idpUrl = 'https://idp.com';
const entityId = 'my-idp-entity-id';
const idpCert = '<your-cert-here>';
const redirectURL = 'https://my-app.com/handle-sso'; // Global redirect URL for SSO/SAML
const domains = ['tenant-users.com']; // Users authentication with this domain will be logged in to this tenant
await descopeClient.management.sso.configureSAMLSettings(
  tenantID,
  { idpUrl, entityId, idpCert },
  redirectURL,
  domains,
);

// Alternatively, configure using an SSO metadata URL
// You can pass ssoId in case using multi SSO and you want to configure specific SSO configuration
await descopeClient.management.sso.configureSAMLByMetadata(
  tenantID,
  { idpMetadataUrl: 'https://idp.com/my-idp-metadata' },
  redirectURL,
  domains,
);

// Descope signs the SAML AuthnRequest it sends to the IdP. A few IdPs reject a signed request because
// their trusted provider entry holds no signing certificate for Descope - pass disableSignRequest on the
// settings (available on both variants above) to send the request unsigned for that configuration only.
await descopeClient.management.sso.configureSAMLSettings(
  tenantID,
  { idpUrl, entityId, idpCert, disableSignRequest: true },
  redirectURL,
  domains,
);

// A configuration can be classified as authentication only: a login through it verifies the person's
// identity and returns the IdP response, creating no user and issuing no session, so it grants no
// access to your application. Omit authenticationOnly and the stored classification is kept, so an
// ordinary settings edit cannot clear it by omission; pass false to clear it. The same field is on the
// OIDC and Cross-App Access settings, and setting it through any one of them classifies the whole
// configuration. Read it back from the load response's top-level authenticationOnly.
await descopeClient.management.sso.configureSAMLSettings(
  tenantID,
  { idpUrl, entityId, idpCert, authenticationOnly: true },
  redirectURL,
  domains,
);

// JIT provisioning can be turned off per SSO configuration: a login through it then signs in only
// users who already exist (invited, or provisioned through SCIM) and refuses anyone else. It applies to
// the configuration named by ssoId, or to the tenant's default configuration when ssoId is omitted.
// Omit jitDisabled to keep the stored value; pass false to turn JIT back on. The same field is on the
// OIDC settings. Read it back from the load response's top-level jitDisabled.
await descopeClient.management.sso.configureSAMLSettings(
  tenantID,
  { idpUrl, entityId, idpCert, jitDisabled: true },
  redirectURL,
  domains,
  'my-sso-id', // ssoId: omit for the tenant's default SSO configuration
);

// In case SSO is configured to work with OIDC use the following
// You can pass ssoId in case using multi SSO and you want to configure specific SSO configuration
const name = 'some-name';
const clientId = 'client id of OIDC';
const clientSecret = 'client secret';
// usePkce sends a PKCE code challenge to the IdP; omit it to keep the stored value
await descopeClient.management.sso.configureOIDCSettings(
  tenantID,
  { name, clientId, clientSecret, redirectUrl, usePkce: true },
  domains,
);

// You can create new SSO configuration (aka multi SSO)
const ssoId = 'my-new-additional-sso-id';
const displayName = 'My additional SSO configuration';
await descopeClient.management.sso.newSettings(tenantID, ssoId, displayName);

// You can delete existing SSO configuration
// You can pass ssoId in case using multi SSO and you want to delete specific SSO configuration
await descopeClient.management.sso.deleteSettings(tenantID);

// You can disable an SSO configuration without deleting it, and enable it again later.
// Its settings, mappings and domains are kept, so re-enabling needs no payload.
// You can pass ssoId in case using multi SSO and you want to disable a specific SSO configuration
await descopeClient.management.sso.configureAuthType(tenantID, 'none', ssoId);
// Enable it again on the protocol it is configured for ('saml' or 'oidc')
await descopeClient.management.sso.configureAuthType(tenantID, 'saml', ssoId);

Note: Certificates should have a similar structure to:

-----BEGIN CERTIFICATE-----
Certifcate contents
-----END CERTIFICATE-----

// You can delete SSO settings for a specific tenant ID await descopeClient.management.sso.deleteSettings("tenant-id")

Manage Permissions

You can create, update, delete or load permissions:

// You can optionally set a description for a permission.
const name = 'My Permission';
let description = 'Optional description to briefly explain what this permission allows.';
await descopeClient.management.permission.create(name, description);

// Update will override all fields as is. Use carefully.
const newName = 'My Updated Permission';
description = 'A revised description';
await descopeClient.management.permission.update(name, newName, description);

// Permission deletion cannot be undone. Use carefully.
await descopeClient.management.permission.delete(newName);

// Load all permissions
const permissionsRes = await descopeClient.management.permission.loadAll();
permissionsRes.data.forEach((permission) => {
  // do something
});

Manage Roles

You can create, update, delete or load roles:

// You can optionally set a description and associated permission for a roles.
// The optional `tenantId` will scope this role for a specific tenant. If left empty, the role will be available to all tenants.
const name = 'My Role';
const tenantId = '<tenant id>';
let description = 'Optional description to briefly explain what this role allows.';
const permissionNames = ['My Updated Permission'];
descopeClient.management.role.create(name, description, permissionNames, tenantId);

// Update will override all fields as is. Use carefully.
const newName = 'My Updated Role';
description = 'A revised description';
permissionNames.push('Another Permission');
descopeClient.management.role.update(name, newName, description, permissionNames, tenantId);

// Role deletion cannot be undone. Use carefully.
descopeClient.management.role.delete(newName, tenantId);

// Load all roles
const rolesRes = await descopeClient.management.role.loadAll();
rolesRes.data.forEach((role) => {
  // do something
});

// Search roles
const rolesRes = await descopeClient.management.role.search({
  tenantIds: ['t1', 't2'],
  roleNames: ['role1'],
});
rolesRes.data.forEach((role) => {
  // do something
});

Query SSO Groups

You can query SSO groups:

// Load all groups for a given tenant id
const groupsRes = descopeClient.management.group.loadAllGroups('tenant-id');

// Load all groups for the given user IDs (can be found in the user's JWT)
const groupsRes = descopeClient.management.group.loadAllGroupsForMember('tenant-id', [
  'user-id-1',
  'user-id-2',
]);

// Load all groups for the given user login IDs (used for sign-in)
const groupsRes = descopeClient.management.group.loadAllGroupsForMember(
  'tenant-id',
  [],
  ['login-id-1', 'login-id-2'],
);

// Load all group's members by the given group id
const groupsRes = descopeClient.management.group.loadAllGroupMembers('tenant-id', 'group-id');

groupsRes.data.forEach((group) => {
  // do something
});

Manage Flows

You can list your flows and also import and export flows and screens, or the project theme:

// List all project flows
const res = await descopeClient.management.flow.list();
console.log('found total flows', res.total);
res.flows.forEach((flowMetadata) => {
  // do something
});

// Delete flows by ids
await descopeClient.management.flow.delete(['flow-1', 'flow-2']);

// Export the flow and it's matching screens based on the given id
const res = await descopeClient.management.flow.export('sign-up');
console.log('found flow', res.data.flow);
res.data.screens.forEach((screen) => {
  // do something
});

// Import the given flow and screens as the given id
const { flow, screens } = res.data;
const updatedRes = descopeClient.management.flow.import('sign-up', flow, screens);
console.log('updated flow', updatedRes.data.flow);
updatedRes.data.screens.forEach((screen) => {
  // do something
});

// Run a management Flow
// Note: Flow must be a management flow, not an interactive flow
const runRes = await descopeClient.management.flow.run('management-flow-id');
console.log('flow result', runRes.data); // The result data will contain the flow's output, which is configured in the 'End' step of the flow

// Run a management Flow with input
// Note: Flow must be a management flow, not an interactive flow
const runWithInputRes = await descopeClient.management.flow.run('management-flow-id', {
  input: {
    key1: 'value1',
  },
});
console.log('flow with input result', runWithInputRes.data); // The result data will contain the flow's output, which is configured in the 'End' step of the flow

// Export the current theme of the project
const res = descopeClient.management.theme.export();
console.log(res.data.theme);

// Import the given theme to the project
const updatedRes = descopeClient.management.theme.import(theme);
console.log(updatedRes.data.theme);

Manage JWTs

You can add custom claims to a valid JWT.

const updatedJWTRes = await descopeClient.management.jwt.update('original-jwt', {
  customKey1: 'custom-value1',
  customKey2: 'custom-value2',
});

Generate a JWT for a user, simulating a sign in request.

const res = await descopeClient.management.jwt.signIn('dummy');

Generate a JWT for a user, simulating a signup request.

const res = await descopeClient.management.jwt.signUp('dummy');

Generate a JWT for a user, simulating a signup or in request.

const res = await descopeClient.management.jwt.signUpOrIn('dummy');

Generate a client assertion JWT for OAuth flows.

const clientAssertionRes = await descopeClient.management.jwt.generateClientAssertionJwt(
  'https://example.com/issuer', // issuer
  'client-id-123', // subject
  ['https://example.com/token'], // audience
  300, // expiresIn - number of seconds the token will will be valid for
  false, // Optional. flattenAudience - set the audience claim as one string instead of array of strings (for case only one audience value has given)
  'RS256', // Optional. algorithm - set the signing algorithm, value should be one of 'RS256', 'RS384', 'ES384' (default is RS256)
);
// clientAssertionRes.data.jwt contains the client assertion JWT

Impersonate

You can impersonate to another user The impersonator user must have the impersonation permission in order for this request to work. The response would be a refresh JWT of the impersonated user

const updatedJWTRes = await descopeClient.management.jwt.impersonate(
  'impersonator-id',
  'login-id',
  true,
  { k1: 'v1' },
  't1',
);

Once impersonation is done, you can call stopImpersonation, and get back a jwt of hte the actor

const updatedJWTRes = await descopeClient.management.jwt.impersonate(
  '<jwt string>',
  { k1: 'v1' },
  't1',
);

Note 1: The generate code/link functions, work only for test users, will not work for regular users. Note 2: In case of testing sign-in / sign-up operations with test users, need to make sure to generate the code prior calling the sign-in / sign-up operations.

Embedded links can be created to directly receive a verifiable token without sending it. This token can then be verified using the magic link 'verify' function, either directly or through a flow.

const { token } = await descopeClient.management.user.generateEmbeddedLink('[email protected]', {
  key1: 'value1',
});

Audit

You can perform an audit search for either specific values or full-text across the fields. Audit search is limited to the last 30 days.

// Full text search on the last 10 days
const audits = await descopeClient.management.audit.search({
  from: Date.now() - 10 * 24 * 60 * 60 * 1000,
  text: 'some-text',
});
console.log(audits);

// Search successful logins in the last 30 days
const audits = await descopeClient.management.audit.search({ actions: ['LoginSucceed'] });
console.log(audits);

You can also create audit event with data

await descopeClient.management.audit.createEvent({
  action: 'pencil.created',
  type: 'info', // info/warn/error
  actorId: 'UXXX',
  tenantId: 'tenant-id',
  data: {
    some: 'data',
  },
});

Manage FGA (Fine-grained Authorization)

Descope supports full relation based access control (ReBAC) using a zanzibar like schema and operations. A schema is comprised of types (entities like documents, folders, orgs, etc.) and each type has relation definitions and permission to define relations to other types.

A simple example for a file system like schema would be:

model AuthZ 1.0

type user

type org
  relation member: user
  relation parent: org

type folder
  relation parent: folder
  relation owner: user | org#member
  relation editor: user
  relation viewer: user

  permission can_create: owner | parent.owner
  permission can_edit: editor | can_create
  permission can_view: viewer | can_edit

type doc
  relation parent: folder
  relation owner: user | org#member
  relation editor: user
  relation viewer: user

  permission can_create: owner | parent.owner
  permission can_edit: editor | can_create
  permission can_view: viewer | can_edit

Descope SDK allows you to fully manage the schema and relations as well as perform simple (and not so simple) checks regarding the existence of relations.

const descopeClient = require('@descope/node-sdk');

// Save schema
await descopeClient.management.fga.saveSchema(schema);

// Create a relation between a resource and user
await descopeClient.management.fga.createRelations([
  {
    resource: 'some-doc',
    resourceType: 'doc',
    relation: 'owner',
    target: 'u1',
    targetType: 'user',
  },
]);

// Check if target has a relevant relation
// The answer should be true because an owner can also view
const relations = await descopeClient.management.fga.check([
  {
    resource: 'some-doc',
    resourceType: 'doc',
    relation: 'can_view',
    target: 'u1',
    targetType: 'user',
  },
]);

Response times of repeated FGA check calls, especially in high volume scenarios, can be reduced to sub-millisecond scales by re-directing the calls to a Descope FGA Cache Proxy running in the same backend cluster as your application.

After setting up the proxy server via the Descope provided Docker image, set the fgaCacheUrl parameter to be equal to the proxy URL to enable its use in the SDK, as shown in the example below:

Note: Both fgaCacheUrl and managementKey must be provided for the cache proxy to be used. If only fgaCacheUrl is configured without managementKey, requests will use the standard Descope API.

import DescopeClient from '@descope/node-sdk';

// Initialize client with FGA cache URL
const descopeClient = DescopeClient({
  projectId: '<Project ID>',
  managementKey: '<Management Key>', // Required for cache proxy
  fgaCacheUrl: 'https://10.0.0.4', // example FGA Cache Proxy URL, running inside the same backend cluster
});

When the fgaCacheUrl is configured, the following FGA methods will automatically use the cache proxy instead of the default Descope API:

  • saveSchema
  • createRelations
  • deleteRelations
  • check

If the cache proxy is unreachable or returns an error, the SDK will automatically fall back to the standard Descope API.

Other FGA operations like loadResourcesDetails and saveResourcesDetails will continue to use the standard Descope API endpoints.

Manage Outbound Applications

You can create, update, delete or load outbound applications:

// Create an outbound application.
// For DCR-based apps (e.g. custom MCP servers that support RFC 7591 dynamic
// client registration), pass `useDcr: true` and `dcrUrl`.
const { id } =
  await descopeClient.management.outboundApplication.createApplication({
    name: 'my new app',
    description: 'my desc',
    ...
  });

// Create an outbound application from a preconfigured app library template.
// The template (e.g. "hubspot", "google", "slack") prepopulates the provider's
// OAuth config - authorization/token endpoints, default scopes, pkce, etc.
// Any field set in `overrides` takes precedence over the template default.
const { id: templatedId } =
  await descopeClient.management.outboundApplication.createApplicationByTemplate({
    templateId: 'hubspot',
    clientId: 'my-client-id',
    clientSecret: 'my-client-secret',
    overrides: {
      name: 'HubSpot',
      // authorizationUrl / tokenUrl / defaultScopes / pkce inherited from the template
    },
  });

// Update an outbound application.
// Update will override all fields as is. Use carefully.
await descopeClient.management.outboundApplication.updateApplication({
  id: 'my-app-id',
  name: 'my updated app',
  ...
});

// delete an outbound appl

版本与发布记录

@descope/node-sdk 共发布 638 个版本,首次发布于 2022/08/16;过去 12 个月发布了 158 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.22.0latest2026/11/11今天
0.0.0-next-2852c2b6-20261111next2026/11/11今天
0.0.0-next-da8a32fe-202611112026/11/11今天
0.0.0-next-e1c4fd90-202611102026/11/111 天前
0.0.0-next-38359867-202611092026/11/102 天前
0.0.0-next-f4505cf6-202611092026/11/092 天前

关于 @descope/node-sdk 的常见问题

@descope/node-sdk 是什么?

Node.js library used to integrate with Descope

如何安装 @descope/node-sdk?

在项目目录运行 npm install @descope/node-sdk,或使用 pnpm add / yarn add / bun add。

@descope/node-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@descope/node-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@descope/node-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >= 18.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@descope/node-sdk 可以商用吗?

@descope/node-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@descope/node-sdk 的下载量有多少?

根据 npm 官方下载统计,@descope/node-sdk 在 2026/10/12 – 2026/11/10 这 30 天内被下载 1,877,989 次,日均约 62,600 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@descope/node-sdk 还在维护吗?

最新版本 2.22.0 发布于 2026/11/11(今天);至今共发布 638 个版本,其中过去 12 个月发布了 158 个;首次发布于 2022/08/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。