{"id":18896491,"url":"https://github.com/descope/node-sdk","last_synced_at":"2025-04-04T19:07:16.071Z","repository":{"id":57750699,"uuid":"490604483","full_name":"descope/node-sdk","owner":"descope","description":"Node.js library used to integrate with Descope","archived":false,"fork":false,"pushed_at":"2025-04-02T12:23:26.000Z","size":2140,"stargazers_count":39,"open_issues_count":32,"forks_count":6,"subscribers_count":16,"default_branch":"main","last_synced_at":"2025-04-03T00:48:15.889Z","etag":null,"topics":["authentication","descope","node","node-sdk","nodejs","nodejs-sdk","sdk","typescript"],"latest_commit_sha":null,"homepage":"https://docs.descope.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/descope.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-10T08:17:38.000Z","updated_at":"2025-04-02T10:25:57.000Z","dependencies_parsed_at":"2023-10-14T19:37:03.613Z","dependency_job_id":"f4e99bdf-a9f2-407a-b461-7a8715c9a86c","html_url":"https://github.com/descope/node-sdk","commit_stats":{"total_commits":146,"total_committers":15,"mean_commits":9.733333333333333,"dds":0.8082191780821918,"last_synced_commit":"a23861ad847914cf52a66123292e5cdb1cf5a3c5"},"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fnode-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fnode-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fnode-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fnode-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/descope","download_url":"https://codeload.github.com/descope/node-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247234921,"owners_count":20905854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","descope","node","node-sdk","nodejs","nodejs-sdk","sdk","typescript"],"created_at":"2024-11-08T08:34:10.727Z","updated_at":"2025-04-04T19:07:16.036Z","avatar_url":"https://github.com/descope.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Descope SDK for Node.js\n\nThe Descope SDK for Node.js provides convenient access to the Descope user management and authentication API\nfor a backend written in Node.js. You can read more on the [Descope Website](https://descope.com).\n\n## Requirements\n\nThe SDK supports Node version 14 and above.\n\n## Installing the SDK\n\nInstall the package with:\n\n```bash\nnpm i --save @descope/node-sdk\n```\n\n## Authentication Functions\n\n### Setup\n\nBefore you can use authentication functions listed below, you must initialize `descopeClient` to use all of the built-in SDK functions.\n\nYou'll need your Descope `Project ID` to create this, and you can find it on the [project page](https://app.descope.com/settings/project) in the Descope Console.\n\n```typescript\nimport DescopeClient from '@descope/node-sdk';\n\nconst descopeClient = DescopeClient({ projectId: 'my-project-ID' });\n```\n\nOnce you've created a `descopeClient`, you can use that to work with the following functions:\n\n1. [OTP Authentication](#otp-authentication)\n2. [Magic Link](#magic-link)\n3. [Enchanted Link](#enchanted-link)\n4. [OAuth](#oauth)\n5. [SSO/SAML](#ssosaml)\n6. [TOTP Authentication](#totp-authentication)\n7. [Passwords](#passwords)\n8. [Session Validation](#session-validation)\n9. [Roles \u0026 Permission Validation](#roles--permission-validation)\n10. [Logging Out](#logging-out)\n\n## Management Functions\n\n### Setup\n\nBefore you can use management functions listed below, you must initialize `descopeClient`.\n\nIf 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](https://app.descope.com/settings/company/managementkeys).\n\n```typescript\nimport DescopeClient from '@descope/node-sdk';\n\nconst descopeClient = DescopeClient({\n  projectId: 'my-project-ID',\n  managementKey: 'management-key',\n});\n```\n\nThen, you can use that to work with the following functions:\n\n1. [Manage Tenants](#manage-tenants)\n2. [Manage Users](#manage-users)\n3. [Manage Access Keys](#manage-access-keys)\n4. [Manage SSO Setting](#manage-sso-setting)\n5. [Manage Permissions](#manage-permissions)\n6. [Manage Roles](#manage-roles)\n7. [Query SSO Groups](#query-sso-groups)\n8. [Manage Flows](#manage-flows)\n9. [Manage JWTs](#manage-jwts)\n10. [Impersonate](#impersonate)\n11. [Embedded Links](#embedded-links)\n12. [Audit](#audit)\n13. [Manage FGA (Fine-grained Authorization)](#manage-fga-fine-grained-authorization)\n14. [Manage Project](#manage-project)\n15. [Manage SSO applications](#manage-sso-applications)\n\nIf you wish to run any of our code samples and play with them, check out our [Code Examples](#code-examples) section.\n\nIf you're performing end-to-end testing, check out the [Utils for your end to end (e2e) tests and integration tests](#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](#management-functions).\n\n---\n\n## Error Handling\n\nEvery `async` operation may fail. In case it does, there will be information regarding what happened on the response object.\nA typical case of error handling might look something like:\n\n```ts\nimport DescopeClient, { SdkResponse } from '@descope/node-sdk';\n\nconst { DescopeErrors } = DescopeClient;\n\n// ...\n\ntry {\n  const resp = await sdk.otp.signIn.email(loginId);\n  if (resp.error) {\n    switch (resp.error.errorCode) {\n      case DescopeErrors.userNotFound:\n        // Handle specifically\n        break;\n      default:\n      // Handle generally\n      // `resp.error` will contain `errorCode`, `errorDescription` and sometimes `errorMessage` to\n      // help understand what went wrong. See SdkResponse for more information.\n    }\n  }\n} catch (e) {\n  // Handle technical error\n}\n```\n\n---\n\n### OTP Authentication\n\nSend 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.\n\nThe user can either `sign up`, `sign in` or `sign up or in`\n\n```typescript\n// Every user must have a login ID. All other user information is optional\nconst loginId = 'desmond@descope.com';\nconst user = {\n  name: 'Desmond Copland',\n  phone: '212-555-1234',\n  email: loginId,\n};\nawait descopeClient.otp.signUp['email'](loginId, user);\n```\n\nThe user will receive a code using the selected delivery method. Verify that code using:\n\n```typescript\nconst jwtResponse = await descopeClient.otp.verify['email'](loginId, 'code');\n// jwtResponse.data.sessionJwt\n// jwtResponse.data.refreshJwt\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### Magic Link\n\nSend a user a Magic Link using your preferred delivery method (_email / SMS_).\nThe Magic Link will redirect the user to page where the its token needs to be verified.\nThis redirection can be configured in code, or globally in the [Descope Console](https://app.descope.com/settings/authentication/magiclink)\n\nThe user can either `sign up`, `sign in` or `sign up or in`\n\n```typescript\n// If configured globally, the redirect URI is optional. If provided however, it will be used\n// instead of any global configuration\nconst URI = 'http://myapp.com/verify-magic-link';\nawait descopeClient.magicLink.signUpOrIn['email']('desmond@descope.com', URI);\n```\n\nTo verify a magic link, your redirect page must call the validation function on the token (`t`) parameter (`https://your-redirect-address.com/verify?t=\u003ctoken\u003e`):\n\n```typescript\nconst jwtResponse = await descopeClient.magicLink.verify('token');\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### Enchanted Link\n\nUsing the Enchanted Link APIs enables users to sign in by clicking a link\ndelivered to their email address. The email will include 3 different links,\nand the user will have to click the right one, based on the 2-digit number that is\ndisplayed when initiating the authentication process.\n\nThis method is similar to [Magic Link](#magic-link) but differs in two major ways:\n\n- The user must choose the correct link out of the three, instead of having just one\n  single link.\n- This supports cross-device clicking, meaning the user can try to log in on one device,\n  like a computer, while clicking the link on another device, for instance a mobile phone.\n\nThe Enchanted Link will redirect the user to page where the its token needs to be verified.\nThis redirection can be configured in code per request, or set globally in the [Descope Console](https://app.descope.com/settings/authentication/enchantedlink).\n\nThe user can either `sign up`, `sign in` or `sign up or in`\n\n```typescript\n// If configured globally, the redirect URI is optional. If provided however, it will be used\n// instead of any global configuration.\nconst URI = 'http://myapp.com/verify-enchanted-link';\nconst enchantedLinkRes = await descopeClient.enchantedLink.signIn('desmond@descope.com', URI);\nenchantedLinkRes.data.linkId; // Should be displayed to the user so they can click the corresponding link in the email\nenchantedLinkRes.data.pendingRef; // Used to poll for a valid session\n```\n\nAfter sending the link, you must poll to receive a valid session using the `pendingRef` from\nthe previous step. A valid session will be returned only after the user clicks the right link.\n\n```typescript\n// Poll for a certain number of tries / time frame. You can control the polling interval and time frame\n// with the optional WaitForSessionConfig\nconst jwtResponse = await descopeClient.enchantedLink.waitForSession(\n  enchantedLinkRes.data.pendingRef,\n);\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nTo verify an enchanted link, your redirect page must call the validation function on the token (`t`) parameter (`https://your-redirect-address.com/verify?t=\u003ctoken\u003e`). Once the token is verified, the session polling will receive a valid response.\n\n```typescript\ntry {\n  await descopeClient.enchantedLink.verify('token');\n  // token is invalid\n} catch (error) {\n  // token is valid\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### OAuth\n\nUsers can authenticate using their social logins, via the OAuth protocol. Configure your OAuth settings on the [Descope console](https://app.descope.com/settings/authentication/social). To start an OAuth flow call:\n\n```typescript\n// Choose an oauth provider out of the supported providers\n// If configured globally, the return URL is optional. If provided however, it will be used\n// instead of any global configuration.\n\nconst urlRes = await descopeClient.oauth.start['google'](redirectUrl);\nurlRes.data.url; // Redirect the user to the returned URL to start the OAuth redirect chain\n```\n\nThe 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:\n\n```typescript\nconst jwtResponse = await descopeClient.oauth.exchange('token');\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### SSO/SAML\n\nUsers can authenticate to a specific tenant using SAML or Single Sign On. Configure your SSO/SAML settings on the [Descope console](https://app.descope.com/settings/authentication/sso). To start a flow call:\n\n```typescript\n// If configured globally, the return URL is optional. If provided however, it will be used\n// instead of any global configuration.\nconst redirectUrl = 'https://my-app.com/handle-saml';\nconst 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\nurlRes.data.url; // Redirect the user to the given returned URL to start the SSO/SAML redirect chain\n```\n\nThe 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:\n\n```typescript\nconst jwtResponse = await descopeClient.saml.exchange('token');\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### TOTP Authentication\n\nThe user can authenticate using an authenticator app, such as Google Authenticator.\nSign up like you would using any other authentication method. The sign up response\nwill then contain a QR code `image` that can be displayed to the user to scan using\ntheir mobile device camera app, or the user can enter the `key` manually or click\non the link provided by the `provisioningURL`.\n\nExisting users can add TOTP using the `update` function.\n\n```typescript\n// Every user must have a login ID. All other user information is optional\nconst loginId = 'desmond@descope.com';\nconst user = {\n  name: 'Desmond Copland',\n  phone: '212-555-1234',\n  email: loginId,\n};\nconst totpRes = await descopeClient.totp.signUp(loginId, user);\n// Use one of the provided options to have the user add their credentials to the authenticator\ntotpRes.data.provisioningURL;\ntotpRes.data.image;\ntotpRes.data.key;\n```\n\nThere are 3 different ways to allow the user to save their credentials in\ntheir authenticator app - either by clicking the provisioning URL, scanning the QR\nimage or inserting the key manually. After that, signing in is done using the code\nthe app produces.\n\n```typescript\nconst jwtResponse = await descopeClient.totp.verify(loginId, 'code');\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### Passwords\n\nThe user can also authenticate with a password, though it's recommended to\nprefer passwordless authentication methods if possible. Sign up requires the\ncaller to provide a valid password that meets all the requirements configured\nfor the [password authentication method](https://app.descope.com/settings/authentication/password) in the Descope console.\n\n```js\n// Every user must have a loginId. All other user information is optional\nconst loginId = 'desmond@descope.com';\nconst password = 'qYlvi65KaX';\nconst user = {\n  name: 'Desmond Copeland',\n  email: loginId,\n};\nconst jwtResponse = await descopeClient.password.signUp(loginId, password, user);\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nThe user can later sign in using the same loginId and password.\n\n```js\nconst jwtResponse = await descopeClient.password.signIn(loginId, password);\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\nIn case the user needs to update their password, one of two methods are available: Resetting their password or replacing their password\n\n**Changing Passwords**\n\n_NOTE: sendReset will only work if the user has a validated email address. Otherwise password reset prompts cannot be sent._\n\nIn the [password authentication method](https://app.descope.com/settings/authentication/password) 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.\n\n```js\n// Start the reset process by sending a password reset prompt. In this example we'll assume\n// that magic link is configured as the reset method. The optional redirect URL is used in the\n// same way as in regular magic link authentication.\nconst loginId = 'desmond@descope.com';\nconst redirectURL = 'https://myapp.com/password-reset';\nconst passwordResetResponse = await descopeClient.password.sendReset(loginId, redirectURL);\n```\n\nThe magic link, in this case, must then be verified like any other magic link (see the [magic link section](#magic-link) for more details). However, after verifying the user, it is expected\nto allow them to provide a new password instead of the old one. Since the user is now authenticated, this is possible via:\n\n```js\n// The refresh token is required to make sure the user is authenticated.\nawait descopeClient.password.update(loginId, newPassword, token);\n```\n\n`update()` can always be called when the user is authenticated and has a valid session.\n\nAlternatively, it is also possible to replace an existing active password with a new one.\n\n```js\n// Replaces the user's current password with a new one\nconst jwtResponse = await descopeClient.password.replace(loginId, oldPassword, newPassword);\n// jwtResponse.data.sessionJwt;\n// jwtResponse.data.refreshJwt;\n```\n\n### Session Validation\n\nEvery secure request performed between your client and server needs to be validated. The client sends\nthe session and refresh tokens with every request, and they are validated using one of the following:\n\n```typescript\n// Validate the session. Will throw if expired\nconst authInfo = await descopeClient.validateSession('sessionToken');\n\n// If validateSession throws an exception, you will need to refresh the session using\nconst authInfo = await descopeClient.refreshSession('refreshToken');\n\n// Alternatively, you could combine the two and\n// have the session validated and automatically refreshed when expired\nconst authInfo = await descopeClient.validateAndRefreshSession('sessionToken', 'refreshToken');\n```\n\nChoose the right session validation and refresh combination that suits your needs.\nRefreshed sessions return the same response as is returned when users first sign up / log in,\ncontaining the session and refresh tokens, as well as all of the JWT claims.\nMake sure to return the session token from the response to the client if tokens are validated directly.\n\nUsually, the tokens can be passed in and out via HTTP headers or via a cookie.\nThe implementation can defer according to your implementation. See our [examples](#code-examples) for a few examples.\n\nIf Roles \u0026 Permissions are used, validate them immediately after validating the session. See the [next section](#roles--permission-validation)\nfor more information.\n\n#### Session Validation Using Middleware\n\nAlternatively, you can create a simple middleware function that internally uses the `validateSession` function.\nThis middleware will automatically parse the cookies from the request.\nOn failure, it will respond with `401 Unauthorized`.\n\n```typescript\nconst authMiddleware = async (req: Request, res: Response, next: NextFunction) =\u003e {\n  try {\n    const cookies = parseCookies(req);\n    const out = await clientAuth.auth.validateSession(\n      cookies[DescopeClient.SessionTokenCookieName],\n      cookies[DescopeClient.RefreshTokenCookieName],\n    );\n    if (out?.cookies) {\n      res.set('Set-Cookie', out.cookies);\n    }\n    next();\n  } catch (e) {\n    res.status(401).json({\n      error: 'Unauthorized!',\n    });\n  }\n};\n```\n\n### Roles \u0026 Permission Validation\n\nWhen using Roles \u0026 Permission, it's important to validate the user has the required\nauthorization immediately after making sure the session is valid. Taking the `AuthenticationInfo`\nreceived by the [session validation](#session-validation), call the following functions:\n\nFor multi-tenant uses:\n\n```typescript\n// You can validate specific permissions\nconst validTenantPermissions = descopeClient.validateTenantPermissions(authInfo, 'my-tenant-ID', [\n  'Permission to validate',\n]);\nif (!validTenantPermissions) {\n  // Deny access\n}\n\n// Or validate roles directly\nconst validTenantRoles = descopeClient.validateTenantRoles(authInfo, 'my-tenant-ID', [\n  'Role to validate',\n]);\nif (!validTenantRoles) {\n  // Deny access\n}\n\n// Or get the matched roles/permissions\nconst matchedTenantRoles = descopeClient.getMatchedTenantRoles(authInfo, 'my-tenant-ID', [\n  'Role to validate',\n  'Another role to validate',\n]);\n\nconst matchedTenantPermissions = descopeClient.getMatchedTenantPermissions(\n  authInfo,\n  'my-tenant-ID',\n  ['Permission to validate', 'Another permission to validate'],\n);\n```\n\nWhen not using tenants use:\n\n```typescript\n// You can validate specific permissions\nconst validPermissions = descopeClient.validatePermissions(authInfo, ['Permission to validate']);\nif (!validPermissions) {\n  // Deny access\n}\n\n// Or validate roles directly\nconst validRoles = descopeClient.validateRoles(authInfo, ['Role to validate']);\nif (!validRoles) {\n  // Deny access\n}\n\n// Or get the matched roles/permissions\nconst matchedRoles = descopeClient.getMatchedRoles(authInfo, [\n  'Role to validate',\n  'Another role to validate',\n]);\n\nconst matchedPermissions = descopeClient.getMatchedPermissions(authInfo, [\n  'Permission to validate',\n  'Another permission to validate',\n]);\n```\n\n### Logging Out\n\nYou can log out a user from an active session by providing their `refreshToken` for that session.\nAfter calling this function, you must invalidate or remove any cookies you have created.\n\n```typescript\nawait descopeClient.logout(refreshToken);\n```\n\nIt is also possible to sign the user out of all the devices they are currently signed-in with. Calling `logoutAll` will\ninvalidate all user's refresh tokens. After calling this function, you must invalidate or remove any cookies you have created.\n\n```typescript\nawait descopeClient.logoutAll(refreshToken);\n```\n\n## Management Functions\n\nIt is very common for some form of management or automation to be required. These can be performed\nusing the management functions. Please note that these actions are more sensitive as they are administrative\nin nature. Please use responsibly.\n\n### Setup\n\nTo use the management API you'll need a `Management Key` along with your `Project ID`.\nCreate one in the [Descope Console](https://app.descope.com/settings/company/managementkeys).\n\n```typescript\nimport DescopeClient from '@descope/node-sdk';\n\nconst descopeClient = DescopeClient({\n  projectId: 'my-project-ID',\n  managementKey: 'management-key',\n});\n```\n\n### Manage Tenants\n\nYou can create, update, delete or load tenants, as well as read and update tenant settings:\n\n```typescript\n// The self provisioning domains or optional. If given they'll be used to associate\n// Users logging in to this tenant\nawait descopeClient.management.tenant.create('My Tenant', ['domain.com'], {\n  customAttributeName: 'val',\n});\n\n// You can optionally set your own ID when creating a tenant\nawait descopeClient.management.tenant.createWithId('my-custom-id', 'My Tenant', ['domain.com'], {\n  customAttributeName: 'val',\n});\n\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.tenant.update(\n  'my-custom-id',\n  'My Tenant',\n  ['domain.com', 'another-domain.com'],\n  { customAttributeName: 'val' },\n);\n\n// Tenant deletion cannot be undone. Use carefully.\n// Pass true to cascade value, in case you want to delete all users/keys associated only with this tenant\nawait descopeClient.management.tenant.delete('my-custom-id', false);\n\n// Load tenant by id\nconst tenant = await descopeClient.management.tenant.load('my-custom-id');\n\n// Load all tenants\nconst tenantsRes = await descopeClient.management.tenant.loadAll();\ntenantsRes.data.forEach((tenant) =\u003e {\n  // do something\n});\n\n// Search all tenants according to various parameters\nconst searchRes = await descopeClient.management.tenant.searchAll(['id']);\nsearchRes.data.forEach((tenant) =\u003e {\n  // do something\n});\n\n// Load tenant settings by id\nconst tenantSettings = await descopeClient.management.tenant.getSettings('my-tenant-id');\n\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.tenant.configureSettings('my-tenant-id', {\n  domains: ['domain1.com'],\n  selfProvisioningDomains: ['domain1.com'],\n  sessionSettingsEnabled: true,\n  refreshTokenExpiration: 12,\n  refreshTokenExpirationUnit: 'days',\n  sessionTokenExpiration: 10,\n  sessionTokenExpirationUnit: 'minutes',\n  enableInactivity: true,\n  JITDisabled: false,\n  InactivityTime: 10,\n  InactivityTimeUnit: 'minutes',\n});\n\n// Generate tenant admin self service link for SSO Suite (valid for 24 hours)\n// ssoId can be provided for a specific sso configuration\n// email can be provided to send the link to (email's templateId can be provided as well)\nconst res = await descopeClient.management.tenant.generateSSOConfigurationLink(\n  'my-tenant-id',\n  60 * 60 * 24,\n);\nconsole.log(res.adminSSOConfigurationLink);\n```\n\n### Manage Password\n\nYou can read and update any tenant password settings and policy:\n\n```typescript\n// Load tenant password settings by id\nconst passwordSettings = await descopeClient.management.password.getSettings('my-tenant-id');\n\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.password.configureSettings('my-tenant-id', {\n  enabled: true,\n  minLength: 8,\n  expiration: true,\n  expirationWeeks: 4,\n  lock: true,\n  lockAttempts: 5,\n  reuse: true,\n  reuseAmount: 6,\n  lowercase: true,\n  uppercase: false,\n  number: true,\n  nonAlphaNumeric: false,\n});\n```\n\n### Manage SSO applications\n\nYou can create, update, delete or load SSO applications:\n\n```typescript\n// Create OIDC sso application\nawait descopeClient.management.ssoApplication.createOidcApplication({\n  name: 'My OIDC app name',\n  loginPageUrl: 'http://dummy.com/login',\n});\n\n// Create SAML sso application\nawait descopeClient.management.ssoApplication.createSamlApplication({\n  name: 'My SAML app name',\n  loginPageUrl: 'http://dummy.com/login',\n  useMetadataInfo: true,\n  metadataUrl: 'http://dummy.com/metadata',\n});\n\n// Update OIDC sso application.\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.ssoApplication.updateOidcApplication({\n  id: 'my-app-id',\n  name: 'My OIDC app name',\n  loginPageUrl: 'http://dummy.com/login',\n});\n\n// Update SAML sso application.\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.ssoApplication.updateSamlApplication({\n  id: 'my-app-id',\n  name: 'My SAML app name',\n  loginPageUrl: 'http://dummy.com/login',\n  enabled: true,\n  useMetadataInfo: false,\n  entityId: 'entity1234',\n  aceUrl: 'http://dummy.com/acs',\n  certificate: 'certificate',\n});\n\n// Tenant deletion cannot be undone. Use carefully.\nawait descopeClient.management.ssoApplication.delete('my-app-id');\n\n// Load sso application by id\nconst app = await descopeClient.management.ssoApplication.load('my-app-id');\n\n// Load all sso applications\nconst appsRes = await descopeClient.management.ssoApplication.loadAll();\nappsRes.data.forEach((app) =\u003e {\n  // do something\n});\n```\n\n### Manage Users\n\nYou can create, update, delete or load users, as well as search according to filters:\n\n```typescript\n// A user must have a login ID, other fields are optional.\n// Roles should be set directly if no tenants exist, otherwise set\n// on a per-tenant basis.\nawait descopeClient.management.user.create('desmond@descope.com', {\n  email: 'desmond@descope.com',\n  displayName: 'Desmond Copeland',\n  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],\n});\n\n// Alternatively, a user can be created and invited via an email / text message.\n// Make sure to configure the invite URL in the Descope console prior to using this function,\n// and that an email address / phone number is provided in the information.\nawait descopeClient.management.user.invite('desmond@descope.com', {\n  email: 'desmond@descope.com',\n  displayName: 'Desmond Copeland',\n  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],\n  // You can override the project's User Invitation Redirect URL with this parameter\n  inviteUrl: '\u003cinvite-url\u003e',\n  // You can inject custom data into the template.\n  // Note that you first need to configure custom template in Descope Console\n  // For example: configure {{options_k1}} in the custom template, and pass { k1: 'v1' } as templateOptions\n  templateOptions: { k1: 'v1', k2: 'v2' },\n});\n\n// You can invite batch of users via an email / text message.\n// Make sure to configure the invite URL in the Descope console prior to using this function,\n// and that an email address / phone number is provided in the information. You can also set\n// a cleartext password or import a prehashed one from another service.\nawait descopeClient.management.user.inviteBatch(\n  [\n    {\n      loginId: 'desmond@descope.com',\n      email: 'desmond@descope.com',\n      phone: '+123456789123',\n      displayName: 'Desmond Copeland',\n      userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],\n      hashedPassword: {\n        bcrypt: {\n          hash: '$2a$...',\n        },\n      },\n    },\n  ],\n  '\u003cinvite_url\u003e',\n  true,\n  false,\n);\n\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.user.update('desmond@descope.com', {\n  email: 'desmond@descope.com',\n  displayName: 'Desmond Copeland',\n  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],\n});\n\n// Update explicit data for a user rather than overriding all fields\nawait descopeClient.management.user.updatePhone('desmond@descope.com', '+18005551234', true);\nawait descopeClient.management.user.updateLoginId('desmond@descope.com', 'bane@descope.com');\nawait descopeClient.management.user.removeTenantRoles(\n  'desmond@descope.com',\n  'tenant-ID1',\n  'role-name2',\n);\n\n// Update explicit user's data using patch (will override only provided fields)\nconst options: PatchUserOptions = { displayName: 'Desmond Copeland Jr.' };\nawait descopeClient.management.user.patch('desmond@descope.com', options);\n\n// User deletion cannot be undone. Use carefully.\nawait descopeClient.management.user.delete('desmond@descope.com');\n\n// Load specific user\nconst userRes = await descopeClient.management.user.load('desmond@descope.com');\n\n// If needed, users can be loaded using the user ID as well\nconst userRes = await descopeClient.management.user.loadByUserId('\u003cuser-ID\u003e');\n\n// Search all users, optionally according to tenant and/or role filter\n// Results can be paginated using the limit and page parameters\nconst usersRes = await descopeClient.management.user.search({ tenantIds: ['tenant-ID'] });\nusersRes.data.forEach((user) =\u003e {\n  // do something\n});\n\nawait descopeClient.management.user.logoutUser('my-custom-id');\n\nawait descopeClient.management.user.logoutUserByUserId('\u003cuser-ID\u003e');\n\n// Get users' authentication history\nconst userIds = ['user-id-1', 'user-id-2'];\nconst usersHistoryRes = await descopeClient.management.user.history(userIds);\nusersHistoryRes.forEach((userHistory) =\u003e {\n  // do something\n});\n```\n\n#### Set or Expire User Password\n\nYou can set a new active password for a user that they can sign in with.\nYou can also set a temporary password that they user will be forced to change on the next login.\nFor a user that already has an active password, you can expire their current password, effectively requiring them to change it on the next login.\n\n```typescript\n// Set a user's temporary password\nawait descopeClient.management.user.setTemporaryPassword('\u003clogin-ID\u003e', '\u003csome-password\u003e');\n\n// Set a user's password\nawait descopeClient.management.user.setActivePassword('\u003clogin-ID\u003e', '\u003csome-password\u003e');\n\n// Or alternatively, expire a user password\nawait descopeClient.management.user.expirePassword('\u003clogin-ID\u003e');\n```\n\n### Manage Project\n\nYou can update project name and tags, as well as clone the current project to a new one:\n\n```typescript\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.project.updateName('new-project-name');\n\n// Set will override all fields as is. Use carefully.\nawait descopeClient.management.project.updateTags(['tag1!', 'new']);\n\n// Clone the current project to a new one\n// Note that this action is supported only with a pro license or above.\nconst cloneRes = await descopeClient.management.project.clone('new-project-name');\n```\n\nWith using a company management key you can get a list of all the projects in the company:\n\n```typescript\nconst projects = await descopeClient.management.project.listProjects();\n```\n\nYou can manage your project's settings and configurations by exporting a snapshot:\n\n```typescript\n// Exports the current state of the project\nconst exportRes = await descopeClient.management.project.exportSnapshot();\n```\n\nYou can also import previously exported snapshots into the same project or a different one:\n\n```typescript\nconst validateReq = {\n  files: exportRes.files,\n};\n\n// Validate that an exported snapshot can be imported into the current project\nconst validateRes = await descopeClient.management.project.import(files);\nif (!validateRes.ok) {\n  // validation failed, check failures and missingSecrets to fix this\n}\n\n// Import the previously exported snapshot into the current project\nconst importReq = {\n  files: exportRes.files,\n};\n\nawait descopeClient.management.project.importSnapshot(files);\n```\n\n### Manage Access Keys\n\nYou can create, update, delete or load access keys, as well as search according to filters:\n\n```typescript\n// An access key must have a name and expiration, other fields are optional.\n// Roles should be set directly if no tenants exist, otherwise set\n// on a per-tenant basis.\n// If userId is supplied, then authorization will be ignored, and the access key will be bound to the user's authorization.\n// If customClaims is supplied, then those claims will be present in the JWT returned by calls to ExchangeAccessKey.\n// If description is supplied, then the access key will hold a descriptive text.\n// If permittedIps is supplied, then the access key can only be used from that list of IP addresses or CIDR ranges.\nawait descopeClient.management.accessKey.create(\n  'key-name',\n  123456789, // expiration time\n  null,\n  [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],\n);\n\n// Load specific user\nconst accessKeyRes = await descopeClient.management.accessKey.load('key-id');\n\n// Search all users, optionally according to tenant and/or role filter\nconst accessKeysRes = await descopeClient.management.accessKey.searchAll(['tenant-ID']);\naccessKeysRes.data.forEach((accessKey) =\u003e {\n  // do something\n});\n\n// Update will override all fields as is. Use carefully.\nawait descopeClient.management.accessKey.update('key-id', 'new-key-name', 'new-description');\n\n// Access keys can be deactivated to prevent usage. This can be undone using \"activate\".\nawait descopeClient.management.accessKey.deactivate('key-id');\n\n// Disabled access keys can be activated once again.\nawait descopeClient.management.accessKey.activate('key-id');\n\n// Access key deletion cannot be undone. Use carefully.\nawait descopeClient.management.accessKey.delete('key-id');\n```\n\n### Manage SSO Setting\n\nYou can manage SSO settings and map SSO group roles and user attributes.\n\n```typescript\n// You can get SSO settings for a specific tenant ID\n// You can pass ssoId in case using multi SSO and you want to load specific SSO configuration\nconst ssoSettings = await descopeClient.management.sso.loadSettings('tenant-id');\n\n// You can get all configured SSO settings for a specific tenant ID (for multi SSO usage)\nconst allSSOSettings = await descopeClient.management.sso.loadAllSettings('tenant-id');\n\n// You can configure SSO settings manually by setting the required fields directly\n// You can pass ssoId in case using multi SSO and you want to configure specific SSO configuration\nconst tenantId = 'tenant-id'; // Which tenant this configuration is for\nconst idpURL = 'https://idp.com';\nconst entityID = 'my-idp-entity-id';\nconst idpCert = '\u003cyour-cert-here\u003e';\nconst redirectURL = 'https://my-app.com/handle-sso'; // Global redirect URL for SSO/SAML\nconst domains = ['tenant-users.com']; // Users authentication with this domain will be logged in to this tenant\nawait descopeClient.management.sso.configureSAMLSettings(\n  tenantID,\n  { idpURL, entityID, idpCert },\n  redirectURL,\n  domains,\n);\n\n// Alternatively, configure using an SSO metadata URL\n// You can pass ssoId in case using multi SSO and you want to configure specific SSO configuration\nawait descopeClient.management.sso.configureSAMLByMetadata(\n  tenantID,\n  { idpMetadataUrl: 'https://idp.com/my-idp-metadata' },\n  redirectURL,\n  domains,\n);\n\n// In case SSO is configured to work with OIDC use the following\n// You can pass ssoId in case using multi SSO and you want to configure specific SSO configuration\nconst name = 'some-name';\nconst clientId = 'client id of OIDC';\nconst clientSecret = 'client secret';\nawait descopeClient.management.sso.configureOIDCSettings(\n  tenantID,\n  { name, clientId, clientSecret, redirectUrl },\n  domains,\n);\n\n// You can create new SSO configuration (aka multi SSO)\nconst ssoId = 'my-new-additional-sso-id';\nconst displayName = 'My additional SSO configuration';\nawait descopeClient.management.sso.newSettings(tenantID, ssoId, displayName);\n\n// You can delete existing SSO configuration\n// You can pass ssoId in case using multi SSO and you want to delete specific SSO configuration\nawait descopeClient.management.sso.deleteSettings(tenantID);\n```\n\nNote: Certificates should have a similar structure to:\n\n```\n-----BEGIN CERTIFICATE-----\nCertifcate contents\n-----END CERTIFICATE-----\n```\n\n// You can delete SSO settings for a specific tenant ID\nawait descopeClient.management.sso.deleteSettings(\"tenant-id\")\n\n### Manage Permissions\n\nYou can create, update, delete or load permissions:\n\n```typescript\n// You can optionally set a description for a permission.\nconst name = 'My Permission';\nlet description = 'Optional description to briefly explain what this permission allows.';\nawait descopeClient.management.permission.create(name, description);\n\n// Update will override all fields as is. Use carefully.\nconst newName = 'My Updated Permission';\ndescription = 'A revised description';\nawait descopeClient.management.permission.update(name, newName, description);\n\n// Permission deletion cannot be undone. Use carefully.\nawait descopeClient.management.permission.delete(newName);\n\n// Load all permissions\nconst permissionsRes = await descopeClient.management.permission.loadAll();\npermissionsRes.data.forEach((permission) =\u003e {\n  // do something\n});\n```\n\n### Manage Roles\n\nYou can create, update, delete or load roles:\n\n```typescript\n// You can optionally set a description and associated permission for a roles.\n// The optional `tenantId` will scope this role for a specific tenant. If left empty, the role will be available to all tenants.\nconst name = 'My Role';\nconst tenantId = '\u003ctenant id\u003e';\nlet description = 'Optional description to briefly explain what this role allows.';\nconst permissionNames = ['My Updated Permission'];\ndescopeClient.management.role.create(name, description, permissionNames, tenantId);\n\n// Update will override all fields as is. Use carefully.\nconst newName = 'My Updated Role';\ndescription = 'A revised description';\npermissionNames.push('Another Permission');\ndescopeClient.management.role.update(name, newName, description, permissionNames, tenantId);\n\n// Role deletion cannot be undone. Use carefully.\ndescopeClient.management.role.delete(newName, tenantId);\n\n// Load all roles\nconst rolesRes = await descopeClient.management.role.loadAll();\nrolesRes.data.forEach((role) =\u003e {\n  // do something\n});\n\n// Search roles\nconst rolesRes = await descopeClient.management.role.search({\n  tenantIds: ['t1', 't2'],\n  roleNames: ['role1'],\n});\nrolesRes.data.forEach((role) =\u003e {\n  // do something\n});\n```\n\n### Query SSO Groups\n\nYou can query SSO groups:\n\n```typescript\n// Load all groups for a given tenant id\nconst groupsRes = descopeClient.management.group.loadAllGroups('tenant-id');\n\n// Load all groups for the given user IDs (can be found in the user's JWT)\nconst groupsRes = descopeClient.management.group.loadAllGroupsForMember('tenant-id', [\n  'user-id-1',\n  'user-id-2',\n]);\n\n// Load all groups for the given user login IDs (used for sign-in)\nconst groupsRes = descopeClient.management.group.loadAllGroupsForMember(\n  'tenant-id',\n  [],\n  ['login-id-1', 'login-id-2'],\n);\n\n// Load all group's members by the given group id\nconst groupsRes = descopeClient.management.group.loadAllGroupMembers('tenant-id', 'group-id');\n\ngroupsRes.data.forEach((group) =\u003e {\n  // do something\n});\n```\n\n### Manage Flows\n\nYou can list your flows and also import and export flows and screens, or the project theme:\n\n```typescript\n// List all project flows\nconst res = await descopeClient.management.flow.list();\nconsole.log('found total flows', res.total);\nres.flows.forEach((flowMetadata) =\u003e {\n  // do something\n});\n\n// Delete flows by ids\nawait descopeClient.management.flow.delete(['flow-1', 'flow-2']);\n\n// Export the flow and it's matching screens based on the given id\nconst res = await descopeClient.management.flow.export('sign-up');\nconsole.log('found flow', res.data.flow);\nres.data.screens.forEach((screen) =\u003e {\n  // do something\n});\n\n// Import the given flow and screens as the given id\nconst { flow, screens } = res.data;\nconst updatedRes = descopeClient.management.flow.import('sign-up', flow, screens);\nconsole.log('updated flow', updatedRes.data.flow);\nupdatedRes.data.screens.forEach((screen) =\u003e {\n  // do something\n});\n\n// Export the current theme of the project\nconst res = descopeClient.management.theme.export();\nconsole.log(res.data.theme);\n\n// Import the given theme to the project\nconst updatedRes = descopeClient.management.theme.import(theme);\nconsole.log(updatedRes.data.theme);\n```\n\n### Manage JWTs\n\nYou can add custom claims to a valid JWT.\n\n```typescript\nconst updatedJWTRes = await descopeClient.management.jwt.update('original-jwt', {\n  customKey1: 'custom-value1',\n  customKey2: 'custom-value2',\n});\n```\n\n### Impersonate\n\nYou can impersonate to another user\nThe impersonator user must have the `impersonation` permission in order for this request to work.\nThe response would be a refresh JWT of the impersonated user\n\n```typescript\nconst updatedJWTRes = await descopeClient.management.jwt.impersonate(\n  'impersonator-id',\n  'login-id',\n  true,\n  { k1: 'v1' },\n  't1',\n);\n```\n\nNote 1: The generate code/link functions, work only for test users, will not work for regular users.\nNote 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.\n\n### Embedded Links\n\nEmbedded links can be created to directly receive a verifiable token without sending it.\nThis token can then be verified using the magic link 'verify' function, either directly or through a flow.\n\n```typescript\nconst { token } = await descopeClient.management.user.generateEmbeddedLink('desmond@descope.com', {\n  key1: 'value1',\n});\n```\n\n### Audit\n\nYou can perform an audit search for either specific values or full-text across the fields. Audit search is limited to the last 30 days.\n\n```typescript\n// Full text search on the last 10 days\nconst audits = await descopeClient.management.audit.search({\n  from: Date.now() - 10 * 24 * 60 * 60 * 1000,\n  text: 'some-text',\n});\nconsole.log(audits);\n\n// Search successful logins in the last 30 days\nconst audits = await descopeClient.management.audit.search({ actions: ['LoginSucceed'] });\nconsole.log(audits);\n```\n\nYou can also create audit event with data\n\n```typescript\nawait descopeClient.management.audit.createEvent({\n  action: 'pencil.created',\n  type: 'info', // info/warn/error\n  actorId: 'UXXX',\n  tenantId: 'tenant-id',\n  data: {\n    some: 'data',\n  },\n});\n```\n\n### Manage FGA (Fine-grained Authorization)\n\nDescope supports full relation based access control (ReBAC) using a zanzibar like schema and operations.\nA 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.\n\nA simple example for a file system like schema would be:\n\n```yaml\nmodel AuthZ 1.0\n\ntype user\n\ntype org\n  relation member: user\n  relation parent: org\n\ntype folder\n  relation parent: folder\n  relation owner: user | org#member\n  relation editor: user\n  relation viewer: user\n\n  permission can_create: owner | parent.owner\n  permission can_edit: editor | can_create\n  permission can_view: viewer | can_edit\n\ntype doc\n  relation parent: folder\n  relation owner: user | org#member\n  relation editor: user\n  relation viewer: user\n\n  permission can_create: owner | parent.owner\n  permission can_edit: editor | can_create\n  permission can_view: viewer | can_edit\n```\n\nDescope 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.\n\n```typescript\nconst descopeClient = require('@descope/node-sdk');\n\n// Save schema\nawait descopeClient.management.fga.saveSchema(schema);\n\n// Create a relation between a resource and user\nawait descopeClient.management.fga.createRelations([\n  {\n    resource: 'some-doc',\n    resourceType: 'doc',\n    relation: 'owner',\n    target: 'u1',\n    targetType: 'user',\n  },\n]);\n\n// Check if target has a relevant relation\n// The answer should be true because an owner can also view\nconst relations = await descopeClient.management.fga.check([\n  {\n    resource: 'some-doc',\n    resourceType: 'doc',\n    relation: 'can_view',\n    target: 'u1',\n    targetType: 'user',\n  },\n]);\n```\n\n### Utils for your end to end (e2e) tests and integration tests\n\nTo ease your e2e tests, we exposed dedicated management methods,\nthat 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.\n\n```typescript\n// User for test can be created, this user will be able to generate code/link without\n// the need of 3rd party messaging services.\n// Test user must have a loginId, other fields are optional.\n// Roles should be set directly if no tenants exist, otherwise set\n// on a per-tenant basis.\nawait descopeClient.management.user.createTestUser('desmond@descope.com', {\n  email: 'desmond@descope.com',\n  displayName: 'Desmond Copeland',\n  userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],\n});\n\n// Search all test users according to various parameters\nconst searchRes = await descopeClient.management.user.searchTestUsers(['id']);\nsearchRes.data.forEach((user) =\u003e {\n  // do something\n});\n\n// Now test user got created, and this user will be available until you delete it,\n// you can use any management operation for test user CRUD.\n// You can also delete all test users.\nawait descopeClient.management.user.deleteAllTestUsers();\n\n// OTP code can be generated for test user, for example:\nconst { code } = await descopeClient.management.user.generateOTPForTestUser(\n  'sms', // you can use also 'email', 'whatsapp', 'voice'\n  'desmond@descope.com',\n);\n// Now you can verify the code is valid (using descopeClient.auth.*.verify for example)\n// LoginOptions can be provided to set custom claims to the generated jwt.\n\n// Same as OTP, magic link can be generated for test user, for example:\nconst { link } = await descopeClient.management.user.generateMagicLinkForTestUser(\n  'email',\n  'desmond@descope.com',\n  '',\n);\n\n// Enchanted link can be generated for test user, for example:\nconst { link, pendingRef } = await descopeClient.management.user.generateEnchantedLinkForTestUser(\n  'desmond@descope.com',\n  '',\n);\n```\n\n## Code Examples\n\nYou can find various usage examples in the [examples folder](/examples).\n\n### Setup\n\nTo run the examples, set your `Project ID` by setting the `DESCOPE_PROJECT_ID` env var or directly\nin the sample code.\nFind your Project ID in the [Descope console](https://app.descope.com/settings/project).\n\n```bash\nexport DESCOPE_PROJECT_ID=\u003cProjectID\u003e\n```\n\n### Run an example\n\nRun the following commands in the root of the project to build and run the examples with a local build\nof the SDK.\n\n1. Run this to start the ES6 typescript module example\n\n   ```bash\n   npm i \u0026\u0026 \\\n   npm run build \u0026\u0026 \\\n   cd examples/es6 \u0026\u0026 \\\n   npm i \u0026\u0026 \\\n   npm run generateCerts \u0026\u0026 \\\n   npm start\n   ```\n\n2. Run this to start the commonjs example\n\n   ```bash\n   npm i \u0026\u0026 \\\n   npm run build \u0026\u0026 \\\n   cd examples/commonjs \u0026\u0026 \\\n   npm i \u0026\u0026 \\\n   npm run generateCerts \u0026\u0026 \\\n   npm start\n   ```\n\n## Providing Custom Public Key\n\nBy 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.\n\nYou can find your public key in the `https://api.descope.com/v2/keys/\u003cproject-id\u003e` endpoint. For further information, please see the [Descope Documentation and API reference page](https://docs.descope.com/api/openapi/sessiongetkeys/operation/GetKeysV2).\n\nTo provide your own public key, you can do so by providing the `publicKey` option when initializing the SDK:\n\n```typescript\nimport DescopeClient from '@descope/node-sdk';\n\nconst descopeClient = DescopeClient({\n  projectId: 'my-project-ID',\n  publicKey: '{\"alg\":\"RS256\", ... }',\n});\n\n// The public key will be used when validating jwt\nconst sessionJWt = '\u003csession-jwt\u003e';\nawait descopeClient.validateJwt(sessionJWt);\n```\n\n## Learn More\n\nTo learn more please see the [Descope Documentation and API reference page](https://docs.descope.com/).\n\n## Contact Us\n\nIf you need help you can email [Descope Support](mailto:support@descope.com)\n\n## License\n\nThe Descope SDK for Node.js is licensed for use under the terms and conditions of the [MIT license Agreement](https://github.com/descope/node-sdk/blob/main/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdescope%2Fnode-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdescope%2Fnode-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdescope%2Fnode-sdk/lists"}