Storing Connection Tokens

After configuring a Connection in Descope, you can start storing tokens for your users or tenants to access third-party APIs.

This section covers how to store tokens for OAuth-based connections or API key-based connections, for both user-scoped and tenant-scoped tokens.

For information on how to fetch connection tokens after they've been stored, see the Fetching Connection Tokens documentation.

Connections can store multiple tokens for the same user or tenant, each with different scopes.

Once you've created a Connection, you can start storing tokens for your users or tenants to access third-party services through Descope Connections.

Storing User Tokens

For user-scoped tokens, you can store tokens using either OAuth-based connections or API key-based connections.

OAuth Provider Connections

For OAuth-based connections (e.g., Google, Microsoft, Salesforce), you can store user tokens using either Adaptive Connect or Descope Flows.

OAuth Connections: Adaptive Connect

Adaptive Connect allows your MCP server to dynamically request connection permissions when a tool requires external OAuth tokens that the user hasn't yet granted. Instead of failing immediately, your MCP server can return a connection URL to the MCP client, prompting the user to grant the necessary permissions.

Adaptive Connect is specifically designed for MCP servers where tools need to request connections on-demand. For traditional web applications, use Descope Flows instead.

When an MCP tool requires a connection token that doesn't exist or lacks the required scopes, Adaptive Connect enables your server to:

  1. Detect the missing token when attempting to fetch a connection token
  2. Request a connection URL from Descope
  3. Return the connection URL to the MCP client in a structured response
  4. Allow the user to grant permissions via the OAuth flow
  5. Retry the tool execution after the connection is established

The simplest implementation pattern is to catch token fetch failures and return a connection URL in the error response. This is shown in the Complete Example below.

Connection Endpoint

The Adaptive Connect endpoint allows you to request a connection URL for a specific connection.

// Request a connection URL
const response = await fetch('https://api.descope.com/v1/mgmt/outbound/app/connect', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${PROJECT_ID}:${MCP_ACCESS_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    appId: 'google-contacts',
    options: {
      redirectUrl: 'https://your-app.com/connection-complete'
    }
  })
});
 
const { url } = await response.json();
// Return this URL to the MCP client

Request Parameters

  • appId (required): The ID of the connection you want to connect to.
  • options (optional): Additional options for the connection request.
    • redirectUrl (required): The URL where the user will be redirected after successfully completing the OAuth flow. This must be a valid URL that your application can handle.

Response

The endpoint returns a JSON object with a url field containing the OAuth authorization URL that the user should be redirected to, such as:

{
  "url": "https://api.descope.com/v1/outbound/oauth/connect?appId=google-contacts&..."
}

Complete Example

Here's a complete example showing how to implement Adaptive Connect in an MCP tool. This example demonstrates the tool elicitation pattern, where the MCP server detects a missing connection token, requests a connection URL, and returns it in a structured response that MCP clients can handle gracefully:

from descope import DescopeClient
import requests
 
descope_client = DescopeClient(project_id="YOUR_PROJECT_ID")
 
def handle_mcp_tool(user_id, tool_name, tool_params):
    """
    Handle an MCP tool request with Adaptive Connect fallback.
    """
    # Step 1: Attempt to fetch the connection token
    try:
        token_response = descope_client.mgmt.outbound_application.fetch_token(
            app_id="google-contacts",
            user_id=user_id,
            options={"withRefreshToken": False}
        )
        access_token = token_response["token"]["token"]
        
    except Exception as token_error:
        # Step 2: Token fetch failed, request connection URL
        try:
            connect_response = requests.post(
                "https://api.descope.com/v1/mgmt/outbound/app/connect",
                headers={
                    "Authorization": f"Bearer {PROJECT_ID}:{MCP_ACCESS_TOKEN}",
                    "Content-Type": "application/json"
                },
                json={
                    "appId": "google-contacts",
                    "options": {
                        "redirectUrl": "https://your-app.com/connection-complete"
                    }
                },
                timeout=10
            )
            
            if connect_response.status_code == 200:
                connection_url = connect_response.json().get("url")
                # Return structured response for tool elicitation
                return {
                    "error": "connection_required",
                    "requiresConnection": True,
                    "connectionUrl": connection_url,
                    "message": (
                        f"Failed to run {tool_name}, additional permissions required. "
                        f"Please grant additional permissions using the following URL and try again: {connection_url}"
                    ),
                    "appId": "google-contacts"
                }
            else:
                return {
                    "error": "connection_initiation_failed",
                    "message": f"Failed to initiate connection: {connect_response.text}"
                }
                
        except Exception as connect_error:
            return {
                "error": "connection_request_failed",
                "message": f"Failed to request connection URL: {str(connect_error)}"
            }
    
    # Step 3: Token exists, execute the tool
    try:
        # Use access_token to call the third-party API
        # ... your tool logic here ...
        return {
            "success": True,
            "result": "Tool executed successfully"
        }
    except Exception as tool_error:
        return {
            "error": "tool_execution_failed",
            "message": str(tool_error)
        }

OAuth Connections: Using Descope Flows

For traditional web applications or when you want to collect OAuth connections during the initial login flow, use Descope Flows with OAuth-specific actions.

If you're using Descope Flows, you must first sign in using the flow before you can connect to a connection. The authenticated session will be used to establish a connection and associate the connection tokens with the respective user.

Flow Components for OAuth Connections

There are two main components for OAuth connections:

  1. Outbound App Button - A button component that initiates the OAuth connection
  2. Outbound App Connect Action - The action that processes the OAuth connection request
    • Outbound App / Connect (user-level) - For user-scoped OAuth tokens

An example of configuring the outbound app button within Descope flows

Implementation Options

You can implement OAuth connections in flows in two ways:

Option 1: Using the Outbound App Button

Place an Outbound App button on your flow screen that users can click to initiate the OAuth connection. The button can be customized with text and styling options, and it automatically displays the connection's logo.

Option 2: Using the Connect Action Directly

If you want to use your own UI elements, you can trigger the Outbound App Connect action directly from your application. You can set a default connection in the action's configuration to bypass the need for Descope's UI components.

In addition to this, you can also configure custom scopes that will override the default scopes configured in your Connection Settings.

Outbound app connect action configuration

Descope manages the OAuth consent flow, token storage, and automatic token refresh.

API Key Connections

For services that use static API keys (e.g., custom APIs, services without OAuth), you can collect and store API keys for users using Descope Flows.

Note

Currently, you cannot store API keys with Connections directly via a REST API. You must use Descope Flows.

API Key Connections: Using Descope Flows

API key connections use different flow actions than OAuth connections:

Flow Components for API Key Connections

Use this action in your Descope Flow:

  1. Outbound App / Connect API Key (user-level) - Collects and stores an API key for a specific user
Implementation

The process is similar to OAuth connections:

  1. Add the appropriate API key connect action to your flow
  2. The action renders a secure input screen for users to submit their API key
  3. Once submitted, the API key is stored securely in the Connection vault
  4. The API key becomes available to your MCP server or backend for tool execution

For machine-to-machine agents or backend automation, API keys can also be inserted programmatically via SDK or API without user interaction, depending on your integration design.

Storing Tenant Tokens

For tenant-scoped tokens, you can store tokens using either OAuth-based connections or API key-based connections.

OAuth Provider Connections

For OAuth-based connections (e.g., Google, Microsoft, Salesforce), you can store tenant tokens only using Descope Flows.

OAuth Connections: Using Descope Flows

For traditional web applications or when you want to collect OAuth connections during the initial login flow, use Descope Flows with OAuth-specific actions.

If you're using Descope Flows, you must first sign in using the flow before you can connect to a connection. The authenticated session will be used to establish a connection and associate the connection tokens with the respective tenant.

Flow Components for OAuth Connections

There are two main components for OAuth connections:

  1. Outbound App Button - A button component that initiates the OAuth connection
  2. Outbound App Connect Action - The action that processes the OAuth connection request
    • Outbound App / Tenant Connect - For tenant-scoped OAuth tokens

An example of configuring the outbound app button within Descope flows

Implementation Options

You can implement OAuth connections in flows in two ways:

Option 1: Using the Outbound App Button

Place an Outbound App button on your flow screen that users can click to initiate the OAuth connection. The button can be customized with text and styling options, and it automatically displays the connection's logo.

Option 2: Using the Connect Action Directly

If you want to use your own UI elements, you can trigger the Outbound App / Tenant Connect action directly from your application. You can set a default connection in the action's configuration to bypass the need for Descope's UI components.

In addition to this, you can also configure custom scopes that will override the default scopes configured in your Connection Settings.

Outbound app connect action configuration

Descope manages the OAuth consent flow, token storage, and automatic token refresh.

API Key Connections

For services that use static API keys (e.g., custom APIs, services without OAuth), you can collect and store API keys for tenants using Descope Flows.

Note

Currently, you cannot store API keys with Connections directly via a REST API. You must use Descope Flows.

API Key Connections: Using Descope Flows

API key connections use different flow actions than OAuth connections:

Flow Components for API Key Connections

Use this action in your Descope Flow:

  1. Outbound App / Connect Tenant API Key - Collects and stores an API key for a tenant
Implementation

The process is similar to OAuth connections:

  1. Add the appropriate API key connect action to your flow
  2. The action renders a secure input screen for users to submit their API key
  3. Once submitted, the API key is stored securely in the Connection vault
  4. The API key becomes available to your MCP server or backend for tool execution

For machine-to-machine agents or backend automation, API keys can also be inserted programmatically via SDK or API without user interaction, depending on your integration design.

Deleting Connection Tokens

Once you've created your tokens, if you wish to delete them, you can remove these connection tokens in two ways:

1. Using the Descope Console

Navigate to the Token Management tab for your connection in the Descope Console. Find the user or tenant whose token you want to delete, and use the delete option in the dashboard (see image above).

2. Using our SDKs / APIs

You can also delete tokens programmatically using our SDKs / APIs:

Delete a Specific Token by ID

// Delete a specific token by its ID
// Token deletion cannot be undone. Use carefully.
await descopeClient.management.outboundApplication.deleteTokenById('token-id-123');

Delete Tokens by App ID and/or User ID

You can delete tokens by providing an app ID, user ID, or both. At least one of appId or userId must be provided.

// Delete all tokens for a specific app and user
// Token deletion cannot be undone. Use carefully.
await descopeClient.management.outboundApplication.deleteUserTokens('my-app-id', 'user-id');
 
// Delete all tokens for a specific app
await descopeClient.management.outboundApplication.deleteUserTokens('my-app-id');
 
// Delete all tokens for a specific user
await descopeClient.management.outboundApplication.deleteUserTokens(undefined, 'user-id');
Was this helpful?