Guides and Tutorials/Inbound Apps

Integrating Inbound Apps

After configuring an Inbound App in Descope, you can integrate it into third-party applications, OAuth clients (like NextAuth), and your backend services.

This guide covers:

  • Authenticating users with Descope as an IdP.
  • Using NextAuth as an OAuth client for a partner application.
  • Backend authentication via /token endpoint for API access.
  • PKCE vs. client secret usage for token exchange.

For a real life example, check out the Descope 3rd-Party Sample App for a working implementation.

1. Authenticating Users with an Inbound App

Once an inbound app is configured, users can sign in using Descope as the identity provider (IdP).

Initiating Login with Descope

The third-party application redirects users to Descope's /authorize endpoint, prompting them to authenticate and approve the requested permissions.

https://api.descope.com/oauth2/v1/apps/authorize? 
  client_id=<CLIENT_ID>
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=openid email profile
  &state=<RANDOM_STATE_STRING>

This is the general process that will occur when a user initates login:

  • The user logs in via Descope.
  • The user approves the requested scopes on the consent screen.
  • Descope redirects the user back to the callback URL with an authorization code.

Without user consent, an OAuth token will not be issued.

Example: NextAuth Application Acting As OAuth Client

In case you don't want to manually develop your application as an OAuth client, here is an example of how you can configure Auth.js to act as an OAuth client for Descope:

import NextAuth from "next-auth";
import { OIDCConfig } from "next-auth/providers";
 
export const baseUrl = "https://api.descope.com";
export const clientId = process.env.CLIENT_ID ?? "";
export const clientSecret = process.env.CLIENT_SECRET ?? "";
 
const DescopeOAuthApps = (): OIDCConfig => ({
    id: "customapp",
    name: "Custom App",
    type: "oidc",
    authorization: { params: { scope: "openid email profile", prompt: "consent" } },
    client: { token_endpoint_auth_method: "client_secret_post" },
    checks: ["pkce", "state"],
});
 
export const { signIn, signOut, auth } = NextAuth({
    providers: [
        {
            ...DescopeOAuthApps(),
            clientId,
            clientSecret,
        },
    ],
});

2. Handling the OAuth Callback

After successful authentication and consent, Descope redirects the user to the redirect_uri, appending an authorization code.

Example response to the callback URL:

https://yourapp.com/callback?code=<AUTHORIZATION_CODE>&state=<RANDOM_STATE_STRING>

Once the application receives the authorization code, it must be exchanged for an access token by making a POST request to the Descope /token endpoint.

3. Exchanging Authorization Code for an Access Token

There are two ways to exchange the authorization code for an access token:

Using a Client Secret (Confidential Clients - Backend Apps)

Recommended for server-side apps that can securely store secrets.

curl -X POST "https://api.descope.com/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"

Using PKCE (Public Clients - SPAs, Mobile Apps)

Recommended for apps that cannot securely store secrets.

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

4. Using the Token with an API

If successful, Descope responds with an access token:

{
  "access_token": "eyJhbGciOiJIUz...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "abc123",
  "id_token": "eyJhbGciOiJIUz..."
}

With this response, three types of tokens will be returned:

  • access_token → Used to authenticate API requests.
  • id_token → Contains user identity claims (for OpenID Connect).
  • refresh_token → Used to obtain a new access token when it expires.

Once authenticated, the access token is used to authorize API calls.

curl -X GET "https://api.yourservice.com/user/profile" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

If the user revokes consent, the token will no longer be valid.

5. Using Client Credentials Flow in Your Backend (Optional)

In Inbound Apps, the client credentials grant allows a backend service to retrieve an already created user token rather than creating a new one. This is useful when a service needs to act on behalf of a user without requiring them to log in again.

How It Works in Inbound Apps

  1. A user logs in via OAuth and grants consent to an Inbound App.
  2. The app exchanges the authorization code for an access token.
  3. Later, a backend service can use client credentials to fetch that token instead of requesting the user to log in again.

Example: Fetching an Already Created Token Using Client Credentials

import { auth, baseUrl, clientId, clientSecret } from "@/auth";
import { NextResponse } from "next/server";
 
export const GET = auth(async function GET(req) {
    if (req.auth) {
        const body = "grant_type=client_credentials" +
            "&client_id=" + clientId +
            "&client_secret=" + clientSecret +
            "&scope=openid email profile";
        const res = await fetch(`${baseUrl}/oauth2/v1/apps/token`, {
            method: "POST",
            headers: { "content-type": "application/x-www-form-urlencoded" },
            body,
        });
        return NextResponse.json(await res.json(), { status: res.status });
    }
    return NextResponse.json({ message: "Not authenticated" }, { status: 401 });
});

Summary: Key Takeaways

When using Inbound Apps with Descope, users authenticate through Descope's OAuth flow, and they must explicitly grant consent before any token is issued. Partner applications, such as those using Auth.js/NextAuth, can function as OAuth clients to facilitate user authentication.

Backend services can then retrieve user tokens via the /token endpoint, allowing them to act on behalf of users without requiring them to log in again. These access tokens are essential for authorizing API requests and ensuring secure interactions between third-party applications and protected resources.

For a complete working implementation, refer to the Descope 3rd-Party Sample App.

Was this helpful?