Session Validation
Descope offers SDK for many languages that can validate the session token after successful authentication. The session management article gives an overview of the session validation in Descope. The video tutorial below covers various configurations and ways to validate the session token.
Sending session token to application server
If you are using Client SDK or using Descope Flows (Integration Approach 1 or Approach 2), then your application client must send the session token to you application server. One of the important point to note is that the session token also contains information about user including tenants, roles and permissions. Based on your application situation, this may make the session token larger than allowed size for a cookie. This is why Descope defaults to storing the session token in browser local storage. You can fetch the session token and send to your application server using the sample code below.
import { getSessionToken } from '@descope/react-sdk';
const sessionToken = getSessionToken();
// example fetch call with authentication header
fetch('your_application_server_url', {
headers: {
Accept: 'application/json',
Authorization: 'Bearer '+sessionToken,
}
})
const sessionToken = sdk.getSessionToken();
// example fetch call with authentication header
fetch('your_application_server_url', {
headers: {
Accept: 'application/json',
Authorization: 'Bearer '+sessionToken,
}
})
At any time, your application client should only send the session token to your application and the application server should validate the session token using Descope Backend SDK.
Session Validation With Backend SDK
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.
Install SDK
npm i --save @descope/node-sdk
pip3 install descope
go get github.com/descope/go-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)
}
from descope import (
REFRESH_SESSION_TOKEN_NAME,
SESSION_TOKEN_NAME,
AuthException,
DeliveryMethod,
DescopeClient,
AssociatedTenant,
RoleMapping,
AttributeMapping,
LoginOptions
)
try:
# You can configure the baseURL by setting the env variable Ex: export DESCOPE_BASE_URI="https://auth.company.com - this is useful when you utilize CNAME within your Descope project."
descope_client = DescopeClient(project_id='__ProjectID__')
except Exception as error:
# handle the error
print ("failed to initialize. Error:")
print (error)
import "github.com/descope/go-sdk/descope"
import "github.com/descope/go-sdk/descope/client"
// DescopeBaseURL // within the client.Config, you can also configure the baseUrl ex: https://auth.company.com - this is useful when you utilize CNAME within your Descope project.
descopeClient, err := client.NewWithConfig(&client.Config{ProjectID:"__ProjectID__"})
if err != nil {
// handle the error
log.Println("failed to initialize: " + err.Error())
}
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);
}
# Args:
# session_token (str): The session token, which contains the signature that will be validated
session_token="xxxx" # extract from request authorization header. The above sample code sends the the session token in authorization header.
try:
jwt_response = descope_client.validate_session(session_token=session_token)
print ("Successfully validated user session:")
print (jwt_response)
except Exception as error:
print ("Could not validate user session. Error:")
print (error)
// Args:
// sessionToken (str): The session token, which contains the signature that will be validated
sessionToken := "xxxx" // extract from request authorization header. The above sample code sends the the session token in authorization header.
authorized, userToken, err := descopeClient.Auth.ValidateSessionWithToken(sessionToken)
if (err != nil){
fmt.Println("Could not validate user session: ", err)
} else {
fmt.Println("Successfully validated user session: ", userToken)
}
Refresh Token Storage
Depending on the project configuration, the Descope service can return the refresh token in two different ways -
- Manage in cookies - Descope Client SDK sets the refresh token as a cookie automatically. This option is more secure and is the recommended method for managing refresh token in production environments. For this option to work - you must also configure a CNAME record for the custom domain listed, which will securely restrict access to the refresh token that is stored.
- Manage in response body - Descope Client SDK returns refresh token in the body and stores in browser local storage. This option does not require configuring a custom domain and is only recommended for testing or working in a development environment.
Logout using Client SDK
If you are integrating using the Descope Client SDK (Approach 1 or Approach 2), then you must use the Client SDK to logout. If you are using Descope Flows (Approach 1) with React SDK, refer to the Quick Start for details. If you are Descope Client SDK without flows, then refer to the sample code below for logout.
// Logout from the current session
const resp = await descopeSdk.logout();
// Logout from all the sessions
const resp = await descopeSdk.logoutAll();
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.message)
}
else {
console.log("Successfully logged user out of current session.")
console.log(resp.data)
}
# Args:
# refresh_token: refresh token from the successful sign-in of the user
try:
resp = descope_client.logout(refresh_token)
print ("Successfully logged user out of current session.")
except AuthException as error:
print ("Failed to log user out of current session.")
print ("Status Code: " + str(error.status_code))
print ("Error: " + str(error.error_message))
// Args:
// r: HttpRequest for the action. Refresh token will be taken from the request header or cookies automatically
// w (optional): If provided, the optional `w http.ResponseWriter` will empty out the session cookies automatically.
err := descopeClient.logout(request, w)
if (err != nil){
fmt.Println("Failed to log user out of current session: ", err)
} else {
fmt.Println("Successfully logged user out of current session.")
}
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.message)
}
else {
console.log("Successfully logged user out of all current sessions.")
console.log(resp.data)
}
# Args:
# refresh_token: refresh token from the successful sign-in of the user
try:
resp = descope_client.logout_all(refresh_token)
print ("Successfully logged user out of all current sessions.")
except AuthException as error:
print ("Failed to log user out of all current sessions.")
print ("Status Code: " + str(error.status_code))
print ("Error: " + str(error.error_message))
// Args:
// r: HttpRequest for the action. Refresh token will be taken from the request header or cookies automatically
// w (optional): If provided, the optional `w http.ResponseWriter` will empty out the session cookies automatically.
err := descopeClient.logoutAll(request, w)
if (err != nil){
fmt.Println("Failed to log user out of all current sessions: ", err)
} else {
fmt.Println("Successfully logged user out of all current sessions.")
}
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.
// 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);
}
// 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);
}
// 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);
}
// 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);
}
# validate_roles function
# Args:
# jwt_response: The response that you get from the validate_session call.
# roles: list of strings - each string represents a role name that is validated.
roles=[]
# Return value: Boolean (True or False)
try:
isRoleValid = descope_client.validate_roles(jwt_response, roles)
if isRoleValid == True:
print ("These roles are valid for user")
else:
print ("These roles are invalid for user")
except Exception as error:
print ("Could not confirm if roles are valid - error prior to confirmation. Error:")
print (error)
# validate_permissions function
# Args:
# jwt_response: The response that you get from the validate_session call.
# permissions: list of strings - each string represents a role name that is validated.
permissions=[]
# Return value: Boolean (True or False)
try:
isPermissionValid = descope_client.validate_permissions(jwt_response, permissions)
if isPermissionValid == True:
print ("These permissions are valid for user")
else:
print ("These permissions are invalid for user")
except Exception as error:
print ("Could not confirm if permissions are valid - error prior to confirmation. Error:")
print (error)
# validate_tenant_roles function
# Args:
# jwt_response: The response that you get from the validate_session call.
# tenant_id: tenant id for the tenant the user is logging in
tenant_id="xxx"
# roles: list of strings - each string represents a role name that is validated.
roles=[]
# Return value: Boolean (True or False)
try:
isRoleValid = descope_client.validate_tenant_roles(jwt_response, tenant_id, roles)
if isRoleValid == True:
print ("These roles are valid for tenant " + tenant_id)
else:
print ("These roles are invalid for tenant " + tenant_id)
except Exception as error:
print ("Could not confirm if roles are valid - error prior to confirmation. Error:")
print (error)
# validate_tenant_permissions function
# Args:
# jwt_response: The response that you get from the validate_session call.
# tenant_id: tenant id for the tenant the user is logging in
tenant_id="xxx"
# permissions: list of strings - each string represents a role name that is validated.
permissions=[]
# Return value: Boolean (True or False)
try:
isPermissionValid = descope_client.validate_tenant_permissions(jwt_response, tenant_id, permissions)
if isPermissionValid == True:
print ("These permissions are valid for tenant " + tenant_id)
else:
print ("These permissions are invalid for tenant " + tenant_id)
except Exception as error:
print ("Could not confirm if permissions are valid - error prior to confirmation. Error:")
print (error)
// ValidateRoles validates the roles that are given as an array using the userToken returned from ValidateSession function
// Args:
// userToken: token returned from ValidateSession call.
// roles: list of roles to validate
roles:= []
// Return value:
// bool: true or false depending of the roles match or not
// err - has the error message (can be populated even if session is valid)
// If the session is not valid, the function returns an error.
if isValidRoles, err := descopeClient.Auth.ValidateRoles(userToken, roles); !isValidRoles {
// roles are invalid for the user
//WIP log.Println("These roles are invalid for the user")
}
// ValidatePermissions validates the roles that are given as an array using the userToken returned from ValidateSession function
// Args:
// userToken: token returned from ValidateSession call.
// permissions: list of permissions to validate
permissions:=[]
// Return value:
// bool: true or false depending of the permissions match or not
// err - has the error message (can be populated even if session is valid)
// If the session is not valid, the function returns an error.
if isValidPermissions, err := descopeClient.Auth.ValidatePermissions(userToken, permissions); !isValidPermissions {
// permissions are invalid for the user
//WIP log.Println("These permissions are invalid for the user")
}
// ValidateTenantRoles validates the roles that are given as an array using the userToken returned from ValidateSession function
// Args:
// userToken: token returned from ValidateSession call.
// tenantID: tenant id for the tenant the user is logging in
tenantID:="xxx"
// roles: list of roles to validate
roles:= []
// Return value:
// bool: true or false depending of the roles match or not
// err - has the error message (can be populated even if session is valid)
// If the session is not valid, the function returns an error.
if isValidRoles, err := descopeClient.Auth.ValidateTenantRoles(userToken, tenantID, roles); !isValidRoles {
// roles are invalid for the tenant
//WIP log.Println("These roles are invalid for tenant " + tenantID)
}
// ValidateTenantPermissions validates the roles that are given as an array using the userToken returned from ValidateSession function
// Args:
// userToken: token returned from ValidateSession call.
// tenantID: tenant id for the tenant the user is logging in
tenantID:="xxx"
// permissions: list of permissions to validate
permissions:=[]
// Return value:
// bool: true or false depending of the permissions match or not
// err - has the error message (can be populated even if session is valid)
// If the session is not valid, the function returns an error.
if isValidPermissions, err := descopeClient.Auth.ValidateTenantPermissions(userToken, tenantID, permissions); !isValidPermissions {
// permissions are invalid for the tenant
//WIP log.Println("These permissions are invalid for tenant " + tenantID)
}