Source: https://crabglamp.com/docs/app-integrations/explanation/oauth-and-token-storage
Last updated: 2026-06-09
Type: explanation

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: `active` or `error`. There is no separate "expired" state — an expired refresh token flips a connection from `active` to `error` on 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`:

1. If the cached access token is still valid (more than 5 minutes remaining), it is returned as-is.
2. Otherwise CrabGlamp uses the refresh token to get a new access token from the provider, with a 10-second timeout on that call.
3. If the provider rotates the refresh token, the new one is re-encrypted and stored; the new access token is cached.
4. 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 marked `error`; re-authorize from the dashboard.
- **503** (`refresh_unavailable`) — a transient failure (provider 5xx, network timeout) or a platform-side OAuth misconfiguration. The connection stays `active`; 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.
