Skip to content

Encrypt integration tokens before production deployment #102

@ColdByDefault

Description

@ColdByDefault

Summary

The current integrations implementation stores OAuth access and refresh tokens directly in the Integration table.

This is acceptable for the current local/self-hosted development setup, where the app is private and the database is controlled by the same operator. Before Princeps is offered as a serious multi-user production service, integration tokens should be encrypted at rest.

Current behavior

Connected providers such as Google Calendar and Google Drive save tokens in:

  • Integration.accessToken
  • Integration.refreshToken

These tokens are only used server-side and are never returned to the client.

Intended production behavior

Before saving provider tokens:

  1. Encrypt access and refresh tokens with a server-side secret key.
  2. Store only encrypted values in the database.
  3. Decrypt tokens server-side only when calling provider APIs.
  4. Keep the encryption key in environment/secret storage, not in the database.

Scope

This is a production-hardening task, not an urgent local-development blocker.

The current behavior is intentional for local hosting and early development simplicity. The change should be made before multi-user hosted production.

Suggested implementation

  • Add a small server-only token encryption helper, e.g. lib/integrations/shared/crypto.ts.
  • Use AES-GCM or another authenticated encryption mode from Node crypto.
  • Add an env var such as INTEGRATION_TOKEN_ENCRYPTION_KEY.
  • Encrypt tokens in upsertIntegration().
  • Decrypt tokens in getValidToken().
  • Support a migration/backward-compatible read path if existing plaintext local tokens should keep working.
  • Document setup in docs/04_DEVELOPER_PLAYBOOK.md and .env.example.

Acceptance criteria

  • Integration tokens are encrypted before being stored.
  • Provider calls still work after token refresh.
  • Existing local/dev behavior is documented.
  • Missing encryption key fails clearly in production.
  • No token values are ever sent to the client or logs.

Metadata

Metadata

Assignees

Labels

securitySecurity vulnerability or audit finding

Projects

Status

Backlog

Relationships

None yet

Development

No branches or pull requests

Issue actions