Search

Connectors

Contact support

Helpdesk portal

How to integrate with the Peliqan OAuth2 server

This article applies to implementing authorization and authentication in your own apps (e.g. vibe coded apps) using Peliqan as the backend and using Peliqan OAuth2 for authorization and authentication of your app users.

Before you start

  • Contact Peliqan support and request activation of OAuth Apps in your Peliqan account
  • Register an OAuth2 app in your Peliqan account: Settings → OAuth2 Apps, and select at least one scope
  • Public app (no client secret) — browser/mobile apps. Use authorization_code; it also gets a rotating refresh_token (no secret needed, PKCE plus rotation cover that).
  • Confidential app (has a client secret). Can additionally use client_credentials (server-to-server, no user), and must also send client_secret when refreshing a token.
  • PKCE (code_challenge / code_verifier, S256 only) is mandatory for authorization_code, there is no way to opt out (RFC 9700).
  • Always pass an explicit scope. An authorize request with no scope and no default_scopes configured on the app is rejected with invalid_scope.

Base URLs

Deployment
Base URL
EU (Peliqan-hosted)
https://app.eu.peliqan.io
US (Peliqan-hosted)
https://app.us.peliqan.io
On-prem / white-label
your own domain

All examples below use a BASE_URL constant — set it to whichever of these applies to you.

Endpoint reference

Purpose
Method + path
Notes
Authorize page (browser)
GET /oauth2/authorize
Send the user's browser here. Nuxt-hosted consent screen.
Token
POST /api/oauth2/token/
All 3 grant types. Rate-limited to 20 req/min per IP.
Revoke
POST /api/oauth2/revoke/
RFC 7009. Idempotent — always succeeds.
Introspect
POST /api/oauth2/introspect/
RFC 7662 — check if a token is still active.
UserInfo
GET /api/oauth2/userinfo/
Requires a profile:read-scoped access token, sent as Authorization: Bearer.
List scopes
GET /api/oauth2/scopes/
Every scope the server recognizes, grouped by category.
Discovery
GET /.well-known/oauth-authorization-server
RFC 8414 metadata document.
JWKS
GET /api/oauth2/jwks/
Public keys for verifying access token signatures.
Dynamic Client Registration
POST /api/oauth2/register/
RFC 7591 — programmatic app registration (advanced, most devs use the Settings UI instead).

1. Authorization code flow (user login)

This is "Login with Peliqan" — the user's browser visits Peliqan, logs in, approves your app, and gets redirected back to you with a code. PKCE requires a code_verifier you generate and keep secret, and a code_challenge (its SHA-256 hash) you send up front.

TypeScript
Python
Curl

2. Refreshing a token

Peliqan rotates refresh tokens on every use — the old one is revoked, so always persist the new refresh_token from the response, not just the new access token. Both public and confidential apps get a refresh token; confidential apps must additionally authenticate with client_secret on every refresh — public apps don't send one.

TypeScript
Python
Curl

3. Client credentials (server-to-server, no user)

Requires a confidential app (has a client secret) — this grant is not available to public apps. Fully scriptable, no browser needed.

TypeScript
Python
Curl

4. Other endpoints

Revoke, introspect, get the logged-in user's identity, list available scopes, and fetch the RFC 8414 discovery document.

TypeScript
Python
Curl

Error handling

Every non-2xx response returns a machine-readable OAuth2 error code — branch on that field, not the message string.

error
Meaning
invalid_request
Missing/malformed parameter.
invalid_client
Unknown client_id, bad client_secret, or client_credentials requested on a public app.
invalid_redirect_uri
redirect_uri doesn't match any URI registered on the app.
invalid_grant
Code/refresh token expired, already used, or PKCE code_verifier mismatch — restart the flow.
invalid_scope
No scope requested and the app has no default_scopes.
unsupported_grant_type
grant_type isn't one of the three supported values.
access_denied
User declined the consent screen.
permission_denied
App-management endpoints only (not the token endpoint) — e.g. OAuth2 app management isn't enabled for the account.
server_error
Unexpected server-side failure.
TypeScript
Python
curl

Gotchas

💡
  • PKCE is not optional. Every authorization_code request needs code_challenge (S256) or the authorize call is rejected — there's no legacy non-PKCE path.
  • No scope is added automatically. You get exactly what's registered on the app plus whatever you explicitly request — nothing (including profile:read) is silently injected.
  • Refresh tokens rotate, for every client type. The old one is revoked the instant you use it — always persist the new refresh_token from the response, or the next refresh will fail with invalid_grant. Public apps get refresh tokens too (PKCE + rotation stand in for a client secret); only confidential apps additionally need to send client_secret on refresh.
  • Token endpoint is rate-limited to 20 requests/minute per IP.
  • state verification is on you. The server doesn't store or check it — you must compare the state you generated against the one that comes back on the redirect, or you're open to CSRF.
  • On-prem/white-label: just point BASE_URL at your own domain — the flow is otherwise identical.