JWTs with SDKs
You can use the Descope management SDK for JWT operations like adding custom claims, generating session tokens for users, et cetera. The management SDK requires a management key, which can be generated here.
Install SDK
npm i --save @descope/node-sdkImport and Initialize Management SDK
import DescopeClient from '@descope/node-sdk';
const managementKey = "xxxx"
try{
// baseUrl="<URL>" // When initializing the Descope client you can also configure the baseUrl ex: https://auth.company.com - this is useful when you utilize a custom domain within your Descope project.
const descopeClient = DescopeClient({ projectId: '__ProjectID__', managementKey: managementKey });
} catch (error) {
// handle the error
console.log("failed to initialize: " + error)
}
Update JWT With Custom Claims
Note
Learn more about configuring custom token claims in our Token Claims Management docs.
This operation updates a valid JWT with the custom claims you provide. You can optionally set how long the updated JWT remains valid using refreshDuration (in seconds). The new JWT will be returned.
// Args:
// jwt (string): The JWT to update (required).
// customClaims (object): Optional, custom claims to add to the JWT
// refreshDuration (number): Optional, duration in seconds for which the new JWT will be valid
const jwt = "original-jwt"
const customClaims = {
"custom-key1": "custom-value1",
"custom-key2": "custom-value2",
}
const refreshDuration = 3600
const resp = await descopeClient.management.jwt.update(jwt, customClaims, refreshDuration)
if (!resp.ok) {
console.log("Failed to update JWT")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
} else {
console.log("Successfully updated JWT")
console.log(resp.data.jwt)
}Generate JWT for Generic Auth Sign In
This operation is only for Sign-In, and if the login ID doesn't already exist it will return an error and ask you to sign up the user first.
This operation programmatically mints a Descope session (session and refresh JWTs) for an existing user (identified by loginID) independent of a specific auth method.
The result is equivalent to that produced by a successful SignIn operation.
Use this if you verify identity elsewhere and want Descope tokens for an existing user, if you need backend-initiated sessions, or to generate valid tokens for automated testing. This requires a management key and must only be called from trusted server-side code.
Note
You can also perform this operation through the Generate JWT for Sign-In Management API.
// Args:
// loginId (string): The login ID of the existing user to sign in (required).
// loginOptions (object): Optional, options to customize the generated session (custom claims, refreshDuration, etc.)
const loginId = "user@example.com"
const loginOptions = {
customClaims: {
"custom-key1": "custom-value1",
},
refreshDuration: 3600, // Optional, duration in seconds for which the session will be valid
}
const resp = await descopeClient.management.jwt.signIn(loginId, loginOptions)
if (!resp.ok) {
console.log("Failed to generate JWT for sign in")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
} else {
console.log("Successfully generated JWT for sign in")
console.log(resp.data.sessionJwt)
console.log(resp.data.refreshJwt)
}Generate JWT for Generic Auth Sign Up
This operation is only for Sign-Up, and if the login ID already exists it will return an error and ask you to sign in the user.
This operation programmatically mints a Descope session (session and refresh JWTs) for a new user independent of a specific auth method.
The result is equivalent to that produced by a successful SignUp operation.
This operation is especially useful for backend-driven user migrations, where you have already collected user details from another system and want to seamlessly onboard users into Descope. It allows you to validate the user’s session and issue a Descope token in a single step. This requires a management key and must only be called from trusted server-side code.
Note
You can also perform this operation through the Generate JWT for Sign-Up Management API.
// Args:
// loginId (string): The login ID of the user to create (required).
// user (object): Optional, user details such as email, phone, and name.
// signUpOptions (object): Optional, options to customize the generated session (custom claims, refreshDuration, etc.)
const loginId = "user@example.com"
const user = {
email: "user@example.com",
phone: "+15551234567",
name: "Jane Doe",
}
const signUpOptions = {
customClaims: {
"custom-key1": "custom-value1",
},
refreshDuration: 3600, // Optional, duration in seconds for which the session will be valid
}
const resp = await descopeClient.management.jwt.signUp(loginId, user, signUpOptions)
if (!resp.ok) {
console.log("Failed to generate JWT for sign up")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
} else {
console.log("Successfully generated JWT for sign up")
console.log(resp.data.sessionJwt)
console.log(resp.data.refreshJwt)
}Generate JWT for Generic Auth Sign Up or In
This operation programmatically mints a Descope session (session and refresh JWTs) for a user identified by loginID, creating the user if they do not exist or signing in an existing user if they do, independent of a specific auth method.
The result is equivalent to that produced by a successful SignUpOrIn operation.
Use this when you want a single backend code path that works for both first-time and returning users. This requires a management key and must only be called from trusted server-side code.
Note
You can also perform this operation through the Generate JWT for Sign-Up or Sign-In Management API.
// Args:
// loginId (string): The login ID of the user to sign up or in (required).
// user (object): Optional, user details such as email, phone, and name (used only if the user is created).
// signUpOptions (object): Optional, options to customize the generated session (custom claims, refreshDuration, etc.)
const loginId = "user@example.com"
const user = {
email: "user@example.com",
phone: "+15551234567",
name: "Jane Doe",
}
const signUpOptions = {
customClaims: {
"custom-key1": "custom-value1",
},
refreshDuration: 3600, // Optional, duration in seconds for which the session will be valid
}
const resp = await descopeClient.management.jwt.signUpOrIn(loginId, user, signUpOptions)
if (!resp.ok) {
console.log("Failed to generate JWT for sign up or in")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
} else {
console.log("Successfully generated JWT for sign up or in")
console.log(resp.data.sessionJwt)
console.log(resp.data.refreshJwt)
}Generate Client Assertion JWT for OAuth
Note
You can learn more about Private Key JWT authentication here.
This operation mints a short-lived, signed client assertion JWT that your application can use to authenticate itself to an OAuth 2.0 authorization server (for example, in the private_key_jwt client authentication method or the client credentials flow).
Instead of sending a static client secret, your backend presents this signed JWT as proof of the client's identity.
This requires a management key and must only be called from trusted server-side code.
// Args:
// issuer (string): The issuer of the JWT, typically the client ID (required).
// subject (string): The subject of the JWT, typically the client ID (required).
// audience (string[]): The intended audience, typically the authorization server token endpoint (required).
// expiresIn (number): Number of seconds the token will be valid for (required).
// flattenAudience (boolean): Optional, set the audience claim as a single string instead of an array (when only one value is provided).
// algorithm (string): Optional, signing algorithm - one of 'RS256', 'RS384', 'ES384' (default is 'RS256').
const issuer = "https://example.com/issuer"
const subject = "client-id-123"
const audience = ["https://example.com/token"]
const expiresIn = 300
const flattenAudience = false
const algorithm = "RS256"
const resp = await descopeClient.management.jwt.generateClientAssertionJwt(
issuer,
subject,
audience,
expiresIn,
flattenAudience,
algorithm,
)
if (!resp.ok) {
console.log("Failed to generate client assertion JWT")
console.log("Status Code: " + resp.code)
console.log("Error Code: " + resp.error.errorCode)
console.log("Error Description: " + resp.error.errorDescription)
console.log("Error Message: " + resp.error.errorMessage)
} else {
console.log("Successfully generated client assertion JWT")
console.log(resp.data.jwt)
}