PlayaOS Developer Docs
SDK Guides

Authentication

How PlayaOS auth works for external camp SDK clients.

External camps authenticate through PlayaOS using OAuth 2.0 with PKCE. The flow is:

  1. Login redirect — your app redirects the applicant to https://auth.playaos.app/c/{your-camp}/login
  2. Code exchange — the redirect back carries a ?code=... parameter. Your server exchanges it for tokens.
  3. Session persistence — a refresh token is stored in an HTTP-only cookie and rotated automatically.

Token lifecycle

TokenTTLPurpose
idToken5–15 minutesShort-lived identity token for API access
refreshToken30 daysLong-lived, single-use, rotating. Used to get fresh idTokens.

When a refresh token is redeemed, a new one is issued and the old one is invalidated. If a refresh token is reused (theft signal), the entire token chain is revoked.

Confidential vs public clients

TypeSecretRefresh tokensUse case
PublicNoneNoBrowser-only PKCE (login/logout)
ConfidentialPLAYAOS_CLIENT_SECRETYesServer-side exchange — enables session persistence

External camps MUST provision a confidential client to get refresh tokens. Without refresh tokens, the idToken expires and the user is logged out after 5–15 minutes.

Generate your SDK client secret →

Server-side exchange

The PLAYAOS_CLIENT_SECRET must never reach the browser. Use server-side API routes for the code exchange and refresh:

  • POST /api/auth/exchange — receives { code, code_verifier, client_id, redirect_uri }, calls the PlayaOS auth server with PLAYAOS_CLIENT_SECRET, returns { idToken } and sets the refresh cookie.
  • GET /api/auth/refresh — reads the refresh cookie, calls the auth server to rotate the token, returns a fresh { idToken } and new refresh cookie.

Implement these routes in your own app. In both exchange and refresh, use NEXT_PUBLIC_PLAYAOS_CONFIDENTIAL_CLIENT_ID with its matching PLAYAOS_CLIENT_SECRET; do not pair a public client ID with a confidential client's secret. Validate client_id and redirect_uri against your configured client and registered callback before exchanging codes.

exchangeCode from @playaos/api-client targets https://auth.playaos.app/api/auth/v1/exchange. Refresh targets /api/auth/v1/refresh on that same auth host. Store the rotating refresh token in an HttpOnly, Secure cookie and send responses with Cache-Control: no-store.

These SDK exchange routes return a PlayaOS idToken for the legacy embed auth flow. It is not a Supabase session JWT: do not send it as X-PlayaOS-Member-Token to /api/v1. Current REST member calls require a Supabase session obtained through Supabase Auth. Read the REST authentication guide before choosing the auth flow for your app.

BRC Pickleball Club

BRC uses the confidential client flow. After login, their admin portal (app.brcpickleballclub.com) keeps session alive via refresh tokens, so admins don't get booted to the login page every few minutes.

The PLAYAOS_CLIENT_SECRET is set in their Vercel environment variables and used in the server-side exchange route.