CrabGlamp brokers OAuth for the supported third-party services directly — no external token service, no Nango, no Supabase Auth in the loop.
What is stored
For each connection, CrabGlamp stores:
- Which account it belongs to.
- The provider (Google, GitHub, Spotify).
- The OAuth scope list.
- Your email at the provider (for display in the dashboard).
- The refresh token, encrypted at rest (AES-256-GCM).
- A cached access token plus its expiry (refreshed on demand).
- A status:
activeorerror. There is no separate "expired" state — an expired refresh token flips a connection fromactivetoerroron the next refresh attempt.
Encryption at rest
The refresh token is the load-bearing secret. It is encrypted with AES-256-GCM using a key held in CrabGlamp's environment configuration, separate from the database. Only the ciphertext is stored; nothing readable ends up in a database snapshot, a log file, or a backup.
The cached access token is also stored encrypted at rest (AES-256-GCM). It is short-lived (about 1 hour for Google and Spotify; GitHub tokens are long-lived) and re-derivable from the refresh token, so even if one leaked it would expire fast.
The refresh flow
When an Agent calls GET /api/apps/{connection-id}/token:
- If the cached access token is still valid (more than 5 minutes remaining), it is returned as-is.
- Otherwise CrabGlamp uses the refresh token to get a new access token from the provider, with a 10-second timeout on that call.
- If the provider rotates the refresh token, the new one is re-encrypted and stored; the new access token is cached.
- The access token is returned to the Agent.
Concurrent token fetches are safe: CrabGlamp serializes refreshes per connection, so two Agents calling at the same time can't race and invalidate each other's token.
Error classification
Your Agent sees one of two error responses:
- 410 Gone (
connection_error) — the refresh token is dead: revoked at the provider, or expired. The connection is markederror; re-authorize from the dashboard. - 503 (
refresh_unavailable) — a transient failure (provider 5xx, network timeout) or a platform-side OAuth misconfiguration. The connection staysactive; retry with backoff. If a provider's connections all 503, it is likely a misconfiguration on our side — contact security@crabglamp.com.
Unknown provider errors default to the 503 (retry) path rather than marking the connection dead: a falsely-active connection costs one extra retry, while a falsely-errored one costs you a broken integration that needs manual re-authorization.
Revocation
Revoking from the dashboard sends a best-effort revoke to the provider, then removes the connection. Removing it on CrabGlamp's side is the guarantee — even if the provider revoke fails (network blip), the connection is gone and no Agent can fetch a token.
If you revoke from the provider side without going through the dashboard, the next Agent fetch fails and the connection is marked error the next time it's used. No background job is needed.