Overview
The Chronos Calendar backend is a FastAPI application that provides a secure REST API for calendar and todo management, integrating with Supabase for authentication and database, and Google Calendar API for bidirectional event synchronization.
Technology Stack
Framework FastAPI with Uvicorn ASGI server for async request handling
Database Supabase PostgreSQL with RLS policies and service role access
Authentication Supabase Auth + Google OAuth 2.0 for secure user sessions
Encryption AES-256-GCM encryption with HKDF key derivation per user
Dependencies
# Core framework
fastapi
uvicorn[standard]
pydantic
pydantic - settings
# Database and auth
supabase
# Security and encryption
cryptography
python - multipart
# HTTP client
httpx
# Rate limiting
slowapi
# AI/Embeddings
cerebras - cloud - sdk
fastembed >= 0.2 .0
# Utilities
cachetools
# Testing
pytest
pytest - asyncio
Location : backend/requirements.txt:1
Application Architecture
backend/
├── app/
│ ├── main.py # FastAPI app initialization
│ ├── config.py # Settings management
│ ├── core/
│ │ ├── security.py # Security middlewares
│ │ ├── encryption.py # AES-GCM encryption
│ │ ├── csrf.py # CSRF token handling
│ │ ├── sessions.py # Session cookie management
│ │ └── dependencies.py # Dependency injection
│ ├── routers/
│ │ ├── auth.py # OAuth and session routes
│ │ ├── calendar.py # Calendar and event routes
│ │ └── todos.py # Todo list routes
│ ├── models/ # Pydantic models
│ └── services/ # Business logic
├── requirements.txt
└── .env # Environment configuration
FastAPI Application
App Initialization
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI(
title = "Chronos Calendar API" ,
version = "1.0.0" ,
redirect_slashes = False ,
lifespan = lifespan
)
# Middleware stack (order matters!)
app.add_middleware(GZipMiddleware, minimum_size = 1000 )
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(CSRFMiddleware)
app.add_middleware(FetchMetadataMiddleware)
app.add_middleware(OriginValidationMiddleware)
app.add_middleware(
CORSMiddleware,
allow_origins = settings.cors_origins,
allow_credentials = True ,
allow_methods = [ "GET" , "POST" , "PUT" , "PATCH" , "DELETE" , "OPTIONS" ],
allow_headers = [ "Content-Type" , "Accept" , "Authorization" , "X-Request-ID" , "X-CSRF-Token" ],
)
# Router registration
app.include_router(auth.router, prefix = "/auth" , tags = [ "auth" ])
app.include_router(calendar.router, prefix = "/calendar" , tags = [ "calendar" ])
app.include_router(todos.router, prefix = "/todos" , tags = [ "todos" ])
Location : backend/app/main.py:40
Lifespan Management
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan ( app : FastAPI):
# Startup: initialize resources
yield
# Shutdown: cleanup
await close_http_client()
Location : backend/app/main.py:34
The lifespan context manager ensures proper cleanup of async HTTP clients and other resources when the application shuts down.
Configuration Management
Settings with Pydantic
from pydantic_settings import BaseSettings
from pydantic import field_validator, model_validator
class Settings ( BaseSettings ):
model_config = SettingsConfigDict(
env_file = "../.env" ,
case_sensitive = True ,
extra = "ignore" ,
)
# Supabase
SUPABASE_URL : str
SUPABASE_SERVICE_ROLE_KEY : str
# Frontend URLs
FRONTEND_URL : str
CORS_ORIGINS : str
OAUTH_REDIRECT_URLS : str
DESKTOP_REDIRECT_URL : str
# Encryption keys
ENCRYPTION_MASTER_KEY : str
CSRF_SECRET_KEY : str
# Google OAuth
GOOGLE_CLIENT_ID : str
GOOGLE_CLIENT_SECRET : str
# Session cookies
SESSION_COOKIE_NAME : str
REFRESH_COOKIE_NAME : str
COOKIE_MAX_AGE : int
COOKIE_SECURE : bool
COOKIE_SAMESITE : Literal[ "lax" , "strict" , "none" ]
# Rate limiting
RATE_LIMIT_AUTH : str # e.g., "5/minute"
RATE_LIMIT_API : str # e.g., "100/minute"
ENVIRONMENT : str # "development" | "production"
Location : backend/app/config.py:10
Security Validation
@model_validator ( mode = "after" )
def validate_security_invariants ( self ):
if self . ENVIRONMENT == "production" :
if not self . COOKIE_SECURE :
raise ValueError ( "COOKIE_SECURE must be true in production" )
if self . COOKIE_SAMESITE not in ( "lax" , "strict" ):
raise ValueError ( "COOKIE_SAMESITE must be 'lax' or 'strict' in production" )
if self . SESSION_COOKIE_NAME .startswith( "__Host-" ) and self . COOKIE_DOMAIN is not None :
raise ValueError ( "SESSION_COOKIE_NAME with __Host- prefix cannot set COOKIE_DOMAIN" )
return self
Location : backend/app/config.py:68
Pydantic validates configuration at startup, preventing production deployments with insecure settings.
Security Middleware Stack
Adds security headers to all responses:
class SecurityHeadersMiddleware ( BaseHTTPMiddleware ):
async def dispatch ( self , request : Request, call_next : RequestResponseEndpoint) -> Response:
request_id = str (uuid.uuid4())
request.state.request_id = request_id
request.state.csp_nonce = base64.b64encode(secrets.token_bytes( 16 )).decode( "ascii" ).rstrip( "=" )
response = await call_next(request)
response.headers[ "X-Content-Type-Options" ] = "nosniff"
response.headers[ "X-Frame-Options" ] = "DENY"
response.headers[ "X-XSS-Protection" ] = "1; mode=block"
response.headers[ "X-Request-ID" ] = request_id
response.headers[ "Referrer-Policy" ] = "strict-origin-when-cross-origin"
response.headers[ "Permissions-Policy" ] = "geolocation=(), microphone=(), camera=()"
if settings. ENVIRONMENT == "production" :
response.headers[ "Strict-Transport-Security" ] = "max-age=31536000; includeSubDomains"
response.headers[ "Content-Security-Policy" ] = (
"default-src 'self'; "
f "script-src 'self' 'nonce- { csp_nonce } ' https://apis.google.com; "
f "style-src 'self' 'nonce- { csp_nonce } ' https://fonts.googleapis.com; "
"font-src 'self' https://fonts.gstatic.com; "
"img-src 'self' data: https:; "
"connect-src 'self' https://*.supabase.co https://apis.google.com; "
"frame-src https://accounts.google.com; "
"object-src 'none'; "
"base-uri 'self';"
)
return response
Location : backend/app/core/security.py:133
2. Origin Validation Middleware
Validates the Origin header on mutating requests:
class OriginValidationMiddleware ( BaseHTTPMiddleware ):
async def dispatch ( self , request : Request, call_next : RequestResponseEndpoint) -> Response:
if request.method in { "POST" , "PUT" , "PATCH" , "DELETE" }:
origin = request.headers.get( "origin" )
if origin and origin not in settings.cors_origins:
return JSONResponse( status_code = 403 , content = { "detail" : "Invalid origin" })
return await call_next(request)
Location : backend/app/core/security.py:50
Certain paths like /calendar/webhook are exempt from origin validation to allow Google Calendar webhooks.
3. CSRF Middleware
Validates CSRF tokens on authenticated mutating requests:
class CSRFMiddleware ( BaseHTTPMiddleware ):
async def dispatch ( self , request : Request, call_next : RequestResponseEndpoint) -> Response:
if request.method in { "POST" , "PUT" , "PATCH" , "DELETE" }:
if _has_auth_cookie(request):
cookie_token = get_csrf_cookie_token(request)
request_token = get_csrf_request_token(request)
if not cookie_token or not request_token or not hmac.compare_digest(cookie_token, request_token):
return JSONResponse( status_code = 403 , content = { "detail" : "Invalid CSRF token" })
if not validate_csrf_token( token = request_token, secret = settings. CSRF_SECRET_KEY ):
return JSONResponse( status_code = 403 , content = { "detail" : "Invalid CSRF token" })
return await call_next(request)
Location : backend/app/core/security.py:74
See Security Architecture for detailed CSRF implementation.
Implements Fetch Metadata Request Headers policy:
class FetchMetadataMiddleware ( BaseHTTPMiddleware ):
async def dispatch ( self , request : Request, call_next : RequestResponseEndpoint) -> Response:
if request.method in { "POST" , "PUT" , "PATCH" , "DELETE" }:
if _has_auth_cookie(request):
sec_fetch_site = request.headers.get( "sec-fetch-site" )
if sec_fetch_site and sec_fetch_site not in { "same-origin" , "same-site" , "none" }:
return JSONResponse( status_code = 403 , content = { "detail" : "Blocked by Fetch Metadata policy" })
return await call_next(request)
Location : backend/app/core/security.py:102
This prevents cross-site request forgery from untrusted origins.
API Router Structure
Authentication Router (/auth)
router = APIRouter()
@router.post ( "/web/login" )
async def web_login ():
"""Initiate OAuth flow, return Google authorization URL"""
@router.get ( "/web/callback" )
async def web_callback ( code : str ):
"""Exchange OAuth code for tokens, create session"""
@router.post ( "/logout" )
async def logout ():
"""Clear session cookies"""
@router.post ( "/refresh" )
async def refresh_session ():
"""Refresh access token using refresh token"""
@router.get ( "/csrf" )
async def get_csrf_token ():
"""Issue CSRF token for authenticated requests"""
Key Features:
Rate limiting: @limiter.limit("5/minute") on login/callback
Session cookies with HTTP-only, Secure, SameSite flags
CSRF token issuance on successful authentication
Calendar Router (/calendar)
router = APIRouter()
@router.get ( "/accounts" )
async def get_google_accounts ():
"""List all connected Google accounts"""
@router.get ( "/calendars" )
async def get_calendars ( account_id : str ):
"""Fetch calendars for a Google account"""
@router.get ( "/events" )
async def get_events (
calendar_id : str ,
sync_token : Optional[ str ] = None ,
time_min : Optional[ str ] = None ,
time_max : Optional[ str ] = None ,
):
"""Fetch events with incremental sync support"""
@router.post ( "/events" )
async def create_event ( event : CreateEventInput):
"""Create new event in Google Calendar"""
@router.put ( "/events/ {event_id} " )
async def update_event ( event_id : str , event : UpdateEventInput):
"""Update existing event"""
@router.delete ( "/events/ {event_id} " )
async def delete_event ( event_id : str , calendar_id : str ):
"""Delete event from Google Calendar"""
@router.post ( "/webhook" )
async def calendar_webhook ( request : Request):
"""Receive Google Calendar push notifications"""
@router.post ( "/watch" )
async def watch_calendar ( calendar_id : str ):
"""Register webhook for calendar changes"""
Key Features:
Incremental sync with sync_token parameter
Automatic encryption/decryption of sensitive fields
Webhook validation with Google’s signatures
Batch operations for efficient data transfer
Todos Router (/todos)
router = APIRouter()
@router.get ( "/lists" )
async def get_todo_lists ():
"""Fetch all todo lists for authenticated user"""
@router.post ( "/lists" )
async def create_todo_list ( list_data : CreateTodoListInput):
"""Create new todo list"""
@router.get ( "/" )
async def get_todos ( list_id : Optional[ str ] = None ):
"""Fetch todos, optionally filtered by list"""
@router.post ( "/" )
async def create_todo ( todo : CreateTodoInput):
"""Create new todo item"""
@router.put ( "/ {todo_id} " )
async def update_todo ( todo_id : str , todo : UpdateTodoInput):
"""Update todo item"""
@router.delete ( "/ {todo_id} " )
async def delete_todo ( todo_id : str ):
"""Delete todo item"""
@router.post ( "/reorder" )
async def reorder_todos ( order : List[ str ]):
"""Bulk reorder todos by IDs"""
Supabase Integration
Client Initialization
from supabase import create_client, Client
def get_supabase_client () -> Client:
settings = get_settings()
return create_client(
supabase_url = settings. SUPABASE_URL ,
supabase_key = settings. SUPABASE_SERVICE_ROLE_KEY ,
)
Database Operations
# Insert event with encryption
async def store_event ( user_id : str , calendar_id : str , event : dict ):
supabase = get_supabase_client()
encrypted_fields = Encryption.batch_encrypt(
fields = {
"summary" : event.get( "summary" ),
"description" : event.get( "description" ),
"location" : event.get( "location" ),
},
user_id = user_id,
)
result = supabase.table( "events" ).insert({
"user_id" : user_id,
"calendar_id" : calendar_id,
"google_event_id" : event[ "id" ],
"encrypted_summary" : encrypted_fields[ "summary" ],
"encrypted_description" : encrypted_fields[ "description" ],
"encrypted_location" : encrypted_fields[ "location" ],
"start" : event[ "start" ],
"end" : event[ "end" ],
"status" : event[ "status" ],
"updated" : event[ "updated" ],
}).execute()
return result.data[ 0 ]
Authentication
Supabase handles user authentication:
from supabase import User
async def get_current_user ( request : Request) -> User:
access_token = request.cookies.get(settings. SESSION_COOKIE_NAME )
if not access_token:
raise HTTPException( status_code = 401 , detail = "Not authenticated" )
supabase = get_supabase_client()
user = supabase.auth.get_user(access_token)
if not user:
raise HTTPException( status_code = 401 , detail = "Invalid token" )
return user
Google Calendar Integration
OAuth Flow
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import Flow
@router.post ( "/web/login" )
async def web_login ():
flow = Flow.from_client_config(
client_config = {
"web" : {
"client_id" : settings. GOOGLE_CLIENT_ID ,
"client_secret" : settings. GOOGLE_CLIENT_SECRET ,
"auth_uri" : "https://accounts.google.com/o/oauth2/auth" ,
"token_uri" : "https://oauth2.googleapis.com/token" ,
}
},
scopes = [
"https://www.googleapis.com/auth/calendar" ,
"https://www.googleapis.com/auth/userinfo.email" ,
"https://www.googleapis.com/auth/userinfo.profile" ,
],
redirect_uri = settings. OAUTH_REDIRECT_URLS [ 0 ],
)
auth_url, state = flow.authorization_url(
access_type = "offline" ,
include_granted_scopes = "true" ,
prompt = "consent" ,
)
return { "auth_url" : auth_url, "state" : state}
API Client
import httpx
from typing import AsyncGenerator
HTTP_CLIENT : httpx.AsyncClient | None = None
def get_http_client () -> httpx.AsyncClient:
global HTTP_CLIENT
if HTTP_CLIENT is None :
HTTP_CLIENT = httpx.AsyncClient( timeout = 30.0 )
return HTTP_CLIENT
async def fetch_google_calendar_events (
access_token : str ,
calendar_id : str ,
sync_token : Optional[ str ] = None ,
time_min : Optional[ str ] = None ,
time_max : Optional[ str ] = None ,
):
client = get_http_client()
params = {
"maxResults" : 2500 ,
"singleEvents" : True ,
"orderBy" : "startTime" ,
}
if sync_token:
params[ "syncToken" ] = sync_token
else :
if time_min:
params[ "timeMin" ] = time_min
if time_max:
params[ "timeMax" ] = time_max
response = await client.get(
f "https://www.googleapis.com/calendar/v3/calendars/ { calendar_id } /events" ,
params = params,
headers = { "Authorization" : f "Bearer { access_token } " },
)
response.raise_for_status()
return response.json()
Webhook Registration
@router.post ( "/watch" )
async def watch_calendar ( calendar_id : str , user : User = Depends(get_current_user)):
client = get_http_client()
access_token = await get_user_access_token(user.id)
webhook_url = f " { settings. WEBHOOK_BASE_URL } /calendar/webhook"
channel_id = str (uuid.uuid4())
response = await client.post(
f "https://www.googleapis.com/calendar/v3/calendars/ { calendar_id } /events/watch" ,
json = {
"id" : channel_id,
"type" : "web_hook" ,
"address" : webhook_url,
"expiration" : int ((time.time() + 7 * 24 * 3600 ) * 1000 ), # 7 days
},
headers = { "Authorization" : f "Bearer { access_token } " },
)
response.raise_for_status()
channel_data = response.json()
# Store channel info for renewal
await store_webhook_channel(user.id, calendar_id, channel_data)
return channel_data
Encryption Layer
See Security Architecture for detailed encryption implementation using AES-256-GCM.
Rate Limiting
SlowAPI Integration
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter( key_func = get_remote_address)
@router.post ( "/web/login" )
@limiter.limit ( "5/minute" )
async def web_login ( request : Request):
# OAuth login logic
pass
Configuration :
Auth endpoints: 5/minute per IP
API endpoints: 100/minute per user
Error Handling
Global Exception Handler
@app.exception_handler ( Exception )
async def global_exception_handler ( request : Request, exc : Exception ):
logger.exception( "Unhandled exception: %s " , exc)
return JSONResponse(
status_code = 500 ,
content = { "detail" : "Internal server error" },
)
Location : backend/app/main.py:43
Custom Exceptions
class AuthenticationError ( HTTPException ):
def __init__ ( self , detail : str = "Authentication failed" ):
super (). __init__ ( status_code = 401 , detail = detail)
class EncryptionError ( HTTPException ):
def __init__ ( self , detail : str = "Encryption failed" ):
super (). __init__ ( status_code = 500 , detail = detail)
class GoogleAPIError ( HTTPException ):
def __init__ ( self , detail : str = "Google API request failed" ):
super (). __init__ ( status_code = 502 , detail = detail)
Logging
import logging
logging.basicConfig(
level = logging. INFO ,
format = " %(asctime)s %(levelname)-8s %(name)s %(message)s " ,
datefmt = "%H:%M:%S" ,
)
# Suppress noisy logs
logging.getLogger( "httpx" ).setLevel(logging. WARNING )
logging.getLogger( "uvicorn.access" ).setLevel(logging. WARNING )
logging.getLogger( "httpcore" ).setLevel(logging. WARNING )
Location : backend/app/main.py:21
Request logs include:
Request ID (from SecurityHeadersMiddleware)
HTTP method and path
Response status code
Duration in milliseconds
GZIP Compression Responses >1KB are compressed, reducing bandwidth by 70-90%
Async I/O HTTPX async client pools connections for parallel Google API requests
Batch Encryption Multiple fields encrypted in one pass, deriving key only once
Database Indexes Supabase indexes on user_id, calendar_id, and google_event_id
Deployment
Running Locally
# Install dependencies
pip install -r requirements.txt
# Set environment variables
cp .env.example .env
# Edit .env with your credentials
# Run development server
uvicorn app.main:app --reload --port 8000
Production Deployment
# Use production ASGI server
uvicorn app.main:app \
--host 0.0.0.0 \
--port 8000 \
--workers 4 \
--log-level info \
--access-log \
--no-access-log # Use middleware logging instead
For production, use a reverse proxy like Nginx or deploy to a platform like Render, Railway, or AWS with auto-scaling.
Next Steps
Security Architecture Learn about encryption, CSRF, and session security
Frontend Architecture Explore the React frontend and offline-first data layer