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.

Not sure which to pick? Jump to the Decision Flowchart for a quick answer.

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: If the account has two-factor authentication enabled, include a 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:

Token Structure and Lifetime

The JWT is a standard signed JSON Web Token containing the user ID and account metadata. The AuthResponse schema returns:

FieldTypeDescription
tokenstringThe JWT to use for Bearer auth
user.iduuidAccount ID
user.emailemailAccount email
user.usernamestringDisplay name
user.tierstringAccount tier
user.is_adminbooleanAdmin flag
user.email_verifiedbooleanWhether email is confirmed
user.totp_enabledbooleanWhether 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"
}
Rate limit: Session refresh is limited to 20 requests per minute per IP.

Best For

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:

  1. Register an appPOST /api/oauth/apps (requires JWT)
  2. Redirect userGET /api/oauth/authorize with code_challenge
  3. Exchange codePOST /api/oauth/token with code_verifier
  4. Use access token — pass as Bearer token on /api/external/* endpoints
  5. RefreshPOST /api/oauth/token with grant_type: refresh_token

What It Grants

OAuth tokens provide access to the external API surface — endpoints designed for apps acting on behalf of users:

Scope-Based Permissions

OAuth tokens are scoped — your app only gets access to what it asks for and what the user approves. Available scopes:

ScopeGrants access to
profile:readRead user profile and session information
points:readRead user point balance
points:spendSpend user points (credits operator account)
points:transferTransfer points between users
app:balance:readRead the operator app's point balance
deploy:manageDeploy and manage hosted applications
Need more scopes? Use 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:

FieldTypeDescription
access_tokenstringShort-lived token for API requests
token_type"Bearer"Always Bearer
expires_inintegerSeconds until access token expires
refresh_tokenstringLong-lived token to obtain new access tokens
session_tokenstringShort-lived session token (authorization_code grants only)
session_token_expires_atdatetimeSession 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

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
Rule of thumb: If your code runs with your credentials, use JWT. If it runs with a user's authorization, use OAuth.

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

  1. Register an account and log in to get a JWT
  2. Build your admin tools and backend services against /api/auth/* endpoints
  3. Manage your operator settings, TOTP, and social features

Phase 2: Add OAuth

  1. Register an OAuth app via POST /api/oauth/apps (this requires your JWT)
  2. Store the client_id and client_secret securely — the secret is shown only once
  3. Implement the PKCE authorization flow in your user-facing app
  4. Request the scopes your app needs (e.g. profile:read, points:spend)

Phase 3: Full Production

  1. Your backend uses JWT for operator-level tasks (managing the app, checking analytics)
  2. Your frontend uses OAuth tokens for user-facing operations (reading profiles, spending points, serving ads)
  3. Both auth methods coexist — they serve different purposes and access different endpoint groups
Important: OAuth apps start with no scopes. You must specify 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.

WhatHow
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.

WhatHow
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.

WhatHow
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
Note: Operator-level analytics (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 prefixAuth methodNotes
/api/auth/*JWT (most) or NoneLogin + register are unauthenticated
/api/oauth/appsJWT (create) or None (lookup)Public lookup by client_id needs no auth
/api/oauth/authorizeJWTUser must be logged in to authorize
/api/oauth/tokenNoneToken exchange is unauthenticated
/api/oauth/token/verifyNoneIntrospection is public
/api/oauth/my-apps/*JWTApp management for the logged-in operator
/api/external/*OAuthAll 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/*JWTFriends, presence, chat, blocking
/api/btc-progress/*JWTLearning paths and modules
/api/user/*JWTConnected apps management
/api/session/refreshSession token (body)Uses session_token, not JWT header
/api/public/*NonePublic operator directory
/healthNoneHealth check
/.well-known/*NoneDiscovery 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