Skip to content

Repository files navigation

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 16 and above.

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.

importDescopeClientfrom'@descope/node-sdk';constdescopeClient=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.

importDescopeClientfrom'@descope/node-sdk';constdescopeClient=DescopeClient({projectId: 'my-project-ID',managementKey: 'management-key',});

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

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

importDescopeClientfrom'@descope/node-sdk';constauthClient=DescopeClient({projectId: 'my-project-ID',authManagementKey: 'auth-management-key',});// This client can be used for authentication-related management operations

Separate clients for different operations:

importDescopeClientfrom'@descope/node-sdk';// Client for general management operationsconstmanagementClient=DescopeClient({projectId: 'my-project-ID',managementKey: 'management-key',});// Client for authentication operationsconstauthClient=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:

importDescopeClient,{SdkResponse}from'@descope/node-sdk';const{ DescopeErrors }=DescopeClient;// ...try{constresp=awaitsdk.otp.signIn.email(loginId);if(resp.error){switch(resp.error.errorCode){caseDescopeErrors.userNotFound:
// Handle specificallybreak;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 optionalconstloginId='desmond@descope.com';constuser={name: 'Desmond Copland',phone: '212-555-1234',email: loginId,};awaitdescopeClient.otp.signUp['email'](loginId,user);

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

constjwtResponse=awaitdescopeClient.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

Magic Link

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 configurationconstURI='http://myapp.com/verify-magic-link';awaitdescopeClient.magicLink.signUpOrIn['email']('desmond@descope.com',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>):

constjwtResponse=awaitdescopeClient.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

Enchanted Link

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.constURI='http://myapp.com/verify-enchanted-link';constenchantedLinkRes=awaitdescopeClient.enchantedLink.signIn('desmond@descope.com',URI);enchantedLinkRes.data.linkId;// Should be displayed to the user so they can click the corresponding link in the emailenchantedLinkRes.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 WaitForSessionConfigconstjwtResponse=awaitdescopeClient.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{awaitdescopeClient.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.consturlRes=awaitdescopeClient.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:

constjwtResponse=awaitdescopeClient.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.constredirectUrl='https://my-app.com/handle-saml';consturlRes=awaitdescopeClient.saml.start('tenant');// Choose which tenant to log into. An email can also be provided here and the domain will be extracted from iturlRes.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:

constjwtResponse=awaitdescopeClient.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 optionalconstloginId='desmond@descope.com';constuser={name: 'Desmond Copland',phone: '212-555-1234',email: loginId,};consttotpRes=awaitdescopeClient.totp.signUp(loginId,user);// Use one of the provided options to have the user add their credentials to the authenticatortotpRes.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.

constjwtResponse=awaitdescopeClient.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.

constresponse=awaitdescopeClient.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.

constresponse=awaitdescopeClient.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 optionalconstloginId='desmond@descope.com';constpassword='qYlvi65KaX';constuser={name: 'Desmond Copeland',email: loginId,};constjwtResponse=awaitdescopeClient.password.signUp(loginId,password,user);// jwtResponse.data.sessionJwt;// jwtResponse.data.refreshJwt;

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

constjwtResponse=awaitdescopeClient.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.constloginId='desmond@descope.com';constredirectURL='https://myapp.com/password-reset';constpasswordResetResponse=awaitdescopeClient.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.awaitdescopeClient.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 oneconstjwtResponse=awaitdescopeClient.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 expiredconstauthInfo=awaitdescopeClient.validateSession('sessionToken');// If validateSession throws an exception, you will need to refresh the session usingconstrefreshed=awaitdescopeClient.refreshSession('refreshToken');// Alternatively, you could combine the two and// have the session validated and automatically refreshed when expiredconstcombined=awaitdescopeClient.validateAndRefreshSession('sessionToken','refreshToken');// Optional: Validate audience for backend-only flows// Provide VerifyOptions with the expected audience(s)constwithAud=awaitdescopeClient.validateSession('sessionToken',{audience: 'my-audience'});constwithAudArray=awaitdescopeClient.validateSession('sessionToken',{audience: ['a','b']});// Audience is enforced for session JWTs only (including after refresh)constrefreshedWithAud=awaitdescopeClient.refreshSession('refreshToken',{audience: 'api'});constcombinedWithAud=awaitdescopeClient.validateAndRefreshSession('sessionToken','refreshToken',{audience: 'api'},);

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

constauthInfo=awaitdescopeClient.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.

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.

constauthMiddleware=async(req: Request,res: Response,next: NextFunction)=>{try{constcookies=parseCookies(req);constout=awaitclientAuth.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 permissionsconstvalidTenantPermissions=descopeClient.validateTenantPermissions(authInfo,'my-tenant-ID',['Permission to validate',]);if(!validTenantPermissions){// Deny access}// Or validate roles directlyconstvalidTenantRoles=descopeClient.validateTenantRoles(authInfo,'my-tenant-ID',['Role to validate',]);if(!validTenantRoles){// Deny access}// Or get the matched roles/permissionsconstmatchedTenantRoles=descopeClient.getMatchedTenantRoles(authInfo,'my-tenant-ID',['Role to validate','Another role to validate',]);constmatchedTenantPermissions=descopeClient.getMatchedTenantPermissions(authInfo,'my-tenant-ID',['Permission to validate','Another permission to validate'],);

When not using tenants use:

// You can validate specific permissionsconstvalidPermissions=descopeClient.validatePermissions(authInfo,['Permission to validate']);if(!validPermissions){// Deny access}// Or validate roles directlyconstvalidRoles=descopeClient.validateRoles(authInfo,['Role to validate']);if(!validRoles){// Deny access}// Or get the matched roles/permissionsconstmatchedRoles=descopeClient.getMatchedRoles(authInfo,['Role to validate','Another role to validate',]);constmatchedPermissions=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.

awaitdescopeClient.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.

awaitdescopeClient.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.

importDescopeClientfrom'@descope/node-sdk';constdescopeClient=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 tenantawaitdescopeClient.management.tenant.create('My Tenant',['domain.com'],{customAttributeName: 'val',});// You can optionally set your own ID when creating a tenantawaitdescopeClient.management.tenant.createWithId('my-custom-id','My Tenant',['domain.com'],{customAttributeName: 'val',});// Update will override all fields as is. Use carefully.awaitdescopeClient.management.tenant.update('my-custom-id','My Tenant',['domain.com','another-domain.com'],{customAttributeName: 'val'},);// 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.awaitdescopeClient.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 tenantawaitdescopeClient.management.tenant.delete('my-custom-id',false);// Load tenant by idconsttenant=awaitdescopeClient.management.tenant.load('my-custom-id');// Load all tenantsconsttenantsRes=awaitdescopeClient.management.tenant.loadAll();tenantsRes.data.forEach((tenant)=>{// do something});// Search all tenants according to various parametersconstsearchRes=awaitdescopeClient.management.tenant.searchAll(['id']);searchRes.data.forEach((tenant)=>{// do something});// Load tenant settings by idconsttenantSettings=awaitdescopeClient.management.tenant.getSettings('my-tenant-id');// Update will override all fields as is. Use carefully.awaitdescopeClient.management.tenant.configureSettings('my-tenant-id',{domains: ['domain1.com'],selfProvisioningDomains: ['domain1.com'],sessionSettingsEnabled: 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)constres=awaitdescopeClient.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.constresWithActor=awaitdescopeClient.management.tenant.generateSSOConfigurationLink('my-tenant-id',60*60*24,undefined,// ssoIdundefined,// emailundefined,// templateId'my-admin-actor-id',// actorId);console.log(resWithActor.adminSSOConfigurationLink);

Manage Password

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

// Load tenant password settings by idconstpasswordSettings=awaitdescopeClient.management.password.getSettings('my-tenant-id');// Update will override all fields as is. Use carefully.awaitdescopeClient.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 applicationawaitdescopeClient.management.ssoApplication.createOidcApplication({name: 'My OIDC app name',loginPageUrl: 'http://dummy.com/login',});// Create SAML SSO applicationawaitdescopeClient.management.ssoApplication.createSamlApplication({name: 'My SAML app name',loginPageUrl: 'http://dummy.com/login',useMetadataInfo: true,metadataUrl: 'http://dummy.com/metadata',});// Create WS-Fed SSO applicationawaitdescopeClient.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.awaitdescopeClient.management.ssoApplication.updateOidcApplication({id: 'my-app-id',name: 'My OIDC app name',loginPageUrl: 'http://dummy.com/login',});// Update SAML SSO application.// Update will override all fields as is. Use carefully.awaitdescopeClient.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.awaitdescopeClient.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.awaitdescopeClient.management.ssoApplication.delete('my-app-id');// Load SSO application by idconstapp=awaitdescopeClient.management.ssoApplication.load('my-app-id');// Load all SSO applicationsconstappsRes=awaitdescopeClient.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.awaitdescopeClient.management.user.create('desmond@descope.com',{email: 'desmond@descope.com',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.awaitdescopeClient.management.user.invite('desmond@descope.com',{email: 'desmond@descope.com',displayName: 'Desmond Copeland',userTenants: [{tenantId: 'tenant-ID1',roleNames: ['role-name1']}],// You can override the project's User Invitation Redirect URL with this parameterinviteUrl: '<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 templateOptionstemplateOptions: {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.awaitdescopeClient.management.user.inviteBatch([{loginId: 'desmond@descope.com',email: 'desmond@descope.com',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.awaitdescopeClient.management.user.createBatch([{loginId: 'desmond@descope.com',email: 'desmond@descope.com',phone: '+123456789123',displayName: 'Desmond Copeland',userTenants: [{tenantId: 'tenant-ID1',roleNames: ['role-name1']}],hashedPassword: {bcrypt: {hash: '$2a$...',},},},]);// Update will override all fields as is. Use carefully.awaitdescopeClient.management.user.update('desmond@descope.com',{email: 'desmond@descope.com',displayName: 'Desmond Copeland',userTenants: [{tenantId: 'tenant-ID1',roleNames: ['role-name1']}],});// Update explicit data for a user rather than overriding all fieldsawaitdescopeClient.management.user.updatePhone('desmond@descope.com','+18005551234',true);awaitdescopeClient.management.user.updateLoginId('desmond@descope.com','bane@descope.com');awaitdescopeClient.management.user.removeTenantRoles('desmond@descope.com','tenant-ID1','role-name2',);// Update explicit user's data using patch (will override only provided fields)constoptions: PatchUserOptions={displayName: 'Desmond Copeland Jr.'};awaitdescopeClient.management.user.patch('desmond@descope.com',options);// User deletion cannot be undone. Use carefully.awaitdescopeClient.management.user.delete('desmond@descope.com');// Delete a batch of users. This requires Descope user IDs.awaitdescopeClient.management.user.deleteBatch(['<user-ID-1>','<user-ID-2>']);// Load specific userconstuserRes=awaitdescopeClient.management.user.load('desmond@descope.com');// If needed, users can be loaded using the user ID as wellconstuserRes=awaitdescopeClient.management.user.loadByUserId('<user-ID>');// loadUsers - load users by their user id, optionally you can decide if to return invalid usersconstusersRes=awaitdescopeClient.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.constusersRes=awaitdescopeClient.management.user.search({tenantIds: ['tenant-ID'],verifiedEmail: true,// optional: filter by verified email statusverifiedPhone: false,// optional: filter by verified phone status});console.log('Total users:',usersRes.data.total);usersRes.data.users.forEach((user)=>{// do something});awaitdescopeClient.management.user.logoutUser('my-custom-id');awaitdescopeClient.management.user.logoutUserByUserId('<user-ID>');// Get users' authentication historyconstuserIds=['user-id-1','user-id-2'];constusersHistoryRes=awaitdescopeClient.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 passwordawaitdescopeClient.management.user.setTemporaryPassword('<login-ID>','<some-password>');// Set a user's passwordawaitdescopeClient.management.user.setActivePassword('<login-ID>','<some-password>');// Or alternatively, expire a user passwordawaitdescopeClient.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.awaitdescopeClient.management.project.updateName('new-project-name');// Set will override all fields as is. Use carefully.awaitdescopeClient.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.constcloneRes=awaitdescopeClient.management.project.clone('new-project-name');

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

constprojects=awaitdescopeClient.management.project.listProjects();

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

// Exports the current state of the projectconstexportRes=awaitdescopeClient.management.project.exportSnapshot();

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

constvalidateReq={files: exportRes.data.files,};// Validate that an exported snapshot can be imported into the current projectconstvalidateRes=awaitdescopeClient.management.project.import(files);if(!validateRes.ok){// validation failed, check failures and missingSecrets to fix this}// Import the previously exported snapshot into the current projectconstimportReq={files: exportRes.data.files,};awaitdescopeClient.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.awaitdescopeClient.management.accessKey.create('key-name',123456789,// expiration timenull,[{tenantId: 'tenant-ID1',roleNames: ['role-name1']}],undefined,// userIdundefined,// customClaimsundefined,// descriptionundefined,// permittedIps{attributeName: 'attributeValue'},// customAttributes);// Load specific userconstaccessKeyRes=awaitdescopeClient.management.accessKey.load('key-id');// Search all users, optionally according to tenant and/or role filterconstaccessKeysRes=awaitdescopeClient.management.accessKey.searchAll(['tenant-ID']);accessKeysRes.data.forEach((accessKey)=>{// do something});// Update will override all fields as is. Use carefully.awaitdescopeClient.management.accessKey.update('key-id','new-key-name','new-description');// Access keys can be deactivated to prevent usage. This can be undone using "activate".awaitdescopeClient.management.accessKey.deactivate('key-id');// Disabled access keys can be activated once again.awaitdescopeClient.management.accessKey.activate('key-id');// Access key deletion cannot be undone. Use carefully.awaitdescopeClient.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 configurationconstssoSettings=awaitdescopeClient.management.sso.loadSettings('tenant-id');// You can get all configured SSO settings for a specific tenant ID (for multi SSO usage)constallSSOSettings=awaitdescopeClient.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 configurationconsttenantId='tenant-id';// Which tenant this configuration is forconstidpURL='https://idp.com';constentityID='my-idp-entity-id';constidpCert='<your-cert-here>';constredirectURL='https://my-app.com/handle-sso';// Global redirect URL for SSO/SAMLconstdomains=['tenant-users.com'];// Users authentication with this domain will be logged in to this tenantawaitdescopeClient.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 configurationawaitdescopeClient.management.sso.configureSAMLByMetadata(tenantID,{idpMetadataUrl: 'https://idp.com/my-idp-metadata'},redirectURL,domains,);// 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 configurationconstname='some-name';constclientId='client id of OIDC';constclientSecret='client secret';awaitdescopeClient.management.sso.configureOIDCSettings(tenantID,{ name, clientId, clientSecret, redirectUrl },domains,);// You can create new SSO configuration (aka multi SSO)constssoId='my-new-additional-sso-id';constdisplayName='My additional SSO configuration';awaitdescopeClient.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 configurationawaitdescopeClient.management.sso.deleteSettings(tenantID);

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.constname='My Permission';letdescription='Optional description to briefly explain what this permission allows.';awaitdescopeClient.management.permission.create(name,description);// Update will override all fields as is. Use carefully.constnewName='My Updated Permission';description='A revised description';awaitdescopeClient.management.permission.update(name,newName,description);// Permission deletion cannot be undone. Use carefully.awaitdescopeClient.management.permission.delete(newName);// Load all permissionsconstpermissionsRes=awaitdescopeClient.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.constname='My Role';consttenantId='<tenant id>';letdescription='Optional description to briefly explain what this role allows.';constpermissionNames=['My Updated Permission'];descopeClient.management.role.create(name,description,permissionNames,tenantId);// Update will override all fields as is. Use carefully.constnewName='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 rolesconstrolesRes=awaitdescopeClient.management.role.loadAll();rolesRes.data.forEach((role)=>{// do something});// Search rolesconstrolesRes=awaitdescopeClient.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 idconstgroupsRes=descopeClient.management.group.loadAllGroups('tenant-id');// Load all groups for the given user IDs (can be found in the user's JWT)constgroupsRes=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)constgroupsRes=descopeClient.management.group.loadAllGroupsForMember('tenant-id',[],['login-id-1','login-id-2'],);// Load all group's members by the given group idconstgroupsRes=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 flowsconstres=awaitdescopeClient.management.flow.list();console.log('found total flows',res.total);res.flows.forEach((flowMetadata)=>{// do something});// Delete flows by idsawaitdescopeClient.management.flow.delete(['flow-1','flow-2']);// Export the flow and it's matching screens based on the given idconstres=awaitdescopeClient.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 idconst{ flow, screens }=res.data;constupdatedRes=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 flowconstrunRes=awaitdescopeClient.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 flowconstrunWithInputRes=awaitdescopeClient.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 projectconstres=descopeClient.management.theme.export();console.log(res.data.theme);// Import the given theme to the projectconstupdatedRes=descopeClient.management.theme.import(theme);console.log(updatedRes.data.theme);

Manage JWTs

You can add custom claims to a valid JWT.

constupdatedJWTRes=awaitdescopeClient.management.jwt.update('original-jwt',{customKey1: 'custom-value1',customKey2: 'custom-value2',});

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

constres=awaitdescopeClient.management.jwt.signIn('dummy');

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

constres=awaitdescopeClient.management.jwt.signUp('dummy');

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

constres=awaitdescopeClient.management.jwt.signUpOrIn('dummy');

Generate a client assertion JWT for OAuth flows.

constclientAssertionRes=awaitdescopeClient.management.jwt.generateClientAssertionJwt('https://example.com/issuer',// issuer'client-id-123',// subject['https://example.com/token'],// audience300,// expiresIn - number of seconds the token will will be valid forfalse,// 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

constupdatedJWTRes=awaitdescopeClient.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

constupdatedJWTRes=awaitdescopeClient.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

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 }=awaitdescopeClient.management.user.generateEmbeddedLink('desmond@descope.com',{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 daysconstaudits=awaitdescopeClient.management.audit.search({from: Date.now()-10*24*60*60*1000,text: 'some-text',});console.log(audits);// Search successful logins in the last 30 daysconstaudits=awaitdescopeClient.management.audit.search({actions: ['LoginSucceed']});console.log(audits);

You can also create audit event with data

awaitdescopeClient.management.audit.createEvent({action: 'pencil.created',type: 'info',// info/warn/erroractorId: '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.0type usertype orgrelation member: userrelation parent: orgtype folderrelation parent: folderrelation owner: user | org#memberrelation editor: userrelation viewer: userpermission can_create: owner | parent.ownerpermission can_edit: editor | can_createpermission can_view: viewer | can_edittype docrelation parent: folderrelation owner: user | org#memberrelation editor: userrelation viewer: userpermission can_create: owner | parent.ownerpermission can_edit: editor | can_createpermission 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.

constdescopeClient=require('@descope/node-sdk');// Save schemaawaitdescopeClient.management.fga.saveSchema(schema);// Create a relation between a resource and userawaitdescopeClient.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 viewconstrelations=awaitdescopeClient.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.

importDescopeClientfrom'@descope/node-sdk';// Initialize client with FGA cache URLconstdescopeClient=DescopeClient({projectId: '<Project ID>',managementKey: '<Management Key>',// Required for cache proxyfgaCacheUrl: '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 }=awaitdescopeClient.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}=awaitdescopeClient.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.awaitdescopeClient.management.outboundApplication.updateApplication({id: 'my-app-id',name: 'my updated app',
...
});// delete an outbound application by id.// inbound application deletion cannot be undone. Use carefully.awaitdescopeClient.management.outboundApplication.deleteApplication('my-app-id');// Load an outbound application by idconstapp=awaitdescopeClient.management.outboundApplication.loadApplication('my-app-id');// Load all outbound applicationsconstappsRes=awaitdescopeClient.management.outboundApplication.loadAllApplications();appsRes.data.forEach((app)=>{// do something});// Fetch user token with specific scopesconstuserToken=awaitdescopeClient.management.outboundApplication.fetchTokenByScopes('my-app-id','user-id',['read','write'],{withRefreshToken: false},'tenant-id');// Fetch latest user tokenconstlatestUserToken=awaitdescopeClient.management.outboundApplication.fetchToken('my-app-id','user-id','tenant-id',{forceRefresh: false});// Fetch tenant token with specific scopesconsttenantToken=awaitdescopeClient.management.outboundApplication.fetchTenantTokenByScopes('my-app-id','tenant-id',['read','write'],{withRefreshToken: false});// Fetch latest tenant tokenconstlatestTenantToken=awaitdescopeClient.management.outboundApplication.fetchTenantToken('my-app-id','tenant-id',{forceRefresh: false});// Delete user tokens by appId and/or userId// At least one of appId or userId should be provided// Token deletion cannot be undone. Use carefully.awaitdescopeClient.management.outboundApplication.deleteUserTokens('my-app-id','user-id');// Delete all tokens for a specific appawaitdescopeClient.management.outboundApplication.deleteUserTokens('my-app-id');// Delete all tokens for a specific userawaitdescopeClient.management.outboundApplication.deleteUserTokens(undefined,'user-id');// Delete a specific token by its ID// Token deletion cannot be undone. Use carefully.awaitdescopeClient.management.outboundApplication.deleteTokenById('token-id');// List the IDs of the outbound apps a user currently holds a valid token for.// Use this for connection-status UIs instead of calling fetchToken once per app.constconnectedApps=awaitdescopeClient.management.outboundApplication.listAppsWithUserToken('user-id','tenant-id'// optional);// connectedApps.data => ['app-1', 'app-2']// Store a static API key for a user / tenant on an apikey-type outbound appawaitdescopeClient.management.outboundApplication.uploadUserApiKey('my-app-id','user-id','the-users-api-key','tenant-id'// optional);awaitdescopeClient.management.outboundApplication.uploadTenantApiKey('my-app-id','tenant-id','the-tenants-api-key');// Upload (migrate) an existing OAuth token for a user / tenant on an oauth-type outbound app,// without requiring the user to re-run the OAuth flow.awaitdescopeClient.management.outboundApplication.uploadUserToken({appId: 'my-app-id',userId: 'user-id',refreshToken: 'the-refresh-token',scopes: ['read','write'],});awaitdescopeClient.management.outboundApplication.uploadTenantToken({appId: 'my-app-id',tenantId: 'tenant-id',accessToken: 'the-access-token',});// Batch upload OAuth tokens (all-or-nothing): inspect `failures` to see rejected itemsconstbatchRes=awaitdescopeClient.management.outboundApplication.batchUploadUserTokens([{appId: 'my-app-id',userId: 'user-1',accessToken: 'token-1'},{appId: 'my-app-id',userId: 'user-2',accessToken: 'token-2'},]);// batchRes.data.failures => [{ appId, userId, errorCode, reason }, ...]

Manage Inbound Applications

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

// Create an inbound application.const{ id,cleartext: secret}=awaitdescopeClient.management.inboundApplication.createApplication({name: 'my new app',description: 'my desc',logo: 'data:image/png;..',approvedCallbackUrls: ['dummy.com'],permissionsScopes: [{name: 'read_support',description: 'read for support',values: ['Support'],},],attributesScopes: [{name: 'read_email',description: 'read user email',values: ['email'],},],loginPageUrl: 'http://dummy.com/login',});// Update an inbound application.// Update will override all fields as is. Use carefully.awaitdescopeClient.management.inboundApplication.updateApplication({id: 'my-app-id',name: 'my updated app',loginPageUrl: 'http://dummy.com/login',approvedCallbackUrls: ['dummy.com','myawesomedomain.com'],});// Patch an inbound application.// patch will not override all fields, but update only what given.awaitdescopeClient.management.inboundApplication.patchApplication({id: 'my-app-id',name: 'my updated app name',description: 'my new description',});// delete an inbound application by id.// inbound application deletion cannot be undone. Use carefully.awaitdescopeClient.management.inboundApplication.deleteApplication('my-app-id');// Load an inbound application by idconstapp=awaitdescopeClient.management.inboundApplication.loadApplication('my-app-id');// Load all inbound applicationsconstappsRes=awaitdescopeClient.management.inboundApplication.loadAllApplications();appsRes.data.forEach((app)=>{// do something});// Get an inbound application secret by application id.const{ cleartext }=awaitdescopeClient.management.inboundApplication.getApplicationSecret('my-app-id',);// Rotate an inbound application secret by application id.const{ cleartext }=awaitdescopeClient.management.inboundApplication.rotateApplicationSecret('my-app-id',);// Search in all consents. search consents by the given app id and offset to the third page.constconsentsRes=awaitdescopeClient.management.inboundApplication.searchConsents({appId: 'my-app',page: 2,});// Delete consents. delete all user consents, application consents or specific consents by id.// inbound application consents deletion cannot be undone. Use carefully.awaitdescopeClient.management.inboundApplication.deleteConsents({userIds: ['user'],});

Manage Management Keys

You can create, update, delete, load, or search management keys:

// Create a new management key.// The name is required, other fields are optional.// expiresIn is the expiration time in seconds (0 for no expiration).// permittedIps is an optional list of IP addresses or CIDR ranges that are allowed to use this key.// reBac specifies the role-based access control configuration for the key.constcreateRes=awaitdescopeClient.management.managementKey.create('my-key-name','Optional description',3600,// expires in 1 hour['10.0.0.1/24'],// optional permitted IPs{companyRoles: ['Admin']},// optional reBac configuration);console.log('Created key:',createRes.data.key);console.log('Key secret (save this!):',createRes.data.cleartext);// Load a management key by IDconstloadRes=awaitdescopeClient.management.managementKey.load('key-id');console.log('Loaded key:',loadRes.data.key);// Search all management keysconstsearchRes=awaitdescopeClient.management.managementKey.search();searchRes.data.keys.forEach((key)=>{// do something});// Update an existing management key.// IMPORTANT: All parameters will override whatever values are currently set in the existing key.awaitdescopeClient.management.managementKey.update('key-id','updated-key-name','Updated description',['1.2.3.4'],// updated permitted IPs'active',// status: 'active' or 'inactive');// Delete management keys by IDs.// IMPORTANT: This action is irreversible. Use carefully.awaitdescopeClient.management.managementKey.delete(['key-id-1','key-id-2']);

Manage Descopers

You can create, update, delete, or load descopers (Descope console users):

// Create descopers. Each descoper must have a loginId.// Optionally set attributes (displayName, email, phone) and RBAC configuration.// sendInvite can be set to true to send an invitation email.awaitdescopeClient.management.descoper.create([{loginId: 'user@example.com',attributes: {displayName: 'Test User',email: 'user@example.com',phone: '+1234567890',},sendInvite: true,rbac: {// exactly one of isCompanyAdmin, projects or tagsprojects: [{projectIds: ['project-id-1'],role: 'admin',// 'admin' | 'developer' | 'support' | 'auditor'},],},},]);// Load a specific descoper by IDconstdescoperRes=awaitdescopeClient.management.descoper.load('descoper-id');console.log('Loaded descoper:',descoperRes.data);// Load all descopersconstdescopersRes=awaitdescopeClient.management.descoper.loadAll();descopersRes.data.descopers.forEach((descoper)=>{// do something});// Update a descoper's attributes and/or RBAC configurationawaitdescopeClient.management.descoper.update('descoper-id',{displayName: 'Updated Name'},// attributes (optional){isCompanyAdmin: true},// rbac (optional));// Descoper deletion cannot be undone. Use carefully.awaitdescopeClient.management.descoper.delete('descoper-id');

Manage Engines

You can create, update, delete, load engines, and rotate their secrets. The engine secret is returned only on create and rotate — store it securely, as it cannot be retrieved again.

// Create an engine. The response includes the generated id and secret.constcreateRes=awaitdescopeClient.management.engine.create('My Engine');const{ id, secret }=createRes.data;// save `secret` securely!// Update an engine's name (the response does not include the secret).awaitdescopeClient.management.engine.update(id,'Updated Engine Name');// Load a specific engine by id (the secret is always empty).constengineRes=awaitdescopeClient.management.engine.load(id);// Load all engines (secrets are always empty).constenginesRes=awaitdescopeClient.management.engine.loadAll();enginesRes.data.forEach((engine)=>{// do something});// Rotate an engine's secret. The previous secret is invalidated and the new one returned.constrotateRes=awaitdescopeClient.management.engine.rotateSecret(id);constnewSecret=rotateRes.data.secret;// Engine deletion cannot be undone. Use carefully.awaitdescopeClient.management.engine.delete(id);

Utils for your end to end (e2e) tests and integration tests

To ease your e2e tests, we exposed dedicated management methods, that way, you don't need to use 3rd party messaging services in order to receive sign-in/up Email, SMS, Voice call or WhatsApp, and avoid the need of parsing the code and token from them.

// User for test can be created, this user will be able to generate code/link without// the need of 3rd party messaging services.// Test user must have a loginId, other fields are optional.// Roles should be set directly if no tenants exist, otherwise set// on a per-tenant basis.awaitdescopeClient.management.user.createTestUser('desmond@descope.com',{email: 'desmond@descope.com',displayName: 'Desmond Copeland',userTenants: [{tenantId: 'tenant-ID1',roleNames: ['role-name1']}],});// Search all test users according to various parametersconstsearchRes=awaitdescopeClient.management.user.searchTestUsers({userIds: ['id']});console.log('Total test users:',searchRes.data.total);searchRes.data.users.forEach((user)=>{// do something});// Now test user got created, and this user will be available until you delete it,// you can use any management operation for test user CRUD.// You can also delete all test users.awaitdescopeClient.management.user.deleteAllTestUsers();// OTP code can be generated for test user, for example:const{ code }=awaitdescopeClient.management.user.generateOTPForTestUser('sms',// you can use also 'email', 'whatsapp', 'voice''desmond@descope.com',);// Now you can verify the code is valid (using descopeClient.auth.*.verify for example)// LoginOptions can be provided to set custom claims to the generated jwt.// Same as OTP, magic link can be generated for test user, for example:const{ link }=awaitdescopeClient.management.user.generateMagicLinkForTestUser('email','desmond@descope.com','',);// Enchanted link can be generated for test user, for example:const{ link, pendingRef }=awaitdescopeClient.management.user.generateEnchantedLinkForTestUser('desmond@descope.com','',);

Code Examples

You can find various usage examples in the examples folder.

Setup

To run the examples, set your Project ID by setting the DESCOPE_PROJECT_ID env var or directly in the sample code. Find your Project ID in the Descope console.

export DESCOPE_PROJECT_ID=<ProjectID>

Run an example

Run the following commands in the root of the project to build and run the examples with a local build of the SDK.

  1. Run this to start the ES6 typescript module example

    npm i && \
    npm run build && \
    cd examples/es6 && \
    npm i && \
    npm run generateCerts && \
    npm start
  2. Run this to start the commonjs example

    npm i && \
    npm run build && \
    cd examples/commonjs && \
    npm i && \
    npm run generateCerts && \
    npm start

Providing Custom Public Key

By default, the SDK will download the public key from Descope's servers. You can also provide your own public key. This is useful when the server you are running the SDK on does not have access to the internet.

You can find your public key in the https://api.descope.com/v2/keys/<project-id> endpoint. For further information, please see the Descope Documentation and API reference page.

To provide your own public key, you can do so by providing the publicKey option when initializing the SDK:

importDescopeClientfrom'@descope/node-sdk';constdescopeClient=DescopeClient({projectId: 'my-project-ID',publicKey: '{"alg":"RS256", ... }',});// The public key will be used when validating jwtconstsessionJWt='<session-jwt>';awaitdescopeClient.validateJwt(sessionJWt);

Learn More

To learn more please see the Descope Documentation and API reference page.

Contact Us

If you need help you can email Descope Support

License

The Descope SDK for Node.js is licensed for use under the terms and conditions of the MIT license Agreement.

About

Node.js library used to integrate with Descope

Topics

Resources

Stars

63 stars

Watchers

15 watching

Forks

Releases

Used by

Contributors

Languages