Authentication Flow
GospeLib uses Clerk as the authentication provider in Phase 1. The auth service is a thin Go wrapper that translates Clerk events into our domain and provides a stable internal interface — swapping Clerk later changes only the wrapper, not anything downstream.
Flow Diagram
sequenceDiagram
participant User as User (Mobile/Web)
participant Clerk as Clerk Auth
participant App as GospeLib App
participant GW as API Gateway
participant Svc as Downstream Service
participant PG as PostgreSQL
User->>Clerk: Sign in (email, magic link, or Google OAuth)
Clerk-->>App: JWT (15 min) + refresh token (30 days)
App->>GW: API request + Authorization: Bearer <jwt>
GW->>GW: Validate JWT signature (JWKS cached)
GW->>GW: Extract claims: userId, email, sessionId
GW->>GW: Inject headers: X-User-Id, X-User-Plan
GW->>Svc: Forward request with injected headers
Svc->>PG: Query using X-User-Id
Svc-->>GW: Response
GW-->>App: Response
Token Lifecycle
| Token | Lifetime | Storage | Refresh Strategy |
|---|---|---|---|
| JWT (access token) | 15 minutes | In-memory (app) | Clerk SDK auto-refreshes before expiry |
| Refresh token | 30 days | Secure storage (Keychain/SecureStore) | Used to obtain new JWT silently |
| Session | 30 days (configurable) | Clerk manages server-side | Revocable via Clerk dashboard |
Gateway JWT Validation
The gateway validates every JWT against Clerk's JWKS (JSON Web Key Set) endpoint:
- JWKS caching — keys are cached in memory and refreshed every 5 minutes
- Signature verification — validates the JWT's RS256 signature against Clerk's public key
- Claims extraction — extracts
userId,email, andsessionIdfrom the JWT payload - Header injection — sets
X-User-IdandX-User-Planheaders on the forwarded request
Downstream services trust X-User-Id without re-validating the JWT because they are only reachable via the gateway (not exposed publicly).
Clerk Webhook Sync
When users sign up or update their profiles in Clerk, webhook events sync the data to PostgreSQL:
sequenceDiagram
participant Clerk
participant GW as Gateway
participant Auth as Auth Service
participant PG as PostgreSQL
participant Redis as Redis Stream
Clerk->>GW: POST /api/v1/auth/webhook (user.created)
GW->>Auth: Forward (no auth required for webhooks)
Auth->>Auth: Verify Clerk webhook signature
Auth->>PG: UPSERT gl_users (clerk_id, email, name)
Auth->>Redis: XADD gl:events:users {user.created}
Auth-->>GW: 200 OK
Webhook signature verification ensures the request actually came from Clerk.
Auth Service Responsibilities
The auth service (Go/Chi, port 8200) handles:
- Webhook receiver —
user.created,user.updated,session.createdevents from Clerk - User sync — Clerk user records → PostgreSQL
gl_userstable - Profile API —
/users/{id}for other services to fetch user profiles - Entitlement join —
/users/{id}/entitlementsjoins user record with billing plan
Phase Progression
| Phase | Auth Strategy |
|---|---|
| Phase 1 | Clerk SaaS (free to 10K MAU) |
| Phase 2 | Clerk SaaS (paid tier) |
| Phase 3 | Option to migrate to custom auth (the wrapper makes this safe) |
Why Clerk in Phase 1? Building production-grade auth (MFA, magic links, social auth, session management, device trust) takes 2–6 months. Clerk does this for $0–$100/month at our scale, saving months of development time.
Related Pages
- Service Communication — How the gateway injects auth headers
- Entitlements & Authorization — Plan-gated route configuration
- Secrets & Network Policy — Clerk key management
- PostgreSQL Schema —
gl_userstable details