Descope OIDC Endpoints Quickstart
Using Descope as an OpenID Connect (OIDC) provider is a common integration path—especially when your framework does not have a native Descope SDK (for example Expo, .NET Blazor, or custom OAuth clients).
This guide explains how to use Descope's OIDC endpoints to authenticate users, obtain tokens, manage sessions, and perform secure logout and token revocation.
Core OIDC Endpoints
| Purpose | Endpoint |
|---|---|
| Authorization - Start the OIDC login flow | https://api.descope.com/oauth2/v1/authorize |
| Token - Exchange authorization codes for tokens | https://api.descope.com/oauth2/v1/token |
| UserInfo - Retrieve user claims and profile information | https://api.descope.com/oauth2/v1/userinfo |
| JWKs URI - Retrieve public keys for verifying Descope-issued JWTs | https://api.descope.com/__ProjectID__/.well-known/jwks.json |
| End Session - Log the user out of their Descope session | https://api.descope.com/oauth2/v1/logout |
| Revocation - Revoke a token (access or refresh) | https://api.descope.com/oauth2/v1/revoke |
Diagram of OIDC Authorization Code Flow
Confidential server-side apps use a client secret at the token endpoint.
- Redirect the user to
/authorizewithclient_idset to your Project ID. - Exchange the authorization code at
/tokenusing a Descope Access Key asclient_secret. - Descope returns an
id_token,access_token, andrefresh_token.
Supported Grant Types
Descope supports the following OAuth 2.0 grant types:
- Authorization Code Flow with PKCE - Recommended for public clients (SPAs, native mobile)
- Authorization Code Flow (without PKCE) - For confidential clients that can safely store a client secret
- Client Credentials Flow - For machine-to-machine (M2M) integrations
- Device Authorization Flow - For devices without a browser or keyboard (e.g., smart TVs)
The sections below walk through each flow in detail.
Authorization Code Flow (with PKCE)
Use this flow when building native mobile apps, SPAs, or any public client that cannot safely store a client secret. PKCE (Proof Key for Code Exchange) protects against code interception attacks.
1. Generate a Code Verifier and Code Challenge
function generateCodeVerifier() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
return Array.from(crypto.getRandomValues(new Uint8Array(128)))
.map(x => chars[x % chars.length])
.join('');
}
async function generateCodeChallenge(verifier) {
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
return btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}2. Redirect the User to the Authorization Endpoint
const authUrl = `https://api.descope.com/oauth2/v1/authorize
?response_type=code
&client_id=__ProjectID__
&redirect_uri=YOUR_REDIRECT_URI
&scope=openid profile email
&code_challenge=${codeChallenge}
&code_challenge_method=S256
&state=YOUR_STATE`;
window.location.href = authUrl;After successful authentication, Descope redirects to your redirect_uri with an authorization code.
3. Exchange the Authorization Code for Tokens
const response = await fetch('https://api.descope.com/oauth2/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: 'AUTHORIZATION_CODE',
redirect_uri: 'YOUR_REDIRECT_URI',
client_id: '__ProjectID__',
code_verifier: codeVerifier,
}),
});
const tokens = await response.json();4. Retrieve User Information
const userResponse = await fetch('https://api.descope.com/oauth2/v1/userinfo', {
headers: { 'Authorization': `Bearer ${tokens.access_token}` },
});
const user = await userResponse.json();The /userinfo response includes user claims (for example email, name, sub) along with any custom claims and role assignments you have configured in Descope.
Tip
Store the refresh_token securely in your application so you can obtain new access tokens without forcing the user to log in again.
Authorization Code Flow (without PKCE)
Confidential clients (such as server-side web apps and trusted backend services) can perform the Authorization Code flow without PKCE by authenticating with their client secret.
Note
We still recommend enabling PKCE wherever possible. However, some enterprise OIDC clients expect the classic authorization code flow with a client secret and no PKCE. This section shows how to support that scenario.
1. Redirect the User to the Authorization Endpoint
https://api.descope.com/oauth2/v1/authorize?
response_type=code&
client_id=__ProjectID__&
redirect_uri=https://your-app.com/callback&
scope=openid%20profile%20email&
state=YOUR_STATE2. Exchange the Authorization Code for Tokens
Send the authorization code to the token endpoint. Include your client secret in either the Authorization header or the POST body.
curl -X POST https://api.descope.com/oauth2/v1/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-u '__ProjectID__:YOUR_CLIENT_SECRET' \
-d 'grant_type=authorization_code' \
-d 'code=AUTHORIZATION_CODE' \
-d 'redirect_uri=https://your-app.com/callback'The response includes the access_token, id_token, and optionally a refresh_token. Use the access_token to call /userinfo, and verify the id_token signature before establishing a session.
Security reminder
Only use this approach when you can safely store and protect the client secret. For SPAs or mobile apps, always use the PKCE variant.
Client Credentials Flow (Machine-to-Machine)
Use the Client Credentials flow to obtain tokens for backend services without user interaction.
- Generate an Access Key in the Descope Console.
- Combine the
<Client ID>and<Access Key>as<ClientID>:<AccessKey>. - Base64-encode the string and send it in the
Authorizationheader.
curl -X POST https://api.descope.com/oauth2/v1/token \
-H 'Authorization: Basic <BASE64(<ClientID>:<AccessKey>)>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials&scope=openid%20profile%20email'Custom claims for machine-to-machine tokens can be configured using Access Key JWT Templates under Project Settings.
Managing Sessions and Tokens
Logout vs Token Revocation
| Action | Affects | When to Use |
|---|---|---|
/logout | User session (browser) | Sign the user out from Descope and your app |
/revoke | Specific token | Invalidate tokens programmatically |
End Session (/logout)
window.location.href = `https://api.descope.com/oauth2/v1/logout
?id_token_hint=${id_token}
&post_logout_redirect_uri=${YOUR_REDIRECT_URL}`;Revocation (/revoke)
await fetch('https://api.descope.com/oauth2/v1/revoke', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
token: 'TOKEN_TO_REVOKE',
client_id: '__ProjectID__',
}),
});Silent Authentication
Silent authentication lets you refresh sessions without prompting the user, as long as they still have an active Descope session.
https://api.descope.com/oauth2/v1/authorize?...&prompt=noneCommon scenarios include SPAs refreshing tokens or background session renewal.
Passing Dynamic Values into Flows
When redirecting to /authorize, you can include additional query parameters and consume them inside your Descope flow with a Scriptlet:
const query = new URL(startUrl).searchParams;
return {
age: query.get('age'),
plan: query.get('plan'),
};These values are available for conditions, screens, or custom logic inside the flow.
JWT Verification
To verify any JWT (such as id_token or access_token), retrieve Descope's public keys from the JWKs endpoint:
https://api.descope.com/__ProjectID__/.well-known/jwks.jsonUse these keys to validate signatures and confirm token authenticity in your backend.
Testing the Endpoints
You can experiment with Descope's OIDC endpoints using OAuth Tools or your preferred API client. This is useful for validating flows, refreshing tokens, and checking error responses.
With these endpoints and grant types, you can integrate Descope into any framework or custom OIDC client while maintaining the same level of security, observability, and user experience provided by Descope flows.