Audit
The Descope Audit Trail provides comprehensive logging of all security-relevant events within your Descope project. This guide explains how to access, search, and utilize the audit trail for security monitoring and compliance purposes.
Descope supports streaming your audit trail to a third-party service. To learn more about this concept, review the Audit Trail Streaming knowledge base article.
You can search the Descope audit trail via the Descope SDK as shown below, or you can utilize the Descope console or the Search Audit API endpoint.
For a full list of audit events that are logged, refer to our Audit Events doc.
For SCIM provisioning, Descope can emit detailed audit rows (SCIMEvent) that include request bodies, results, and field-level changes. See SCIM Audit Events.
Searching the Descope Audit Trail via SDK
Rate Limiting
Descope enforces a rate limit of 10 requests per minute for audit search operations.
Backend SDK
Install SDK
npm i --save @descope/node-sdkpip3 install descopego get github.com/descope/go-sdk// Include the following in your `pom.xml` (Maven)
<dependency>
<artifactId>java-sdk</artifactId>
<groupId>com.descope</groupId>
<version>sdk-version</version> // Check https://github.com/descope/descope-java/releases for the latest versions
</dependency>gem install descopecomposer require descope/descope-phpdotnet add package descopeImport and initialize SDK
import DescopeClient from '@descope/node-sdk';
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__' });
} 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 custom domain 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"
// Utilizing the context package allows for the transmission of context capabilities like cancellation
// signals during the function call. In cases where context is absent, the context.Background()
// function serves as a viable alternative.
// Utilizing context within the Descope GO SDK is supported within versions 1.6.0 and higher.
import (
"context"
)
// DescopeBaseURL // within the client.Config, you can also configure the baseUrl ex: https://auth.company.com - this is useful when you utilize a custom domain 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())
}import com.descope.client.Config;
import com.descope.client.DescopeClient;
var descopeClient = new DescopeClient(Config.builder().projectId("__ProjectID__").build());require 'descope'
@project_id = ENV['__ProjectID__']
@client = Descope::Client.new({ project_id: @project_id})require 'vendor/autoload.php';
use Descope\SDK\DescopeSDK;
$descopeSDK = new DescopeSDK([
'projectId' => $_ENV['__ProjectID__'],
]);// appsettings.json
{
"Descope": {
"ProjectId": "__ProjectID__",
"ManagementKey": "DESCOPE_MANAGEMENT_KEY"
}
}
// Program.cs
using Descope;
using Microsoft.Extensions.Configuration;
// ... In your setup code
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var descopeProjectId = config["Descope:ProjectId"];
var descopeManagementKey = config["Descope:ManagementKey"];
var descopeConfig = new DescopeConfig(projectId: descopeProjectId);
var descopeClient = new DescopeClient(descopeConfig)
{
ManagementKey = descopeManagementKey,
};Search Audits
// Args:
// searchOptions: (AuditSearchOptions): A completed descope structure with the desired audit search options.
const searchOptions = {
userIDs: ["xxxxxx"],
actions: ["LoginSucceed"],
excludedActions: null, // List of actions to exclude
// from: time.Tim, // Retrieve records newer than given time. Limited to no older than 30 days.
// to: time.Time, // Retrieve records older than given time.
devices: null, // List of devices to filter by. Current devices supported are "Bot"/"Mobile"/"Desktop"/"Tablet"/"Unknown"
methods: null, // List of methods to filter by. Current auth methods are "otp"/"totp"/"magiclink"/"oauth"/"saml"/"password"
geos: null, // List of geos to filter by. Geo is currently country code like "US", "IL", etc.
remoteAddresses: null, // List of remote addresses to filter by
loginIDs: null, // List of login IDs to filter by
tenants: null, // List of tenants to filter by
noTenants: true, // Should audits without any tenants always be included
// text: "John" // Free text search across all fields
}
const resp = await descopeClient.management.audit.search(searchOptions)
if (!resp.ok) {
console.log("Failed to search audits.")
}
else {
console.log("Successfully searched audits.")
console.log(resp)
}# Args:
# user_ids (List[str]): Optional list of user IDs to filter by
user_ids = ["xxxxxx"]
# actions (List[str]): Optional list of actions to filter by
actions = ["LoginSucceed"]
# excluded_actions (List[str]): Optional list of actions to exclude
excluded_actions = None
# devices (List[str]): Optional list of devices to filter by. Current devices supported are "Bot"/"Mobile"/"Desktop"/"Tablet"/"Unknown"
devices = None
# methods (List[str]): Optional list of methods to filter by. Current auth methods are "otp"/"totp"/"magiclink"/"oauth"/"saml"/"password"
methods = None
# geos (List[str]): Optional list of geos to filter by. Geo is currently country code like "US", "IL", etc.
geos = None
# remote_addresses (List[str]): Optional list of remote addresses to filter by
remote_addresses = None
# login_ids (List[str]): Optional list of login IDs to filter by
login_ids = None
# tenants (List[str]): Optional list of tenants to filter by
tenants = None
# no_tenants (bool): Should audits without any tenants always be included
no_tenants = True
# text (str): Free text search across all fields
text = None
# from_ts (datetime): Retrieve records newer than given time but not older than 30 days
from_ts = None
# to_ts (datetime): Retrieve records older than given time
to_ts = None
try:
resp = descope_client.mgmt.audit.search(user_ids=user_ids, actions=actions, excluded_actions=excluded_actions, devices=devices, methods=methods, geos=geos, remote_addresses=remote_addresses, login_ids=login_ids, tenants=tenants, text=text, from_ts=from_ts, to_ts=to_ts)
print ("Successfully searched audits")
print (resp)
except AuthException as error:
print ("Failed to search audits")
print ("Status Code: " + str(error.status_code))
print ("Error: " + str(error.error_message))// Args:
// ctx: context.Context - Application context for the transmission of context capabilities like
// cancellation signals during the function call. In cases where context is absent, the context.Background()
// function serves as a viable alternative.
// Utilizing context within the Descope GO SDK is supported within versions 1.6.0 and higher.
ctx := context.Background()
// searchOptions: (AuditSearchOptions): A completed descope structure with the desired audit search options.
searchOptions := &descope.AuditSearchOptions{}
searchOptions.UserIDs = []string{"xxxxxx"}
searchOptions.Actions = []string{"LoginSucceed"}
searchOptions.ExcludedActions = nil // List of actions to exclude
// searchOptions.From = time.Time // Retrieve records newer than given time. Limited to no older than 30 days.
// searchOptions.To = time.Time // Retrieve records older than given time.
searchOptions.Devices = nil // List of devices to filter by. Current devices supported are "Bot"/"Mobile"/"Desktop"/"Tablet"/"Unknown"
searchOptions.Methods = nil // List of methods to filter by. Current auth methods are "otp"/"totp"/"magiclink"/"oauth"/"saml"/"password"
searchOptions.Geos = nil // List of geos to filter by. Geo is currently country code like "US", "IL", etc.
searchOptions.RemoteAddresses = nil // List of remote addresses to filter by
searchOptions.LoginIDs = nil // List of login IDs to filter by
searchOptions.Tenants = nil // List of tenants to filter by
searchOptions.NoTenants = true // Should audits without any tenants always be included
// searchOptions.Text = "John" // Free text search across all fields
res, err := descopeClient.Management.Audit().Search(ctx, searchOptions)
if (err != nil){
fmt.Println("Unable to search audits: ", err)
} else {
fmt.Println("Successfully searched audits: ")
for _, auditEvent := range res {
fmt.Println(auditEvent)
}
}
return errAuditService as = descopeClient.getManagementServices().getAuditService();
// Full text search on the last 10 days
try {
AuditSearchResponse resp = as.search(AuditSearchRequest.builder()
.from(Instant.now().minus(Duration.ofDays(10))));
} catch (DescopeException de) {
// Handle the error
}
// Search successful logins in the last 30 days
try {
AuditSearchResponse resp = as.search(AuditSearchRequest.builder()
.from(Instant.now().minus(Duration.ofDays(30)))
.actions(Arrays.asList("LoginSucceed")));
} catch (DescopeException de) {
// Handle the error
}// Any of the following arguments can be used as a search term for the function,
// not all of them need to be included in a search
$response = $descopeSDK->management->audit->search(
"userIds", // List of user IDs to filter by.
"actions", // List of actions to filter by.
"excludedActions", // List of actions to exclude.
"devices", // List of devices to filter by (e.g., "Bot", "Mobile", "Desktop").
"methods", // List of methods to filter by (e.g., "otp", "totp", "magiclink").
"geos", // List of geographical locations to filter by (country codes).
"remoteAddresses", // List of remote addresses to filter by.
"loginIds", // List of login IDs to filter by.
"tenants", // List of tenants to filter by.
"noTenants", // Whether to include audits without tenants.
"text", // Free text search across all fields.
"fromTs", // Retrieve records newer than this timestamp.
"toTs" // Retrieve records older than this timestamp.
);
print_r($response);User Update Audit Detail
It's crucial to see user configuration changes in your audit trail. To help you track these changes, Descope
logs the new values of the changed UserModified actions. Below is an example of the detail provided within
the audit trail of a UserModified action with various changes.
{
"Change": {
"added_multi_tenant_roles": [
"xx"
],
"added_roles": [
"xx"
],
"custom_attribute_emailConsent": true,
"custom_attribute_myAttribute": true,
"display_name": "Test Me",
"family_name": "Test",
"given_name": "Me",
"middle_name": "Middle",
"phone": "12223334455"
},
"correlation_id": "xx",
"request_details": {
"contentLength": "956",
"headers": {
"descope": {
"cf-bot-score": "99",
"cf-connecting-ip": "xx",
"cf-ja3-hash": "xx",
"cf-ray": "xx-DFW",
"cf-verified-bot": "false",
"x-request-id": "xx"
},
"http": {
"origin": "https://app.descope.com",
"referer": "https://app.descope.com/",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
}
},
"host": "console.descope.com",
"method": "POST",
"uri": "/console/v1/users/xx",
"url": "/console/v1/users/xx"
}
}