Auth (OAuth2 + PKCE)
User identity across the RBS ecosystem. The IdP is the CPR (Control Plane Router) at
ecosystem.redbroomsoftware.com— every app is a client. Camino is NOT the issuer: its/oauth/*endpoints return 410 Gone (301 on/oauth/authorize) pointing here.
Status: LIVE (CPR since Topic 17 Phase 4D, 2026-05) Provider: CPR (ecosystem.redbroomsoftware.com) Source of truth: ecosystem-sdk/packages/api/api/oauth/*.ts + ecosystem-sdk/packages/api/lib/oauth-provider.ts — read the code, do not replicate the flow in prose. This page states only the contract facts the discovery document gets wrong. Flow: OAuth2 Authorization Code with PKCE (RFC 7636, PKCE mandatory) Scopes: openid profile email ecosystem
⚠️ Contract facts every client MUST know (the discovery doc over-claims)
- Tokens are OPAQUE — 32-byte random hex, validated server-side by SHA-256 hash lookup. There is no JWT.
id_tokenis NEVER issued. The token response is{access_token, token_type, expires_in, refresh_token, scope}(lib/oauth-provider.ts:369-377). Any code that decodestokens.id_tokenfails 100% of the time (this broke Kiina F2, 2026-07-31).- There is no JWKS.
/.well-known/jwks.json→ 404. The discovery document'sjwks_uriandid_token_signing_alg_values_supported: ['RS256']are a known over-claim (api/oauth/openid-configuration.ts:47,56, PD-S186-B / PD-197 Gap D). - User identity comes ONLY from
GET /oauth/userinfowith the Bearer access token:sub(stable — the CPR Supabaseauth.users.id),email(omitted for phone-only users — key your grants accordingly),email_verified,name,picture,locale, and with scopeecosystem:organization_id,linked_accounts. - Standard OIDC libraries (
openid-client, next-auth) will fail on discovery-then-validate. UseRBSAuthfrom@r-bsoftware/ecosystem-sdk(issuer defaults to the CPR since 0.29.x) or a hand-rolled client that follows facts 1-4.
Endpoints (all on ecosystem.redbroomsoftware.com)
| Method | Path | Purpose |
|---|---|---|
| GET | /.well-known/openid-configuration | OIDC discovery (⚠️ see over-claims above) |
| GET | /oauth/authorize | Start auth flow (PKCE required; 302 to /login without hub_session) |
| POST | /oauth/token | Exchange code or refresh token (rotation on refresh) |
| GET | /oauth/userinfo | Current user (Bearer access token) |
| POST | /oauth/revoke | Revoke the user×client token pair + consent (⚠️ does NOT propagate to app-local sessions — PD-197 Gap E) |
SDK helpers
import { RBSAuth, generateState, validateState, createRBSCallbackHandler } from '@r-bsoftware/ecosystem-sdk'- SDK ≥0.29 defaults
issuerto the CPR. Apps pinned to ≤0.20 (manchapackages/shared, cookie-monster, puppy-love) still default to Camino and work only via the 301 + token-exchange fallback — bump the SDK before "fixing" their issuer. - The callback factory's
idpBaseUrls: [Camino, CPR]ordering is legacy dual-issue Phase 3; the Phase 4 cutover to CPR-first was never applied fleet-wide (PD-197). - The factory's identity-link POST (
/api/ecosystem/identity/link) currently 404s against the CPR — no newecosystem_user_linksrows are produced anywhere (PD-197 Gap B).
Server-to-server
For partner apps that don't need a user session, use API keys instead:
Authorization: Bearer <api_key>API keys are provisioned in Colectiva — contact api-support@redbroomsoftware.com.
Gotchas
- Refresh tokens rotate. Every
/oauth/tokencall withgrant_type=refresh_tokenreturns a new refresh token — persist it or lose the session. - State parameter is required. Use
generateState()/validateState()from the SDK for CSRF protection. - Pre-registered clients only. OAuth clients live in
oauth_clientsin the CPR Supabase (shburtekrfoavfaeipgw),client_idconventionrbs_<slug>_<hex16>. Registration runbook:kiina/docs/F2-APLICAR.mdpaso 2. Not self-service. - PKCE is mandatory for all flows.
History
Camino was the original IdP (retired Topic 17 Phase 4D; its oauth_clients rows were copied to the CPR in Phase 2). The user directory (auth.users) was unified into the CPR Supabase by PD-192 Opción A (S554) with preserved UUIDs, so sub == legacy camino_user_id for backfilled users. Remaining architectural gaps (new-Camino-user flow, dead identity-link producer, dual ecosystem_user_links copies, revocation propagation) are tracked as PD-197 in the memory pending-decisions queue.