JWT vs OAuth Tokens
Buddo supports two authentication methods: JWT (direct operator authentication) and OAuth (delegated user authentication). This guide explains what each does, when to use it, and how they work together.
Quick Comparison
| Dimension | JWT | OAuth |
|---|---|---|
| Who authenticates | The operator (you) | An end user, via your app |
| How token is obtained | POST /api/auth/login with email + password |
Authorization Code + PKCE flow via /api/oauth/authorize and /api/oauth/token |
| Token lifetime | Long-lived; refreshed via session tokens | Short-lived access token + long-lived refresh token |
| Scopes | None — full operator-level access | Fine-grained: profile:read, points:spend, deploy:manage, etc. |
| Typical use case | Admin dashboards, operator tools, backend services | User-facing apps, games, third-party integrations |
| Endpoints accessible | /api/auth/*, /api/oauth/my-apps/*, /api/social/*, /api/btc-progress/*, /api/user/* |
/api/external/*, /api/operator/*, /api/deploy/* |
JWT Authentication
JWT auth is the simplest way to interact with the Buddo API. You log in with an email and password, receive a token, and pass it as a Bearer token on every request. This is operator-level access — the token represents your account directly.
Getting a JWT
Send a POST request to
/api/auth/login
with your credentials.
Request
curl -X POST https://api.buddo.xyz/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "operator@example.com",
"password": "your-password"
}'
Response 200
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "operator@example.com",
"username": "myoperator",
"tier": "free",
"is_admin": false,
"email_verified": true,
"totp_enabled": false
}
}
totp_code field. Without it, the server returns
401 with "totp_required": true.
Using the JWT
Pass the token in the Authorization header on every request.
curl https://api.buddo.xyz/api/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
What It Grants
A JWT gives direct access to all bearerAuth-protected endpoints.
These span several API domains:
- Auth —
/api/auth/me, TOTP setup/verify, email verification, password reset - OAuth management —
/api/oauth/my-apps, create/update apps, request scopes - Social — friends, presence, chat, user search, blocking
- Education — learning paths, modules, step completion
- User — connected apps, app revocation
- Sessions —
/api/session/refresh
Token Structure and Lifetime
The JWT is a standard signed JSON Web Token containing the user ID and
account metadata. The AuthResponse schema returns:
| Field | Type | Description |
|---|---|---|
token | string | The JWT to use for Bearer auth |
user.id | uuid | Account ID |
user.email | email | Account email |
user.username | string | Display name |
user.tier | string | Account tier |
user.is_admin | boolean | Admin flag |
user.email_verified | boolean | Whether email is confirmed |
user.totp_enabled | boolean | Whether TOTP is active |
Refreshing Sessions
For long-running connections, Buddo provides a session token that can be
rotated via
POST /api/session/refresh.
This endpoint validates the current session token, sends a heartbeat, and
returns a new rotated token. The old token is invalidated atomically.
curl -X POST https://api.buddo.xyz/api/session/refresh \
-H "Content-Type: application/json" \
-d '{
"session_token": "current-session-token"
}'
Response 200
{
"session_token": "new-rotated-token",
"expires_at": "2026-05-22T12:00:00Z"
}
Best For
- Admin dashboards and internal tools
- Backend services that manage your OAuth apps
- Operator-level account management (TOTP, email, password)
- Social features used by the account owner directly
- Quick prototyping before implementing full OAuth
OAuth Authentication
OAuth is how your application acts on behalf of a Buddo user. Instead of using your own credentials, the user authorizes your app to access specific data and actions through scope-based permissions.
How It Works
Buddo implements OAuth 2.0 Authorization Code with PKCE (S256). Plain code challenges are not supported. The full flow is covered in the OAuth PKCE Flow guide. Here is the summary:
- Register an app —
POST /api/oauth/apps(requires JWT) - Redirect user —
GET /api/oauth/authorizewithcode_challenge - Exchange code —
POST /api/oauth/tokenwithcode_verifier - Use access token — pass as Bearer token on
/api/external/*endpoints - Refresh —
POST /api/oauth/tokenwithgrant_type: refresh_token
What It Grants
OAuth tokens provide access to the external API surface — endpoints designed for apps acting on behalf of users:
- User —
GET /api/external/user(profile) - Points — balance, spend, transfer via
/api/external/points/* - Sessions — status, end via
/api/external/session/* - Ads — serve campaigns, record events via
/api/external/ads/* - App balance —
GET /api/external/app/balance - Operator — app details, analytics, updates via
/api/operator/* - Deploy — full deployment lifecycle via
/api/deploy/*
Scope-Based Permissions
OAuth tokens are scoped — your app only gets access to what it asks for and what the user approves. Available scopes:
| Scope | Grants access to |
|---|---|
profile:read | Read user profile and session information |
points:read | Read user point balance |
points:spend | Spend user points (credits operator account) |
points:transfer | Transfer points between users |
app:balance:read | Read the operator app's point balance |
deploy:manage | Deploy and manage hosted applications |
POST /api/oauth/my-apps/{id}/scope-request
to request additional scopes. Requests require admin approval.
Access + Refresh Token Model
The OAuthTokenResponse from POST /api/oauth/token includes:
| Field | Type | Description |
|---|---|---|
access_token | string | Short-lived token for API requests |
token_type | "Bearer" | Always Bearer |
expires_in | integer | Seconds until access token expires |
refresh_token | string | Long-lived token to obtain new access tokens |
session_token | string | Short-lived session token (authorization_code grants only) |
session_token_expires_at | datetime | Session token expiry (authorization_code grants only) |
When the access token expires, exchange the refresh token for a new pair:
curl -X POST https://api.buddo.xyz/api/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "your-refresh-token",
"client_id": "your-client-id",
"client_secret": "your-client-secret"
}'
Token Introspection
Verify any access token's validity and metadata with
POST /api/oauth/token/verify.
No authentication required. Check the active field.
curl -X POST https://api.buddo.xyz/api/oauth/token/verify \
-H "Content-Type: application/json" \
-d '{ "token": "access-token-to-check" }'
Response 200
{
"active": true,
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"app_id": "660e8400-e29b-41d4-a716-446655440000",
"scopes": ["profile:read", "points:read"],
"expires_at": "2026-05-22T12:00:00Z"
}
Best For
- User-facing apps (games, loyalty programs, storefronts)
- Third-party integrations that need user consent
- Apps that spend or transfer user points
- Serving ads and tracking events
- Container deployments on buddocloud
Decision Flowchart
Walk through these questions to pick the right auth method:
Are you building a user-facing app?
+-- YES --> Does it need to act on behalf of individual users?
| +-- YES --> OAuth (PKCE flow)
| +-- NO --> JWT may suffice for read-only public data
+-- NO --> Are you building an admin tool or backend service?
+-- YES --> JWT
+-- NO --> Do you need user-specific data (points, profile, sessions)?
+-- YES --> OAuth
+-- NO --> Do you need operator-level access (app management, analytics)?
+-- YES --> Could be either:
| - JWT for /api/oauth/my-apps/*
| - OAuth for /api/operator/*
+-- NO --> Public endpoints need no auth at all
Migration Path: JWT to OAuth
Many operators start with JWT for speed and migrate to OAuth when they need user-level auth. Here is the typical progression:
Phase 1: JWT Only
- Register an account and log in to get a JWT
- Build your admin tools and backend services against
/api/auth/*endpoints - Manage your operator settings, TOTP, and social features
Phase 2: Add OAuth
- Register an OAuth app via
POST /api/oauth/apps(this requires your JWT) - Store the
client_idandclient_secretsecurely — the secret is shown only once - Implement the PKCE authorization flow in your user-facing app
- Request the scopes your app needs (e.g.
profile:read,points:spend)
Phase 3: Full Production
- Your backend uses JWT for operator-level tasks (managing the app, checking analytics)
- Your frontend uses OAuth tokens for user-facing operations (reading profiles, spending points, serving ads)
- Both auth methods coexist — they serve different purposes and access different endpoint groups
allowed_scopes at creation or submit a scope request afterward.
Scope requests require admin approval.
Common Patterns
Pattern 1: Operator Dashboard (JWT)
An internal dashboard where you manage your Buddo account, view analytics, and configure your OAuth apps.
| What | How |
|---|---|
| Auth method | JWT via POST /api/auth/login |
| View your profile | GET /api/auth/me |
| Manage OAuth apps | GET /api/oauth/my-apps, PUT /api/oauth/my-apps/{id} |
| Check connected apps | GET /api/user/connected-apps |
| Enable 2FA | POST /api/auth/totp/setup then /api/auth/totp/enable |
Pattern 2: Loyalty Rewards Game (OAuth)
A user-facing game where players earn and spend Buddo points. The app acts on behalf of each player.
| What | How |
|---|---|
| Auth method | OAuth PKCE with scopes profile:read points:read points:spend |
| Get player profile | GET /api/external/user |
| Check balance | GET /api/external/points |
| Charge for in-game purchase | POST /api/external/points/spend |
| Show rewarded ads | GET /api/external/ads/serve?surface_type=rewarded |
| Track ad completion | POST /api/external/ads/event |
Pattern 3: Analytics Backend (JWT)
A server-side service that monitors your operator app's performance. No user interaction required.
| What | How |
|---|---|
| Auth method | JWT via POST /api/auth/login (or stored token) |
| List your apps | GET /api/oauth/my-apps |
| View friends' status | GET /api/social/presence |
| Search users | GET /api/social/users/search?q=name |
GET /api/operator/analytics)
and app management (GET /api/operator/app) require OAuth tokens with
the app:balance:read scope, not JWT. If your backend needs these
endpoints, set up a confidential OAuth client using client_secret authentication.
Endpoint-to-Auth Quick Reference
A fast lookup of which authentication method each endpoint group requires.
| Endpoint prefix | Auth method | Notes |
|---|---|---|
/api/auth/* | JWT (most) or None | Login + register are unauthenticated |
/api/oauth/apps | JWT (create) or None (lookup) | Public lookup by client_id needs no auth |
/api/oauth/authorize | JWT | User must be logged in to authorize |
/api/oauth/token | None | Token exchange is unauthenticated |
/api/oauth/token/verify | None | Introspection is public |
/api/oauth/my-apps/* | JWT | App management for the logged-in operator |
/api/external/* | OAuth | All user-facing external endpoints |
/api/operator/* | OAuth (app:balance:read) | Operator app details and analytics |
/api/deploy/* | OAuth (deploy:manage) | Container deployment lifecycle |
/api/social/* | JWT | Friends, presence, chat, blocking |
/api/btc-progress/* | JWT | Learning paths and modules |
/api/user/* | JWT | Connected apps management |
/api/session/refresh | Session token (body) | Uses session_token, not JWT header |
/api/public/* | None | Public operator directory |
/health | None | Health check |
/.well-known/* | None | Discovery metadata |
Related Guides
| Guide | When to use it |
|---|---|
| Quickstart | Register, log in, and make your first authenticated API call (JWT) |
| OAuth PKCE Flow | Step-by-step implementation of the full OAuth authorization flow |
| Auth API Reference | Complete documentation for all authentication endpoints |
| OAuth API Reference | Complete documentation for all OAuth endpoints |