Skip to main content
POST
/
auth
/
web
/
callback
curl -X POST "https://api.chronos.app/auth/web/callback" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "code": "4/0AeanS0ZZ9K..."
  }'
{
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "email": "[email protected]",
    "name": "Jane Doe",
    "avatar_url": "https://lh3.googleusercontent.com/a/...",
    "created_at": "2026-03-04T10:30:00.000Z"
  },
  "expires_at": 1709553000
}

Request

Body Parameters

code
string
required
The authorization code returned by Google after the user authorizes the application. This code is single-use and expires quickly (typically within 10 minutes).

Response

user
object
required
The authenticated user’s profile information
expires_at
integer
required
Unix timestamp (in seconds) when the session expires. Used to determine when to call the refresh endpoint.
curl -X POST "https://api.chronos.app/auth/web/callback" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "code": "4/0AeanS0ZZ9K..."
  }'
{
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "email": "[email protected]",
    "name": "Jane Doe",
    "avatar_url": "https://lh3.googleusercontent.com/a/...",
    "created_at": "2026-03-04T10:30:00.000Z"
  },
  "expires_at": 1709553000
}

Cookies Set

This endpoint sets three HTTP-only cookies on successful authentication:
session_token
cookie
Session cookie containing the access token. Name is configured via SESSION_COOKIE_NAME (typically __Host-session in production).
  • HttpOnly: Yes
  • Secure: Yes (production)
  • SameSite: Lax or Strict (production)
  • Max-Age: From COOKIE_MAX_AGE config
refresh_token
cookie
Refresh token cookie for obtaining new access tokens. Name is configured via REFRESH_COOKIE_NAME (typically __Host-refresh).
  • HttpOnly: Yes
  • Secure: Yes (production)
  • SameSite: Lax or Strict (production)
  • Max-Age: From COOKIE_MAX_AGE config
csrf_token
cookie
CSRF protection token. Name is configured via CSRF_COOKIE_NAME.
  • Max-Age: From CSRF_TOKEN_TTL_SECONDS config

Error Codes

400
error
Bad Request - Invalid or expired authorization code, or failed to create session
429
error
Rate Limit Exceeded - Too many callback requests from this IP address
502
error
Bad Gateway - External service (Google or Supabase) is unavailable or returned an error

Rate Limits

This endpoint is rate-limited according to the RATE_LIMIT_AUTH configuration. The rate limiter uses the client’s IP address as the key.

Implementation Details

Code Exchange Flow

  1. Exchanges authorization code for session using Supabase Auth
  2. Retrieves user profile from Supabase
  3. Extracts Google OAuth tokens (access and refresh) from session
  4. Stores Google account credentials and tokens in database:
    • Encrypts tokens using user-specific encryption key
    • Sets token expiration to 1 hour (Google’s default for access tokens)
    • Links Google account to user via google_accounts table
  5. Sets session cookies (access token, refresh token, CSRF token)
  6. Returns user profile and session expiration time

Google Account Storage

The endpoint automatically stores Google account information including:
  • Google user ID and email
  • Encrypted access and refresh tokens for Google Calendar API
  • Account metadata (name, reauth status)
This allows the application to make Calendar API requests on behalf of the user.

Security Notes

  • Authorization codes are single-use and expire quickly
  • All tokens are stored as HTTP-only cookies, preventing XSS attacks
  • Google OAuth tokens are encrypted before database storage
  • PKCE verification ensures the code was generated by this server
  • Failed Google account storage is logged but doesn’t fail authentication (user can link later)
Important: The credentials: 'include' option must be set when calling this endpoint from JavaScript to ensure cookies are properly sent and received.
Source: backend/app/routers/auth.py:179-204

Build docs developers (and LLMs) love