Skip to content

Connection Encryption

Comprehensive encryption/decryption has been implemented for all connection types in the WorkspaceDO. This ensures sensitive credentials are encrypted at rest in the database and only decrypted when needed.

All connection auth types now have full encryption/decryption support:

  1. OAuth2 - Meta Ads, Google Ads, etc.

    • Encrypts: access_token, refresh_token
    • Plaintext: expires_at, scopes, token_type
  2. API Key - CallRail, Stripe, etc.

    • Encrypts: api_key
    • Plaintext: server_prefix, api_version
  3. Basic Auth - Legacy systems

    • Encrypts: password
    • Plaintext: username
  4. JWT - Token-based auth

    • Encrypts: token
    • Plaintext: expires_at
  5. Webhook Secret - Webhook validation

    • Encrypts: secret
    • Plaintext: endpoint_url

Core Encryption Utilities (packages/honeygrid-types/utils/encryption.ts)

Section titled “Core Encryption Utilities (packages/honeygrid-types/utils/encryption.ts)”

Added type-specific encryption/decryption functions:

  • encryptOAuth2AuthData() / decryptOAuth2AuthData()
  • encryptApiKeyAuthData() / decryptApiKeyAuthData()
  • encryptBasicAuthData() / decryptBasicAuthData()
  • encryptJWTAuthData() / decryptJWTAuthData()
  • encryptWebhookSecretAuthData() / decryptWebhookSecretAuthData()

Generic helpers that route to the correct type:

  • encryptAuthData(authType, plainAuthData, encryptionKey)
  • decryptAuthData(authType, encryptedAuthData, encryptionKey)

WorkspaceDO Helper Methods (apps/fullstack/src/worker/api/durable-objects/WorkspaceDO.ts)

Section titled “WorkspaceDO Helper Methods (apps/fullstack/src/worker/api/durable-objects/WorkspaceDO.ts)”

Get decrypted auth data:

const { auth_type, auth_data, platform } = await workspace.getConnectionAuthData(connectionId)
// Returns fully decrypted auth data based on auth_type

Set encrypted auth data:

await workspace.setConnectionAuthData(connectionId, 'api_key', {
api_key: 'my-plaintext-key',
server_prefix: 'us19',
})
// Automatically encrypts api_key before storing

OAuth2-Specific Methods (Meta, Google Ads)

Section titled “OAuth2-Specific Methods (Meta, Google Ads)”

Get tokens:

const { access_token, refresh_token, expires_at } =
await workspace.getConnectionOAuth2Tokens(connectionId)
// Returns decrypted access_token and refresh_token

Set tokens:

await workspace.setConnectionOAuth2Tokens(connectionId, {
access_token: 'new-token',
refresh_token: 'new-refresh',
expires_at: '2024-12-31T23:59:59Z',
scopes: ['scope1', 'scope2'],
})
// Encrypts both tokens before storing

Get API key:

const apiKey = await workspace.getConnectionApiKey(connectionId)
// Returns decrypted API key string

Set API key:

await workspace.setConnectionApiKey(connectionId, 'my-api-key', {
server_prefix: 'us19',
api_version: '3.0',
})
// Encrypts API key before storing

Legacy methods still work (OAuth2 only):

  • getConnectionAccessToken() - gets OAuth2 access_token only
  • setConnectionAccessToken() - sets OAuth2 access_token only
  • refreshConnectionAccessToken() - alias for setConnectionAccessToken

Separate helpers for Meta accounts table (not generic connections):

  • getMetaAccountAccessToken() - decrypt Meta account access_token
  • setMetaAccountAccessToken() - encrypt Meta account access_token
  • refreshMetaAccountAccessToken() - alias for set
// Creating a CallRail connection
const connection = await workspace.createConnection({
name: 'CallRail Account',
platform: 'callrail',
auth_type: 'api_key',
status: 'active',
})
// Store encrypted API key
await workspace.setConnectionApiKey(connection.id, 'my-callrail-api-key-123')
// Later: retrieve decrypted API key
const apiKey = await workspace.getConnectionApiKey(connection.id)
// apiKey = 'my-callrail-api-key-123' (decrypted)
// Or get all auth data
const { auth_data } = await workspace.getConnectionAuthData(connection.id)
// auth_data = { api_key: 'my-callrail-api-key-123' } (decrypted)
// Creating a Meta connection
const connection = await workspace.createConnection({
name: 'Meta Business',
platform: 'meta_messaging',
auth_type: 'oauth2',
status: 'pending',
})
// Store encrypted OAuth2 tokens
await workspace.setConnectionOAuth2Tokens(connection.id, {
access_token: 'EAABsb...',
refresh_token: '1//04...',
expires_at: '2024-12-31T23:59:59Z',
scopes: ['pages_messaging', 'instagram_basic'],
})
// Later: retrieve decrypted tokens
const { access_token, refresh_token } = await workspace.getConnectionOAuth2Tokens(connection.id)
// Or use generic method
const { auth_data } = await workspace.getConnectionAuthData(connection.id)
// auth_data = { access_token: '...', refresh_token: '...', ... } (all decrypted)
const connection = await workspace.createConnection({
name: 'Stripe Account',
platform: 'stripe',
auth_type: 'api_key',
status: 'active',
})
await workspace.setConnectionApiKey(connection.id, 'sk_live_abc123...', {
api_version: '2023-10-16',
})
const apiKey = await workspace.getConnectionApiKey(connection.id)
// apiKey = 'sk_live_abc123...' (decrypted)
  1. Encryption at Rest: All sensitive credentials are encrypted before being stored in the database
  2. Decryption on Demand: Credentials are only decrypted when explicitly requested
  3. AES-GCM: Uses Web Crypto API with AES-GCM encryption (256-bit keys)
  4. Environment Key: Requires ENCRYPTION_KEY environment variable (32-byte base64-encoded key)
  5. Field-Specific: Only sensitive fields are encrypted (tokens, keys, passwords); metadata stays plaintext for queries

Existing connections with plaintext credentials should be re-encrypted:

// For each connection:
const connection = await workspace.getConnection(id)
const plainAuthData = connection.auth_data
// Re-encrypt using the appropriate method
await workspace.setConnectionAuthData(connection.id, connection.auth_type, plainAuthData)

Type checking passes with no errors:

Terminal window
cd apps/fullstack
pnpm check # ✓ No TypeScript errors
  1. packages/honeygrid-types/utils/encryption.ts - Added auth-specific encryption helpers
  2. packages/honeygrid-types/index.ts - Exported new encryption functions
  3. apps/fullstack/src/worker/api/durable-objects/WorkspaceDO.ts - Added helper methods
  4. packages/honeygrid-types/validators-and-types/ConnectionTypes.ts - Removed unused type guards

Removed unused type guard functions that were replaced by the encryption utilities:

  • isEncrypted() - No longer needed
  • isOAuth2AuthData() - Replaced by decryptAuthData()
  • isApiKeyAuthData() - Replaced by decryptAuthData()
  • isBasicAuthData() - Replaced by decryptAuthData()
  • isJWTAuthData() - Replaced by decryptAuthData()
  • isWebhookSecretAuthData() - Replaced by decryptAuthData()

These were never used in the codebase and became unnecessary with the new encryption helper approach that routes based on auth_type string values.