Skip to main content

OAuth 2.1

How third-party applications authenticate with the ResQueServe using its built-in OAuth 2.1 authorization server

All API access requires a valid Bearer token obtained through one of the two authentication methods described below. For a full, interactive reference of every API endpoint and its request/response schema, see the OpenAPI documentation published at /api-docs.


Quick Reference

What

Value

Spec

OAuth 2.1 (draft) + RFC 8414 + RFC 9728 + RFC 7591

Token type

JWT Bearer

Token lifetime

15 minutes (900 s)

Supported grant types

authorization_code (+ mandatory PKCE), client_credentials

Supported response types

code

PKCE methods

S256 only

Token auth method

client_secret_post

Available scope

mcp:user


Authentication Methods

ResQueServe supports two distinct ways to obtain a Bearer token:

Method 1: Client Credentials

Method 2: Authorization Code + PKCE

Best for

Automated scripts, server-side integrations, direct API access

Third-party applications and integrations that act on behalf of a logged-in user via a browser-based consent flow (includes OAuth 2.1 clients such as MCP-compatible tools)

User login required

No

Yes - user authenticates interactively

How to get credentials

Account settings page in the Dashboard

Dynamic client registration via POST /oauth/register

Implementation effort

A single HTTP request for each token

Requires redirect handling, PKCE, and a callback endpoint

Method 1: Client Credentials (Recommended for direct API access)

This is the straightforward method for integrations that run on a server or in automated pipelines. You register an API client in your ResQueServe account settings and then exchange the client credentials for a short-lived Bearer token with a single HTTP request.

Log in and open Account from the top-right menu, and scroll to the API Clients / Developer section. Enter a description and click Create. Copy the client_id and client_secret that are displayed. The secret is shown only once and cannot be retrieved again. A user account may have at most 10 API clients.

Post grant_type=client_credentials together with your client_id (the UUID from the account settings page) and client_secret (the value shown once at creation time) to the token endpoint:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=<your-client-id>
&client_secret=<your-client-secret>

Success response (200 OK):

{
"access_token": "<JWT>",
"token_type": "Bearer",
"expires_in": 900
}

The issued token carries the roles and permissions of the user account that owns the API client. Tokens expire after 15 minutes (900 seconds); simply repeat this request to obtain a fresh token. Include the token as a Bearer credential in every subsequent API request (Authorization: Bearer <access_token>). See the OpenAPI docs at /api-docs for the full list of available endpoints.

TOKEN=$(curl -sX POST https://dashboard.example.com/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=<your-client-id>" \
--data-urlencode "client_secret=<your-client-secret>" \
| jq -r '.access_token')

curl -H "Authorization: Bearer $TOKEN" https://dashboard.example.com/api/v1/...

Method 2: Authorization Code + PKCE (for third-party applications and redirect-based flows)

This method implements the full OAuth 2.1 authorization code flow and is intended for any third-party application that needs to act on behalf of a logged-in ResQueServe user, for example custom web or desktop apps, integrations that embed a consent screen, or OAuth 2.1 clients such as MCP-compatible tools that require interactive user authorization. The implementation is significantly more involved than Method 1 and is only necessary when a redirect-based consent flow is required. PKCE with S256 is mandatory. Plain code exchange without a code challenge is not accepted.

Client                      Dashboard                    User browser
| | |
|-- POST /oauth/register ----->| |
|<-- client_id, client_secret -| |
| | |
|-- redirect user to /oauth/authorize ---------------------->|
| |<-- user logs in ------------|
| |-- redirect with code ------>|
|<-- callback with ?code=... --------------------------------|
| | |
|-- POST /oauth/token (code + verifier) ->| |
|<-- access_token -------------------------------- |

Step 1: Register a dynamic client

Third-party applications using this flow must register before starting it. The server issues a client_id and client_secret that are valid for a limited time. If the server requires an initial access token, include it as Authorization: Bearer <initial-access-token>.

POST /oauth/register
Content-Type: application/json

{
"redirect_uris": ["https://your-app.example.com/callback"],
"grant_types": ["authorization_code"],
"client_name": "My MCP Client"
}

Field

Required

Description

redirect_uris

Yes

One or more absolute URIs; must use HTTPS (http://localhost is allowed for desktop apps)

grant_types

No

Defaults to ["authorization_code"]

client_name

No

Human-readable display name

On success (201 Created) the response contains client_id and client_secret. The secret is shown only once. Store it securely immediately. Dynamically registered clients expire after a configurable period and must be re-registered. Rate limit: 5 requests per minute per IP address.

Step 2: Generate PKCE values

Before redirecting the user, generate a cryptographically random code_verifier (43–128 ASCII characters: [A-Z] [a-z] [0-9] - . _ ~) and derive code_challenge = BASE64URL(SHA-256(ASCII(code_verifier))) with no padding.

Step 3: Redirect the user to the authorization endpoint

GET /oauth/authorize
?response_type=code
&client_id=<your-client-id>
&redirect_uri=<your-redirect-uri>
&code_challenge=<BASE64URL(SHA-256(code_verifier))>
&code_challenge_method=S256
&scope=mcp:user
&state=<random-opaque-value>

Parameter

Required

Description

response_type

Yes

Must be code

client_id

Yes

UUID issued during client registration

redirect_uri

Yes

Must exactly match a URI registered for the client

code_challenge

Yes

BASE64URL(SHA-256(ASCII(code_verifier)))

code_challenge_method

Yes

Must be S256

scope

No

Space-separated list of scopes; defaults to mcp:user

state

No

Opaque value to protect against CSRF; returned unchanged in the callback

The server redirects the user to the identity provider for login. After a successful login it redirects back to your redirect_uri with the authorization code and the original state value. Authorization codes expire after 5 minutes and are single-use.

Step 4: Exchange the code for a token

Rate limit: 10 requests per minute per IP on POST /oauth/token.

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=<your-client-id>
&code=<authorization-code>
&code_verifier=<original-code-verifier>
&redirect_uri=<same-redirect-uri-as-step-3>

Parameter

Required

Description

grant_type

Yes

Must be authorization_code

client_id

Yes

UUID of the registered client

code

Yes

Authorization code from the callback

code_verifier

Yes

The original random string used in Step 2

redirect_uri

Yes

Must match the URI used in Step 3

On success the response is identical to the client credentials token response: a JSON object with access_token, token_type: "Bearer", and expires_in: 900.

# Generate PKCE values
CODE_VERIFIER=$(openssl rand -base64 64 | tr -d '=+/' | cut -c1-64)
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')

# Register a dynamic client (if not already registered)
REGISTRATION=$(curl -sX POST https://dashboard.example.com/oauth/register \
-H 'Content-Type: application/json' \
-d '{
"redirect_uris": ["https://your-app.example.com/callback"],
"client_name": "My Integration"
}')

CLIENT_ID=$(echo $REGISTRATION | jq -r '.client_id')
CLIENT_SECRET=$(echo $REGISTRATION | jq -r '.client_secret')

# Direct the user to the authorization URL (open in browser)
# https://dashboard.example.com/oauth/authorize?response_type=code
# &client_id=$CLIENT_ID&redirect_uri=...&code_challenge=$CODE_CHALLENGE
# &code_challenge_method=S256&scope=mcp:user&state=random-csrf-token

# After the user logs in, the browser is redirected to your callback with ?code=AUTH_CODE

# Exchange the code for a token
TOKEN=$(curl -sX POST https://dashboard.example.com/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "code=$AUTH_CODE" \
--data-urlencode "code_verifier=$CODE_VERIFIER" \
--data-urlencode "redirect_uri=https://your-app.example.com/callback" \
| jq -r '.access_token')

curl -H "Authorization: Bearer $TOKEN" https://dashboard.example.com/mcp/user

Discovery

The authorization server publishes machine-readable metadata at two well-known URLs used by OAuth 2.1 clients for automatic configuration.

GET /.well-known/oauth-authorization-server (RFC 8414) returns the issuer, endpoint URLs, supported grant types, PKCE methods, and available scopes. GET /.well-known/oauth-protected-resource (RFC 9728) advertises the resource and its authorization server.

Clients that receive a 401 Unauthorized from /mcp/* will also find a WWW-Authenticate header pointing to the protected resource metadata document:

WWW-Authenticate: Bearer realm="https://dashboard.example.com",
resource_metadata="https://dashboard.example.com/.well-known/oauth-protected-resource"

Example response for /.well-known/oauth-authorization-server:

{
"issuer": "https://dashboard.example.com",
"authorization_endpoint": "https://dashboard.example.com/oauth/authorize",
"token_endpoint": "https://dashboard.example.com/oauth/token",
"registration_endpoint": "https://dashboard.example.com/oauth/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "client_credentials"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["client_secret_post"],
"scopes_supported": ["mcp:user"]
}

Access Token Format (JWT)

Tokens are signed JWTs. The payload structure for user-facing tokens:

{
"jti": "019723ab-...",
"sub": "550e8400-e29b-41d4-a716-446655440000",
"name": "dispatcher.name",
"iat": 1714300000,
"exp": 1714300900,
"iss": "https://dashboard.example.com",
"aud": "user-api-production",
"roles": ["rqs:user"],
"https://schemas.xenbit.app/rqs/claims/userid": "42",
"https://schemas.xenbit.app/rqs/claims/rank": "3",
"https://schemas.xenbit.app/rqs/claims/dispatchertag": "XY42",
"given_name": "Jane",
"family_name": "Doe"
}

Standard claims:

Claim

Type

Description

jti

string (UUID v7)

Unique token identifier

sub

string (UUID)

Subject — the user's identity UUID

name

string

Username / login name

iat

number

Issued-at timestamp (Unix epoch)

exp

number

Expiration timestamp (Unix epoch)

iss

string

Issuer — the Dashboard base URL

aud

string

Audience — user-api-{environment}

roles

string[]

Granted roles

given_name

string

First name

family_name

string

Last name

Custom claims under the https://schemas.xenbit.app/rqs/claims/ namespace:

Claim

Type

Description

userid

string

Internal numeric user ID

rank

string

User rank level

dispatchertag

string

Short dispatcher identification tag


Error Responses

The OAuth endpoints (/oauth/authorize, /oauth/token, /oauth/register) return errors in the RFC 6749 JSON format. This is the only part of the API that does not follow the RFC 7807 Problem Details format used by all other API endpoints. Keep this in mind when writing error-handling code that covers both OAuth and regular API responses.

{
"error": "error_code",
"error_description": "Human-readable explanation."
}

Endpoint

error

HTTP

Cause

GET /oauth/authorize

unsupported_response_type

400

response_type is not code

GET /oauth/authorize

invalid_request

400

Missing required parameter (client_id, redirect_uri, code_challenge) or code_challenge_method is not S256

GET /oauth/authorize

invalid_scope

400

One or more requested scopes are not supported

GET /oauth/authorize

invalid_client

400

client_id is unknown or expired

GET /oauth/authorize

access_denied

Redirected to redirect_uri — user lacks the roles required for the requested scope

POST /oauth/token

unsupported_grant_type

400

grant_type is not authorization_code or client_credentials

POST /oauth/token

invalid_request

400

Missing required parameter

POST /oauth/token

invalid_grant

401

Authorization code not found, expired, or PKCE verification failed

POST /oauth/token

invalid_client

401

Client credentials not found or secret is wrong

POST /oauth/register

invalid_client_metadata

400

No redirect_uris provided

POST /oauth/register

invalid_redirect_uri

400

A URI is not an absolute URL, or uses HTTP outside of localhost

POST /oauth/register

access_denied

401

An initial access token is required and the provided token is missing or wrong

Did this answer your question?