Authorization Server Endpoints

When you configure an Inbound App, Descope acts as the OAuth 2.0 / OpenID Connect authorization server for that client.

Third-party applications, AI agents, and MCP clients call a shared set of HTTP endpoints under /oauth2/v1/apps/ to start user login, exchange codes for tokens, refresh sessions, and read token claims.

Each Inbound App also exposes a Discovery URL and Issuer in the Descope Console (see Creating Inbound Apps). Standard OAuth libraries use those values to resolve the same endpoints listed below automatically.

If you use a custom domain, replace https://api.descope.com with your configured API hostname in every URL on this page.

Base URL

EnvironmentBase URL
Descope (default)https://api.descope.com
Custom domainhttps://<your-api-host>

All authorization server routes are rooted at:

{baseUrl}/oauth2/v1/apps/...

Endpoint Reference

EndpointMethodPurposeAPI reference
/oauth2/v1/apps/authorizeGETStart the authorization code flow (browser redirect)Get authorization
/oauth2/v1/apps/authorizePOSTStart authorization with a JSON body (non-browser clients)Post authorization
/oauth2/v1/apps/parPOSTPush authorization parameters from your server before redirecting (PAR)
/oauth2/v1/apps/tokenPOSTIssue and refresh tokens; client credentials; JWT bearer; token exchangeToken endpoint
/oauth2/v1/apps/revokePOSTRevoke access or refresh tokensRevoke token
/oauth2/v1/apps/userinfoGETReturn claims for the bearer access tokenGet UserInfo
/oauth2/v1/apps/userinfoPOSTReturn claims (POST variant for clients that require it)Post UserInfo

Discovery and JWKs

Inbound Apps are OpenID Connect-compatible. For each app, the Console provides:

  • Discovery URL: OpenID Provider metadata, including authorization_endpoint, token_endpoint, jwks_uri, supported scopes, and grant types
  • Issuer: The value to check iss against on ID tokens and access tokens

The project-level issuer uses the same base URL as the endpoints, so it changes with your region or custom domain:

__BaseURL__/v1/apps/{projectId}

Imported client IDs

If you've imported apps with custom client IDs, the issuer is __BaseURL__/v1/apps/customized/{projectId} instead. See Issuer After Importing Client IDs for more details.

Project-level well-known documents are also available when you need metadata scoped to the whole project:

__BaseURL__/v1/apps/{projectId}/.well-known/openid-configuration
__BaseURL__/v1/apps/{projectId}/.well-known/oauth-authorization-server

MCP Server Resources

MCP servers are defined as MCP Server Resources: the same OAuth resource model as API Resources. Descope uses one authorization server for all Inbound Apps; to authenticate against a specific Resource (including an MCP server), include the resource parameter (RFC 8707) on the authorize or token request. Use the Resource identifier from the console, typically your MCP server URL, which also appears in the token aud claim.

MCP clients discover that Resource through OAuth Protected Resource Metadata on your server; see MCP discovery URL for the client-side discovery flow.

Token Endpoint

The token endpoint is the central exchange point for Inbound Apps. Send application/x-www-form-urlencoded (or JSON, per the API schema) with a grant_type and the parameters required for that grant.

Grant typeTypical useGuide
authorization_codeUser-delegated access after consentAuthorization code
refresh_tokenRenew an access token without re-consentRefresh token
client_credentialsMachine-to-machine Inbound App tokensClient credentials
urn:ietf:params:oauth:grant-type:jwt-bearerExchange a trusted external JWT for Descope tokensJWT bearer
urn:ietf:params:oauth:grant-type:token-exchangeTrade a subject token the caller already holds for one scoped to a specific downstream Resource, governed by Policies (RFC 8693)Token exchange

Common optional parameters on the token endpoint include:

  • scope: Requested OAuth scopes (must be allowed on the Inbound App and, for user flows, approved in consent).
  • resource: RFC 8707 resource indicator (single Resource URI per request). Required when issuing tokens for a specific API or MCP Server Resource; pass the same value on authorize and token requests.
  • audience: Target audience for issued tokens or exchanges.

Example (authorization code exchange):

curl -X POST "__BaseURL__/oauth2/v1/apps/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=<CLIENT_ID>" \
  -d "client_secret=<CLIENT_SECRET>" \
  -d "code=<AUTHORIZATION_CODE>" \
  -d "redirect_uri=https://yourapp.com/callback"

See the Token endpoint API reference for the full request and response schema.

Client authentication

Confidential clients have to prove who they are on every token request. Descope supports two methods.

Client secret. The client sends client_id and client_secret, as in the example above. This is the default and needs no extra setup.

Private key JWT. Instead of sending a shared secret, the client signs a short-lived JWT with its own private key and sends it as a client_assertion (RFC 7523). Descope validates the signature against the public keys the client publishes, so no shared secret is ever transmitted or stored on the Descope side.

curl -X POST "__BaseURL__/oauth2/v1/apps/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=<CLIENT_ID>" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=<SIGNED_JWT>" \
  -d "code=<AUTHORIZATION_CODE>" \
  -d "redirect_uri=https://yourapp.com/callback"

Prefer private key JWT when you would rather not distribute a long-lived shared secret, or when a compliance requirement rules one out. It is also the stronger option for Cross-App Access, where the client handles assertions that can be redeemed for access tokens elsewhere.

Private key JWT is available on request

Private key JWT client authentication for Inbound Apps and Agentic Clients is behind a feature flag. Contact Descope to have it enabled for your project.

Public (non-confidential) clients do not authenticate this way. They send client_id with no secret and rely on PKCE to protect the exchange.

Note

Descope authenticating to an external provider with a signed JWT is a separate feature. See Private Key JWT for custom OAuth providers and OIDC SSO for that direction.

Token exchange

Token exchange (RFC 8693) trades a subject token the caller already holds for a new token scoped to a specific downstream Resource. Credentials stored in Connections come from the Connection token endpoints instead. Token exchange is always available at this endpoint for every project; there is no per-app toggle. Policies with grant type Delegated access (token exchange) decide what each client may exchange.

Pass the token being exchanged as subject_token, its type as subject_token_type, and the target Resource as resource (RFC 8707):

curl -X POST "__BaseURL__/oauth2/v1/apps/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "client_id=<CLIENT_ID>" \
  -d "client_secret=<CLIENT_SECRET>" \
  -d "subject_token=<SUBJECT_TOKEN>" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "resource=https://your-api.example.com" \
  -d "scope=orders.read"

See Token exchange for policy setup, Resource targets, how Connection credentials differ, diagrams, and troubleshooting.

Audience

The aud (audience) claim names who the access token is intended for. Your API or gateway should reject tokens whose aud does not match the service receiving the request.

See Enforce scopes and Resource claims, for more details.

Resource Audience

When a client targets a Resource (API or MCP server), Descope issues a token whose aud includes that Resource's identifier (for MCP Server Resources, typically the MCP server URL).

Clients select the Resource with the resource parameter (RFC 8707) on authorize and token requests. Pass the same Resource URI on both when the flow uses both endpoints.

{
  "aud": ["https://api.example.com"],
  "scope": "orders.read",
  "iss": "https://api.descope.com/<projectId>"
}

Resource Access and Policies

Which clients may receive tokens for which Resources (and scopes) is controlled by Policies, not by listing every Resource only on the Inbound App.

resource vs audience on the token endpoint

ParameterRole
resourceRFC 8707 resource indicator: the Resource URI to issue (or exchange) a token for. Preferred for API and MCP Server Resources.
audienceTarget audience for issued tokens or exchanges when the client or grant uses an audience value directly (including some exchange flows).

Prefer resource when calling a Descope-defined Resource. Use audience when your integration specifically needs that parameter; see the token endpoint schema.

Inbound App audience settings

Each Inbound App also has Console settings under Audience:

  • Default Audience — Values always included on tokens from that app (for example client ID and/or project ID), independent of which Resource was requested.
  • Audience Whitelist — Legacy allowlist of audiences the app may request; prefer Policies to control Resource access.

A token can therefore include both the Resource identifier and the inbound app's default audience values in aud.

Authorize Endpoint

The authorize endpoint starts interactive login and consent. Browser-based apps redirect users with query parameters such as client_id, redirect_uri, response_type=code, scope, state, PKCE fields (code_challenge, code_challenge_method), and optionally resource to target a specific Resource (required when authenticating to an MCP Server Resource).

Example redirect:

__BaseURL__/oauth2/v1/apps/authorize?\
client_id=<CLIENT_ID>&\
redirect_uri=https://yourapp.com/callback&\
response_type=code&\
scope=openid%20email%20profile&\
state=<RANDOM_STATE>

When targeting an MCP Server Resource, add resource (URL-encoded Resource URI):

__BaseURL__/oauth2/v1/apps/authorize?\
client_id=<CLIENT_ID>&\
redirect_uri=https://yourapp.com/callback&\
response_type=code&\
scope=mcp:tools.read&\
resource=https%3A%2F%2Fyour-mcp.example.com%2Fmcp&\
state=<RANDOM_STATE>

After the user completes the consent flow, Descope redirects back to redirect_uri with an authorization code. Exchange that code at the token endpoint, including the same resource value if you specified one at authorize time.

To keep these parameters out of the browser, push them to /oauth2/v1/apps/par from your server first, then redirect with only client_id and the returned request_uri. See Pushed Authorization Requests for the request format.

Revoke Endpoint

The revoke endpoint invalidates an access or refresh token (RFC 7009). Call it when a user disconnects an integration, you rotate credentials, or you need to end a session without waiting for natural expiry.

Send application/x-www-form-urlencoded (or JSON, per the API schema) with:

  • token: The access or refresh token to revoke.
  • token_type_hint: Optional hint: access_token or refresh_token.
  • client_id / client_secret: Inbound App credentials (required for confidential clients).

Example (revoke a refresh token):

curl -X POST "__BaseURL__/oauth2/v1/apps/revoke" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=<REFRESH_TOKEN>" \
  -d "token_type_hint=refresh_token" \
  -d "client_id=<CLIENT_ID>" \
  -d "client_secret=<CLIENT_SECRET>"

Revoking a refresh token prevents future renewals at the token endpoint. Revoking an access token invalidates it for subsequent API calls.

See the Revoke token API reference for the full request and response schema.

User Info Endpoint

The UserInfo endpoint returns OpenID Connect claims for the subject of a valid access token. Use it when your app needs profile data (sub, email, name) or custom claims mapped in the Inbound App beyond what is embedded in the JWT. For when to introspect vs validate locally, see Token introspection.

Send the access token as a Bearer credential on the GET endpoint: the standard OIDC variant. A POST variant is available for clients that require it.

Example (GET):

curl "__BaseURL__/oauth2/v1/apps/userinfo" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Example response (shape varies by scopes and Inbound App configuration):

{
  "sub": "U2lz...",
  "email": "user@example.com",
  "email_verified": true,
  "name": "Jane Doe"
}

The access token must include the openid scope, and any scopes required for specific claims (such as email or profile). Claims returned depend on what the user approved during consent and how the Inbound App maps user attributes.

See the Get UserInfo and Post UserInfo API references for the full request and response schema.

Configure clients and scopes

Was this helpful?

On this page