Best Practices
Security, caching, and UX patterns for OAuth 2.0 integrations.
Security
- Never store tokens client-side — all tokens are server-side secrets. The
bi_oat_prefix distinguishes OAuth tokens frombi_pub_public keys - Use the narrowest scope set — users approve fewer permissions more readily
- Handle
410 grant_revokedas a first-class state — don't show a generic error, show "Reconnect" - Don't retry
403 missing_scope— the user must re-authorize with wider scope
Caching
- Cache installation tokens in memory — 1h TTL with 60s refresh window = ~2 mints/hour
- Don't persist installation tokens to disk — they're short-lived; caching in Redis with matching TTL is fine, Postgres is wasteful
- Key cache by
(grantId, organizationId)— a scope change re-issues a new grant
Token rotation
- Always persist the latest
refresh_token— it may rotate on each use - Refresh proactively — don't wait for a 401, check expiry before each API call
Multiple organizations
Each grant is scoped to one organization. The consent screen can create grants for multiple organizations in one approval when the user selects more than one. Store one connection row per returned grant.
To add access later:
- Start a new authorize flow
- Ask the user to select the additional organization
- Store the new
grant_id+refresh_token
If a user re-authorizes for an org they already have a grant for, we update the existing grant in-place. Your callback should UPSERT on (userId, organizationId).
What to store
The minimum per connection:
interface BetterI18nConnection {
ownerId: string; // Your user ID
grantId: string; // From token exchange
refreshToken: string; // Server-side only
organizationId: string; // From installation token response or organizations API
scopes: string[]; // What was granted
projectIds: string[]; // [] = all projects
disconnectedAt?: Date; // Set on revoke
}CDN delivery
Translations published via the API are served at:
https://cdn.better-i18n.com/{org}/{project}/{locale}.jsonThis URL is public — no auth needed. Your users' apps fetch translations from the CDN at runtime using our SDKs or a simple fetch().
MCP for AI agents
The same bi_oat_ token works in the MCP server. If your platform has AI coding capabilities, inject the MCP config into the user's workspace after OAuth consent. See MCP Integration for details.