OAuth Integration Guide
PDC turns an upstream login into a standard OAuth 2.0 AS via @qlover/oauth-wrapper. Two options ship in-box—default Supabase Auth (incl. GitHub/Google), optional Brain User to proxy an existing login API. Docs below follow the Supabase path; switch with NEXT_PUBLIC_OAUTH_UPSTREAM_PROVIDER.
Architecture: authentication client and OAuth authorization
PDC connects to Supabase Auth via SupabaseOAuthProvider, supporting GitHub, Google sign-in and email/password, phone OTP. It also provides standard OAuth 2.0 endpoints (authorize, consent, token, PKCE, userinfo) via @qlover/oauth-wrapper for third-party app integration.
Current deployment: Supabase Auth
Users can sign in via GitHub, Google, or email/password (POST /api/user/login), phone OTP, email Magic Link. OAuth authorization endpoints are independent of the sign-in method. See .env.template for SUPABASE_URL, SESSION_SECRET, ENCRYPTION_KEY, etc.
Protocol overview
Exposes the OAuth 2.0 authorization code grant (RFC 6749) for confidential and public clients (PKCE for public). The access_token returned to clients comes from Supabase Auth (JWT); the platform also issues its own refresh_token for third-party refresh grants.
Authorization code flow
- Create an app in the developer console and register redirect_uri values (HTTPS required except localhost).
- Redirect the user to GET /oauth/authorize with client_id, redirect_uri, response_type=code; public clients must include PKCE parameters.
- The end user signs in on this site (GitHub, Google, or email/password), grants consent, then the browser returns to redirect_uri with ?code=...&state=....
- Your backend calls POST /oauth/token with the code to obtain access_token (and optional refresh_token).
- Call GET /userinfo with Bearer access_token to load the user profile.
Endpoints
| Method | Path | Role | Notes |
|---|---|---|---|
| POST | /api/oauth/verify | End-user sign-in (server) | Email/password sign-in for the consent UI session; orchestrated by Supabase Auth—not for OAuth clients. |
| GET | /oauth/authorize | Authorize (browser) | Consent UI; unauthenticated users are sent to login then returned here. |
| POST | /oauth/token | Token (server) | grant_type=authorization_code or refresh_token; client auth via Basic or form body. |
| GET | /oauth/userinfo | Userinfo (server) | Requires Authorization: Bearer; returns sub, email, name, and related claims. |
Token exchange
Exchange authorization code
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https%3A%2F%2Fapp.example%2Fcallback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&code_verifier=VERIFIERRefresh token
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET// 200 OK { "access_token": "...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "...", "scope": "openid profile" }
PKCE (public clients)
Generate code_verifier before authorize, pass its S256 digest as code_challenge, then send code_verifier when exchanging the code. Confidential clients may omit PKCE.
Userinfo
Success returns JSON with sub (user id), email, name, and optional roles. Invalid or expired tokens yield 401 with error=invalid_token.
GET /oauth/userinfo
Authorization: Bearer ACCESS_TOKENError responses
Authorization errors redirect with error and error_description query params; token and userinfo errors are JSON (e.g. invalid_request, invalid_grant, invalid_client, invalid_token).