Access Keys

Introduction

Access keys enable machine-to-machine authentication for your application. The access keys in Descope behave similarly to users. When users sign in to your application using your application front-end, a JWT token is delivered to the browser. By contrast, for machine-to-machine communication, the machine connecting to your application presents an access key, and a JWT token is returned to the connecting machine. The connecting machine can then use this JWT token to make API calls to your application. Your application backend can validate the session token as covered in the session management article.

Each access key has an expiration duration and can be associated with tenants and roles. The tenants and roles association work similarly to the user management. The expiration duration is used to calculate the key's expiry time, and the exchanged session token will be valid until the expiry time. The access keys can be created or deleted using the Descope console interface or the management SDK.

You can also configure the Access Key Session Token Timeout within project settings. This timeout controls how long the session token is valid after exchanging the access key for a session token.

Access Key Lifecycle

Access keys will continue to function as long as they are active and not expired. Once the access key is expired or deactivated, it will no longer be usable. Within the UI, you can deactivate (revoke) access keys; however, the access key will remain in the Descope project and may be reactivated if you choose to reactivate them. You can also delete access keys. Once an access key is deleted, it will no longer be usable. Deleting access keys will remove the access key's details from the Descope project.

Associating Access Key to Users

Access keys can be created or deleted within the Descope console. While generating the key, you need to provide the name, expiration, tenants, and roles associated with it. You can also set the user of the access key with the management SDK or via the user access key management widget. The rest of the options to edit, delete, or deactivate are provided within the UI. Also note that, when exchanging access keys for a JWT, you can set custom claims, including the user ID, on the JWT token.

Access key management using the management SDK

Install SDK

NodeJSPythonGoJavaRuby
npm i --save @descope/node-sdk
pip3 install descope
go get github.com/descope/go-sdk
// Include the following in your `pom.xml` (for 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 descope

Import and initialize Management SDK

NodeJSPythonGoJavaRuby
import DescopeClient from '@descope/node-sdk';

const managementKey = "xxxx"

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__', managementKey: managementKey });
} 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
from descope import (
    REFRESH_SESSION_TOKEN_NAME,
    SESSION_TOKEN_NAME,
    AuthException,
    DeliveryMethod,
    DescopeClient,
    AssociatedTenant,
    RoleMapping,
    AttributeMapping
)

management_key = "xxxx"

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__', management_key=management_key)
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"
import "fmt"

// 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"
)

managementKey = "xxxx"

// 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__", managementKey:managementKey})
if err != nil {
    // handle the error
    log.Println("failed to initialize: " + err.Error())
}
import com.descope.client;

// Initialized after setting the DESCOPE_PROJECT_ID env var (and optionally DESCOPE_MANAGEMENT_KEY)
var descopeClient = new DescopeClient();

// ** Or directly **
var descopeClient = new DescopeClient(Config.builder()
        .projectId("__ProjectID__")
        .managementKey("management-key")
        .build());
require 'descope'


descope_client = Descope::Client.new(
  {
    project_id: '__ProjectID__',
    management_key: 'management_key'
  }
)

Create Access Key

This operation is used to create an access key. At the time of creation of the access key, you can provide the name, expiration duration, tenants, and roles associated with the key. After the successful creation of the key, the response object contains id of the key and key value. The key value is only delivered at the creation time from Descope service, and your application must store or deliver to the connecting machine based on your use case. Your application can use the key id for updating the name, deleting, etc. An access key must have a name and expiration, other fields are optional. Roles should be set directly if no tenants exist, otherwise set on a per-tenant basis.

NodeJSPythonGoJava
// Args:
//   name (str): Access key name.
const name = "xxxx"
//   expireTime (int): Access key expiration. Leave at 0 to make it indefinite.
const expireTime = 0
//   roles (List[str]): An optional list of the access key's roles without tenant association. These roles are mutually exclusive with the `key_tenant` roles, which take precedence over them.
const roles = ["TestRole1"]
// userId (str): An optional user id to associate to a role. If the user is disabled or deleted - it will affect the access key accordingly.
const userId = 'yyyy'
//   keyTenants (List[AssociatedTenant[]]): An optional list of the access key's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
const keyTenants = [{tenantId: 'TestTenant'}]
// CustomClaims Record<string, any>: An optional record of custom attributes to add on the top level of the jwt for the access key.
const customClaims = {'key':'value'}

// Create with associated roles rather than tenants
// const resp = await descopeClient.management.accessKey.create(name, expireTime, roles, userId, null, customClaims)

// Create with associated tenants rather than roles
const resp = await descopeClient.management.accessKey.create(name, expireTime, userId, null, keyTenants, customClaims)
if (!resp.ok) {
  console.log("Failed to create access key.")
  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 created access key.")
  console.log(resp.data)
}
# Args:
#   name (str): Access key name.
name = "xxxx"
#   expire_time (int): Access key expiration. Leave at 0 to make it indefinite.
expire_time = 0
#   role_names (List[str]): An optional list of the access key's roles without tenant association. These roles are mutually exclusive with the `key_tenant` roles, which take precedence over them.
role_names = ["TestRole"]
// user_id (str): An optional user id to associate to a role. If the user is disabled or deleted - it will affect the access key accordingly.
const user_id = 'yyyy'
#   key_tenants (List[AssociatedTenant]): An optional list of the access key's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
key_tenants = [AssociatedTenant("TestTenant")]
#custom_claims (Dict): An optional dictionary of custom attributes to add on the top level of the jwt for the access key.
custom_claims = {"key":"value"}

try:
  # Create with associated roles rather than tenants
  # resp = descope_client.mgmt.access_key.create(name=name, expire_time=expire_time, role_names=role_names, user_id=user_id, custom_claims=custom_claims)

  # Create with associated tenants rather than roles
  resp = descope_client.mgmt.access_key.create(name=name, expire_time=expire_time, key_tenants=key_tenants, user_id=user_id, custom_claims=custom_claims)

  print("Successfully created access key.")
  print("Access Key info. Save the returned cleartext securely, it will not be returned again.")
  print(json.dumps(resp, indent=4))
except AuthException as error:
  print ("Unable to create access key.")
  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()
//   name (str): Access key name.
name := "xxxx"
//   expireTime (int64): Access key expiration. Leave at 0 to make it indefinite.
var expireTime int64 = 0
// userId (str): An optional user id to associate to a role. If the user is disabled or deleted - it will affect the access key accordingly.
const userId := "yyyy"
//   roles (List[str]): An optional list of the access key's roles without tenant association. These roles are mutually exclusive with the `key_tenant` roles, which take precedence over them.
roles := []string{"TestRole1"}
//   keyTenants (List[AssociatedTenant]): An optional list of the access key's tenants, and optionally, their roles per tenant. These roles are mutually exclusive with the general `role_names`, and take precedence over them.
keyTenants := []*descope.AssociatedTenant{{TenantID: "TestTenant"}}
// customClaims map[string] : An optional dictionary of custom attributes to add on the top level of the jwt for the access key.
customClaims := map[string]any{"key": "value"}

// Create with associated roles rather than tenants
// cleartext, res, err := descopeClient.Management.AccessKey().Create(ctx, name, expireTime, roles, userId, nil, customClaims)

// Create with associated tenants rather than roles
cleartext, res, err := descopeClient.Management.AccessKey().Create(ctx, name, expireTime, userId, nil, keyTenants, customClaims)

if (err != nil){
  fmt.Println("Unable to create access key: ", err)
} else {
  fmt.Println("Successfully created access key: ")
  fmt.Println("Access Key Created with ID: ", res.ID)
  fmt.Println("Cleartext:", cleartext)
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();
try {
    // custom claims map initialization
    Map<String, Object> customClaims = new HashMap<String, Object>()
    {
      {
          put("key", "value");
      }
    };
    // Create a new access key with a name, delay time, and tenant
    AccessKeyResponse resp = aks.create("access-key-1", 0,
            Arrays.asList("Role names"),
            Arrays.asList(
                new Tenant("tenant-ID1",
                    "Key Tenant",
                    Arrays.asList(new AssociatedTenant("tenant-ID2", Arrays.asList("Role names"))))),
                    "userId",
                    // add custom claims here if needed
                    customClaims);
} catch (DescopeException de) {
    // Handle the error
}

Load Access Key

The Descope SDK allows administrators to use a management key to load details of an existing access key.

NodeJSPythonGoJava
// Args:
//   id (str): The id of the access key to be loaded.
const id = "xxxx"

const resp = await descopeClient.management.accessKey.load(id);
if (!resp.ok) {
  console.log("Failed to load access key.")
  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 loaded access key.")
  console.log(resp.data)
}
# Args:
#   id (str): The id of the access key to be loaded.
id = "xxxx"

try:
  resp = descope_client.mgmt.access_key.load(id=id)
  print("Successfully loaded access key.")
  print("Access Key info:")
  print(json.dumps(resp, indent=4))
except AuthException as error:
  print ("Unable to load access key.")
  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()
//   id (str): The id of the access key to be loaded.
id := "xxxx"

res, err := descopeClient.Management.AccessKey().Load(ctx, id)
if (err != nil){
  fmt.Println("Unable to load access key: ", err)
} else {
  fmt.Println("Successfully loaded access key: ", res)
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();

// Load specific user
try {
    AccessKeyResponse resp = aks.load("access-key-1");
} catch (DescopeException de) {
    // Handle the error
}

Search Access Keys

The Descope SDK allows administrators to use a management key to search for existing access keys. Administrators can search all or search based on a specific tenantIds.

NodeJSPythonGoJava
// Args:
//   tenantIds (List[str]): Optional list of tenant IDs to filter by
const tenantIds = ["TestTenant"]

// Search all access keys:
// const resp = await descopeClient.management.accessKey.searchAll(null)

// Search keys based on tenantIds
const resp = await descopeClient.management.accessKey.searchAll(tenantIds)
if (!resp.ok) {
  console.log("Failed to search access keys.")
  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 searched access keys.")
  console.log(resp.data)
}
# Args:
#   tenant_ids (List[str]): Optional list of tenant IDs to filter by
tenant_ids = ["TestTenant"]

try:
  # Search all access keys:
  # resp = descope_client.mgmt.access_key.search_all_access_keys())

  # Search keys based on tenant_ids
  resp = descope_client.mgmt.access_key.search_all_access_keys(tenant_ids=tenant_ids)

  print("Successfully searched access keys.")
  print("Access Key info:")
  print(json.dumps(resp, indent=4))
except AuthException as error:
  print ("Unable to search access keys.")
  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()
//   tenantIDs (List[str]): Optional list of tenant IDs to filter by
tenantIDs := []string{"TestTenant"}

// Search all access keys:
// res, err := descopeClient.Management.AccessKey().SearchAll(ctx, nil)

// Search keys based on tenantIDs
res, err := descopeClient.Management.AccessKey().SearchAll(ctx, tenantIDs)
if (err != nil){
  fmt.Println("Unable to search access keys: ", err)
} else {
  fmt.Println("Successfully searched access key:")
  for _, u := range res {
    fmt.Println(u)
  }
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();

// Search all users, optionally according to tenant and/or role filter
try {
    AccessKeyResponseList resp = aks.searchAll(Arrays.asList("Tenant IDs"));
    for (AccessKeyResponse r : aks.getKeys()) {
        // Do something
    }
} catch (DescopeException de) {
    // Handle the error
}

Update Access Key

The Descope SDK allows administrators to use a management key to update the name of an existing access key. It is important to note that all parameters are used as overrides to the existing access key; empty fields will override populated fields.

NodeJSPythonGoJava
// Args:
//   id (str): The id of the access key to update.
const id = "xxxx"
//   name (str): The updated access key name.
const name = "xxxx"

const resp = await descopeClient.management.accessKey.update(id, name);
if (!resp.ok) {
  console.log("Failed to update access key.")
  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 access key.")
  console.log(resp.data)
}
# Args:
#   id (str): The id of the access key to update.
id = "xxxx"
#   name (str): The updated access key name.
name = "xxxx"

try:
  resp = descope_client.mgmt.access_key.update(id=id, name=name)
  print("Successfully updated access key.")
except AuthException as error:
  print ("Unable to update access keys.")
  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()
//   id (str): The id of the access key to update.
id := "xxxx"
//   name (str): The updated access key name.
name := "xxxx"

_, err := descopeClient.Management.AccessKey().Update(ctx, id, name)
if (err != nil){
  fmt.Println("Unable to update access key: ", err)
} else {
  fmt.Println("Successfully updated access key.")
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();

// Update will override all fields as is. Use carefully.
try {
    AccessKeyResponse resp = aks.update("access-key-1", "updated-name");
} catch (DescopeException de) {
    // Handle the error
}

Activate Access Key

The Descope SDK allows administrators to use a management key to activate an existing access key that is currently deactivated.

NodeJSPythonGoJava
// Args:
//   id (str): The id of the access key to be activated.
const id = "xxxx"

const resp = await descopeClient.management.accessKey.activate(id);
if (!resp.ok) {
  console.log("Failed to activate access key.")
  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 activated access key.")
  console.log(resp.data)
}
# Args:
#   id (str): The id of the access key to be activated.
id = "xxxx"

try:
  resp = descope_client.mgmt.access_key.activate(id=id)
  print("Successfully activated access key.")
except AuthException as error:
  print ("Unable to activate access keys.")
  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()
//   id (str): The id of the access key to be activated.
id := "xxxx"

err := descopeClient.Management.AccessKey().Activate(ctx, id)
if (err != nil){
  fmt.Println("Unable to activate access key: ", err)
} else {
  fmt.Println("Successfully activated access key.")
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();

// Access keys can be deactivated to prevent usage. This can be undone using "activate".
try {
    AccessKeyResponse resp = aks.activate("access-key-1");
} catch (DescopeException de) {
    // Handle the error
}

Deactivate Access Key

The Descope SDK allows administrators to use a management key to deactivate an existing access key. After deactivating an access key, it will no longer be usable. The key will persist within the project, and can be activated again if needed.

NodeJSPythonGoJava
// Args:
//   id (str): The id of the access key to be deactivated.
const id = "xxxx"

const resp = await descopeClient.management.accessKey.deactivate(id);
if (!resp.ok) {
  console.log("Failed to deactivate access key.")
  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 deactivated access key.")
  console.log(resp.data)
}
# Args:
#   id (str): The id of the access key to be deactivated.
id = "xxxx"

try:
  resp = descope_client.mgmt.access_key.deactivate(id=id)
  print("Successfully deactivated access key.")
except AuthException as error:
  print ("Unable to deactivate access keys.")
  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()
//   id (str): The id of the access key to be deactivated.
id := "xxxx"

err := descopeClient.Management.AccessKey().Deactivate(ctx, id)
if (err != nil){
  fmt.Println("Unable to deactivate access key: ", err)
} else {
  fmt.Println("Successfully deactivated access key.")
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();

// Disabled access keys can be activated once again.
try {
    AccessKeyResponse resp = aks.deactivate("access-key-1");
} catch (DescopeException de) {
    // Handle the error
}

Delete Access Key

The Descope SDK allows administrators to use a management key to delete an existing access key. Once an access key is deleted, it is removed from the project and no longer usable. This action is irreversible.

NodeJSPythonGoJava
// Args:
//   id (str): The id of the access key to be deleted.
const id = "xxxx"

const resp = await descopeClient.management.accessKey.delete(id);
if (!resp.ok) {
  console.log("Failed to delete access key.")
  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 deleted access key.")
  console.log(resp.data)
}
# Args:
#   id (str): The id of the access key to be deleted.
id = "xxxx"

try:
  resp = descope_client.mgmt.access_key.delete(id=id)
  print("Successfully deleted access key.")
except AuthException as error:
  print ("Unable to delete access keys.")
  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()
//   id (str): The id of the access key to be deleted.
id := "xxxx"

err := descopeClient.Management.AccessKey().Delete(ctx, id)
if (err != nil){
  fmt.Println("Unable to delete access key: ", err)
} else {
  fmt.Println("Successfully deleted access key.")
}
// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AccessKeyService aks = descopeClient.getManagementServices().getAccessKeyService();

// Access key deletion cannot be undone. Use carefully.
try {
    aks.delete("access-key-1");
} catch (DescopeException de) {
    // Handle the error
}

Exchange Access Keys

The Descope SDK allows for exchanging access keys for a JWT token. For machine-to-machine communication, the machine connecting to your application presents an access key, and a JWT token is returned to the connecting machine. The connecting machine can then use this JWT token to make API calls to your application. You can also provide this method with a custom claims object, that will eventually nest the custom claims inside the nsec key. The custom claims that sit under nsec are in a less secure state by definition in contrast to custom claims that are created with the access key itself.
NodeJSPythonGoJava
// Args:
//   accessKey (str): The access key
const accessKey = "xxxx"
// loginOptions: Optional advanced controls over login parameters, e.g. custom claims:
const loginOptions = {customClaims: {"key":"value"}}

try {
    const authInfo = await descopeClient.exchangeAccessKey(mykey, loginOptions);
    console.log(`Exchanged access key for JWT: ${authInfo.jwt}`);
} catch (err) {
    console.log(`Failed to exchange access key: ${err}`);
}
# Args:
#   access_key (str): The access key
access_key = "xxxxxx"
// login_options: Optional advanced controls over login parameters, e.g. custom claims:
login_options = {"custom_claims": {"key":"value"}}

try:
  resp = descope_client.exchange_access_key(access_key=access_key, login_options=login_options)
  print("Successfully exchanged access key. Below is the session info.")
  print(json.dumps(resp, indent=4))
except AuthException as error:
  print ("Unable to exchange access keys.")
  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()
//   accessKey (str): The access key
accessKey := "xxxxxx"
//   loginOptions (AccessKeyLoginOptions): Optional advanced controls over login parameters, e.g. custom claims:
	loginOptions := &descope.AccessKeyLoginOptions{
		CustomClaims: map[string]any{"k1": "v1"}
	}

authenticated, token, err := descopeClient.Auth.ExchangeAccessKey(ctx, accessKey, loginOptions)
if (err != nil){
  fmt.Println("Unable to exchange access keys: ", err)
} else {
  fmt.Println("Successfully exchanged access key. Below is the session info.")
  fmt.Println("Authenticated: ", authenticated)
  fmt.Println("Token: ", token)
}
// Args:
//   accessKey (str): The access key
accessKey := "xxxxxx"
// accessKeyLoginOptions:  Optional advanced controls over login parameters, e.g. custom claims:
Map<String, Object> loginOptions = {"customClaims":{"key":"value"}}

// Roles should be set directly if no tenants exist, otherwise set
// on a per-tenant basis.
AuthenticationServices authService = AuthenticationServiceBuilder.buildServices(client);
aks = authService.getAuthService();

try {
    AuthenticationInfo info = aks.exchangeAccessKey(accessKey, loginOptions);
} catch (DescopeException de) {
    // Handle the error
}