Backend Session Validation

This page focuses on backend session validation. Backend session validation is an integral part of secure session management, especially when dealing with APIs or more intricate use cases.

This approach validates the session token at the server-side, thereby ensuring that the token has not been tampered with and is valid.

It helps to verify that the incoming requests are indeed from authenticated users and not from potential attackers.

If you're looking to set up frontend validation, check out our Client Validation page. For an in-depth understanding of session validation in Descope, refer to our session management article.

For validating the session, you need to integrate the Descope Backend SDK with your application server. To use the backend SDK, first install the SDK using your package manager. After installing use the code below to add the session validation.

Backend SDK

Install SDK

Terminal
npm i --save @descope/node-sdk

Import and initialize SDK

import DescopeClient from '@descope/node-sdk';
try{
    //  baseUrl="<URL>" // When initializing the Descope clientyou can also configure the baseUrl ex: https://auth.company.com  - this is useful when you utilize CNAME within your Descope project.
    const descopeClient = DescopeClient({ projectId: '__ProjectID__' });
} catch (error) {
    // handle the error
    console.log("failed to initialize: " + error)
}
 
// Note that you can handle async operation failures and capture specific errors to customize errors.
//     An example can be found here: https://github.com/descope/node-sdk?tab=readme-ov-file#error-handling

Validate Session

The session validation code below should be added to your application middleware (if you are using application middleware) for validating the session on all required routes. If you are not using a middleware, then you can add the validation code to all the routes which serve protected resource. Before validation, you need to extract the session token from the request authorization header.

Note that you can customize certain properties like refresh token timeout in Settings>Projects. Learn more here.

// Args:
//   sessionToken (str): The session token, which contains the signature that will be validated
const sessionToken="xxxx" // extract from request authorization header. The above sample code sends the the session token in authorization header.
 
try {
  const authInfo = await descopeSdk.validateSession(sessionToken);
  console.log("Successfully validated user session:");
  console.log(authInfo);
} catch (error) {
  console.log ("Could not validate user session " + error);
}

Logout Current Session using Backend SDK

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.

// Args:
//    refreshToken: refresh token from the successful sign-in of the user
const refreshToken = "xxxx"
 
const resp = await descopeClient.logout(refreshToken);
if (!resp.ok) {
  console.log("Failed to log user out of current session.")
  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 logged user out of current session.")
  console.log(resp.data)
}

Logout All Sessions using Backend SDK

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

// Args:
//    refreshToken: refresh token from the successful sign-in of the user
const refreshToken = "xxxx"
 
const resp = await descopeClient.logoutAll(refreshToken);
if (!resp.ok) {
  console.log("Failed to log user out of all current sessions.")
  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 logged user out of all current sessions.")
  console.log(resp.data)
}

Note

To learn more about how refresh token storage works, check out our Refresh Token Storage page.

Validating roles and permissions

If your applications does not need different roles for the logged in users, then skip this section.

If you are using authorization feature in Descope, then you can use the Descope backend sdk to validate roles and permissions. The roles and permissions for the user are returned in the session token. You can either use your own JWT validation library and access the tenant information, roles and permissions for the authenticated user or use the SDK calls shown below.

If you are using tenant capabilities in Descope, then use the tenant validation calls for validating permissions and roles.

Roles

The "Roles" section pertains to the process of validating user roles within an application. These roles are typically defined and allocated by an administrator and are used to manage user access to certain resources or actions within an application. The validateRoles function is used to confirm whether a user has a valid role, based on the role names provided as arguments. The roles to be validated are provided as a string array. This function returns a Boolean value indicating whether the user's roles are valid (true) or invalid (false).

// validateRoles function
// Args:
//   authenticationInfo : authentication information with claims returned from the validateSession call
//   roles: string array containing names of roles to be validated
const roles=[]
 
// Return value : Boolean (true or false)
try {
    const isRoleValid =  descopeClient.validateRoles(authInfo, roles);
    if (isRoleValid) {
        console.log("These roles are valid for user")
    } else {
        console.log("These roles are invalid for user")
    }
} catch (error) {
    console.log ("Could not confirm if roles are valid - error prior to confirmation. " + error);
}

Permissions

The "Permissions" section discusses the mechanism of checking user permissions. Permissions, similar to roles, regulate what actions a user can perform within a system, but they are often more granular and specific. The validatePermissions function is used to validate if a user has specific permissions based on the permission names provided as arguments. Like with roles, the permissions to be validated are supplied as a string array. The function then returns a Boolean value, where 'true' indicates the user has the validated permissions, and 'false' suggests the opposite.

// validatePermissions function
// Args:
//   authenticationInfo : authentication information with claims returned from the validateSession call
//   roles: string array containing names of roles to be validated
const permissions=[]
 
// Return value : Boolean (true or false)
try {
    const isPermissionValid =  descopeClient.validatePermissions(authInfo, permissions);
    if (isPermissionValid) {
        console.log("These permissions are valid for user")
    } else {
        console.log("These permissions are invalid for user")
    }
} catch (error) {
    console.log ("Could not confirm if permissions are valid - error prior to confirmation. " + error);
}

Roles with Tenant Function

The "Roles with Tenant Function" section deals with the validation of user roles within the context of a specific tenant. In multi-tenant environments, where a single instance of software serves multiple users or groups of users (tenants), users might have different roles depending on the tenant they are interacting with. The validateTenantRoles function is used to check if a user's role is valid for a specific tenant. This function takes in the tenant's ID and the roles to be validated, and it returns a Boolean value indicating the validity of the roles for the specified tenant.

// validateRoles with tenant function
// Args:
//   authenticationInfo : authentication information with claims returned from the validateSession call
//   tenantId : tenant id for the tenant the user is logging in
const tenantId="xxx"
 
//   roles: string array containing names of roles to be validated
const roles=[]
 
// Return value : Boolean (true or false)
try {
    const isRoleValid =  descopeClient.validateTenantRoles(authInfo, tenantId, roles);
    if (isRoleValid) {
        console.log("These roles are valid for tenant " + tenantId)
    } else {
        console.log("These roles are invalid for tenant " + tenantId)
    }
} catch (error) {
    console.log ("Could not confirm if roles are valid - error prior to confirmation. " + error);
}

Permissions with Tenant Function

The "Permissions with Tenant Function" section revolves around the process of verifying user permissions within the context of a specific tenant. Similar to the role validation, permission validation in multi-tenant environments can be tenant-specific. The validateTenantPermissions function is used to determine whether a user's permissions are valid for a particular tenant. This function takes in the tenant's ID and the permissions to be validated, returning a Boolean value that represents the validity of these permissions for the specified tenant.

// validatePermissions with tenant function
// Args:
//   authenticationInfo : authentication information with claims returned from the validateSession call
//   tenantId : tenant id for the tenant the user is logging in
const tenantId="xxx"
 
//   roles: string array containing names of roles to be validated
const permissions=[]
 
// Return value : Boolean (true or false)
try {
    const isPermissionValid =  descopeClient.validateTenantPermissions(authInfo, tenantId, permissions);
    if (isPermissionValid) {
        console.log("These permissions are valid for tenant " + tenantId)
    } else {
        console.log("These permissions are invalid for tenant " + tenantId)
    }
} catch (error) {
    console.log ("Could not confirm if permissions are valid - error prior to confirmation. " + error);
}

Retrieving Matching Roles and Permissions

To get matching roles or permissions of a user, you can use the Descope backend sdk, as shown below.

The roles and permissions for the user are returned in the session token. So alternatively, you can even use your own JWT validation library.

If you are using tenant capabilities in Descope, then use the tenant matching calls for getting matching permissions and roles.

Roles

Roles are extracted from JWT claims based on specified criteria. Below is an example of how to retrieve roles using different programming languages:

 /**
 * Retrieves the roles from JWT top level claims that match the specified roles list
 * @param authInfo JWT parsed info containing the roles
 * @param roles List of roles to match against the JWT claims
 * @returns An array of roles that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
 */
try {
    const matchedRoles = descopeClient.getMatchedRoles(authInfo, [
    'Role to validate',
    'Another role to validate',
    ]);
   
} catch (error) {
    console.log ("Could not confirm if roles are valid - error prior to confirmation. " + error);
}

Permissions

Permissions are similarly retrieved from JWT claims and checked against a specified list. Example implementations across different languages are provided below:

/**
 * Retrieves the permissions from JWT top level claims that match the specified permissions list
 * @param authInfo JWT parsed info containing the permissions
 * @param permissions List of permissions to match against the JWT claims
 * @returns An array of permissions that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
 */
try {
    const matchedPermissions = descopeClient.getMatchedPermissions(authInfo, [
    'Permission to validate',
    'Another permission to validate',
    ]);
    if (isPermissionValid) {
        console.log("These permissions are valid for user")
    } else {
        console.log("These permissions are invalid for user")
    }
} catch (error) {
    console.log ("Could not confirm if permissions are valid - error prior to confirmation. " + error);
}

Roles with Tenant Function

When using tenant-specific functionalities, roles can be matched as follows:

/**
* Retrieves the roles from JWT tenant claims that match the specified roles list
* @param authInfo JWT parsed info containing the roles
* @param tenant tenant to match the roles for
* @param roles List of roles to match against the JWT claims
* @returns An array of roles that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
*/
try {
    const matchedTenantRoles = descopeClient.getMatchedTenantRoles(authInfo, 'my-tenant-ID', [
    'Role to validate',
    'Another role to validate',
    ]);
} catch (error) {
    console.log ("Could not confirm if roles are valid - error prior to confirmation. " + error);
}

Permissions with Tenant Function

Matching permissions within a specific tenant context is handled similarly:

/**
 * Retrieves the permissions from JWT top level claims that match the specified permissions list
 * @param authInfo JWT parsed info containing the permissions
 * @param permissions List of permissions to match against the JWT claims
 * @returns An array of permissions that are both in the JWT claims and the specified list. Returns an empty array if no matches are found
 */
try {
   const matchedTenantPermissions = descopeClient.getMatchedTenantPermissions(
    authInfo,
    'my-tenant-ID',
    ['Permission to validate', 'Another permission to validate'],
    );
} catch (error) {
    console.log ("Could not confirm if permissions are valid - error prior to confirmation. " + error);
}
Was this helpful?

On this page