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
| Environment | Base URL |
|---|---|
| Descope (default) | https://api.descope.com |
| Custom domain | https://<your-api-host> |
All authorization server routes are rooted at:
{baseUrl}/oauth2/v1/apps/...Endpoint Reference
| Endpoint | Method | Purpose | API reference |
|---|---|---|---|
/oauth2/v1/apps/authorize | GET | Start the authorization code flow (browser redirect) | Get authorization |
/oauth2/v1/apps/authorize | POST | Start authorization with a JSON body (non-browser clients) | Post authorization |
/oauth2/v1/apps/par | POST | Push authorization parameters from your server before redirecting (PAR) | |
/oauth2/v1/apps/token | POST | Issue and refresh tokens; client credentials; JWT bearer; token exchange | Token endpoint |
/oauth2/v1/apps/revoke | POST | Revoke access or refresh tokens | Revoke token |
/oauth2/v1/apps/userinfo | GET | Return claims for the bearer access token | Get UserInfo |
/oauth2/v1/apps/userinfo | POST | Return 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
issagainst 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-serverMCP 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 type | Typical use | Guide |
|---|---|---|
authorization_code | User-delegated access after consent | Authorization code |
refresh_token | Renew an access token without re-consent | Refresh token |
client_credentials | Machine-to-machine Inbound App tokens | Client credentials |
urn:ietf:params:oauth:grant-type:jwt-bearer | Exchange a trusted external JWT for Descope tokens | JWT bearer |
urn:ietf:params:oauth:grant-type:token-exchange | Trade 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
| Parameter | Role |
|---|---|
resource | RFC 8707 resource indicator: the Resource URI to issue (or exchange) a token for. Preferred for API and MCP Server Resources. |
audience | Target 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_tokenorrefresh_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
- Create and manage Inbound Apps in the Console or via Management API.
- Map API permissions to OAuth scopes on Resources.
- Validate Descope JWTs and enforce Resource scopes in Session validation. See also Developing APIs with OAuth.
Related
- Using Inbound Apps: step-by-step flows for each grant type
- Inbound Apps API reference: interactive OpenAPI docs for every authorization server route
- Resources: define scopes and audiences your tokens target
- Creating Inbound Apps → Audience: Default Audience and Audience Whitelist
- Agentic Identity Hub: agents, MCP servers, Connections, and STS token exchange
Building Consent Flows
Build Descope flows for Inbound App user consent and CIBA approval, including screen components and flow actions.
Pushed Authorization Requests
Send OAuth authorization parameters to Descope over a back-channel call with PAR (RFC 9126), so the browser redirect carries only a short request_uri.