-
Notifications
You must be signed in to change notification settings - Fork 15
Description
GitHub Issue for Frigg Core: API Key Encryption Enhancement
Repository: https://github.com/friggframework/frigg
Priority: High
Timeline: 72 hours
Title
Add API Key Fields to Default Encryption Schema + Module-Level Encryption Configuration
Issue Type
- Feature Request
- Security Enhancement
Summary
The Frigg framework's field-level encryption currently only auto-encrypts OAuth-related fields (access_token, refresh_token, id_token) in the Credential model. API key-based authentication is increasingly common, but api_key and apiKey fields are not automatically encrypted, creating a security gap for integrations using API key authentication.
Current State:
// @friggframework/core/database/encryption/encryption-schema-registry.js
const CORE_ENCRYPTION_SCHEMA = {
Credential: {
fields: [
'data.access_token', // ✅ Encrypted
'data.refresh_token', // ✅ Encrypted
'data.id_token', // ✅ Encrypted
// ❌ data.api_key - NOT encrypted
// ❌ data.apiKey - NOT encrypted
],
},
};Proposed Solutions
Solution 1: Add API Key Fields to Core Schema (Quick Fix)
File: packages/core/database/encryption/encryption-schema-registry.js
const CORE_ENCRYPTION_SCHEMA = {
Credential: {
fields: [
'data.access_token',
'data.refresh_token',
'data.id_token',
'data.api_key', // ✅ NEW: Encrypt API keys
'data.apiKey', // ✅ NEW: Encrypt API keys (camelCase variant)
],
},
// ... rest unchanged
};Pros:
- Simple, one-line change
- Immediate protection for all API key integrations
- Consistent with OAuth token encryption
- No breaking changes
Cons:
- Still requires core code changes for new field types
- Module developers can't customize encryption per-module
Solution 2: Module-Level Encryption Configuration (Recommended)
Allow API module developers to specify which fields should be encrypted directly in their module definition, without modifying core framework code.
File: packages/core/modules/module.js
Option A: encrypt flag in apiPropertiesToPersist
// In API module definition.js
const Definition = {
requiredAuthMethods: {
apiPropertiesToPersist: {
credential: ['api_key', 'siteNumber'],
entity: [],
// NEW: Specify which credential fields to encrypt
encrypt: {
credential: ['api_key'], // Encrypt only api_key, not siteNumber
entity: []
}
},
},
};Option B: Separate apiPropertiesToEncrypt object (More Explicit)
// In API module definition.js
const Definition = {
requiredAuthMethods: {
apiPropertiesToPersist: {
credential: ['api_key', 'siteNumber'],
entity: [],
},
// NEW: Explicit encryption configuration
apiPropertiesToEncrypt: {
credential: ['api_key'], // Only encrypt sensitive fields
entity: []
},
},
};Option C: Simple boolean flag (Encrypt All)
// In API module definition.js
const Definition = {
requiredAuthMethods: {
apiPropertiesToPersist: {
credential: ['api_key', 'siteNumber'],
entity: [],
},
// NEW: Encrypt all persisted credential fields
encryptCredentials: true, // Default: false
},
};Implementation Notes:
- Backward Compatibility: If no encryption config is provided, fall back to core schema
- Merge Strategy: Module-level encryption fields should MERGE with core schema, not replace
- Validation: Validate that encryption fields are subset of
apiPropertiesToPersist - Registration: Register module encryption fields with
registerCustomSchema()during module initialization
Pros:
- Module developers control their own encryption needs
- No core code changes needed for new field types
- Fine-grained control (encrypt some fields but not others)
- Self-documenting (encryption requirements visible in module definition)
Cons:
- Slightly more complex implementation
- Module developers must remember to configure encryption
Solution 3: Hybrid Approach (Best of Both)
Implement both solutions:
- ✅ Add
api_keyandapiKeyto core schema (immediate security for common case) - ✅ Add module-level encryption configuration (flexibility for custom fields)
This provides:
- Security by default for API key integrations
- Flexibility for custom authentication fields (bearer tokens, session tokens, etc.)
- No breaking changes for existing integrations
Use Cases
Use Case 1: API Key Integration (AxisCare, Quo)
// Currently requires storing as access_token (workaround)
// With fix: can use proper field name
const Definition = {
requiredAuthMethods: {
apiPropertiesToPersist: {
credential: ['api_key', 'siteNumber'],
entity: [],
},
// Option B example
apiPropertiesToEncrypt: {
credential: ['api_key'], // siteNumber is public, don't encrypt
},
},
};Use Case 2: Custom Bearer Token
const Definition = {
requiredAuthMethods: {
apiPropertiesToPersist: {
credential: ['bearer_token', 'workspace_id'],
entity: [],
},
apiPropertiesToEncrypt: {
credential: ['bearer_token'], // Custom field, auto-encrypted
},
},
};Use Case 3: Multiple Secret Fields
const Definition = {
requiredAuthMethods: {
apiPropertiesToPersist: {
credential: ['api_key', 'webhook_secret', 'signing_key', 'tenant_id'],
entity: [],
},
apiPropertiesToEncrypt: {
credential: ['api_key', 'webhook_secret', 'signing_key'],
// tenant_id is not sensitive, don't encrypt
},
},
};Impact
Affected Integrations:
- Any API module using
api_keyorapiKeyfields - Existing integrations: AxisCare, Quo (currently using workarounds)
- Future integrations: Proper API key support out of the box
Security Risk Without Fix:
- API keys stored in plain text in database (if not using
access_tokenworkaround) - Non-compliant with SOC2, HIPAA, PCI-DSS encryption requirements
- Higher risk of credential exposure in database breach
Implementation Checklist
Quick Fix (Solution 1) - Estimated 30 minutes
- Add
'data.api_key'toCORE_ENCRYPTION_SCHEMA.Credential.fields - Add
'data.apiKey'toCORE_ENCRYPTION_SCHEMA.Credential.fields - Add test case for API key encryption
- Update encryption README documentation
- Release as patch version (2.0.x)
Full Solution (Solution 3) - Estimated 4-6 hours
- Implement Solution 1 (add to core schema)
- Design module-level encryption API (decide on Option A, B, or C)
- Implement module encryption registration in
module.js - Update
encryption-schema-registry.jsto merge module schemas - Add validation for module encryption config
- Write comprehensive tests for module-level encryption
- Update documentation with examples
- Add migration guide for existing integrations
- Release as minor version (2.1.0)
Testing Requirements
// Test: Core API key encryption
describe('Core Encryption Schema', () => {
it('should encrypt data.api_key field', async () => {
const credential = await prisma.credential.create({
data: {
userId: testUser.id,
data: { api_key: 'secret-key-123' }
}
});
// Read directly from DB (bypassing decryption)
const raw = await prisma.$queryRaw`SELECT data FROM Credential WHERE id = ${credential.id}`;
expect(raw[0].data.api_key).toMatch(/^[A-Za-z0-9+/=]+:[A-Za-z0-9+/=]+:[A-Za-z0-9+/=]+$/); // Encrypted format
// Read through Prisma (with decryption)
const decrypted = await prisma.credential.findUnique({ where: { id: credential.id } });
expect(decrypted.data.api_key).toBe('secret-key-123'); // Decrypted
});
});
// Test: Module-level encryption configuration
describe('Module-Level Encryption', () => {
it('should encrypt fields specified in apiPropertiesToEncrypt', async () => {
// Test module with custom encryption config
const testModule = {
apiPropertiesToPersist: {
credential: ['custom_token', 'workspace_id'],
},
apiPropertiesToEncrypt: {
credential: ['custom_token'], // Only encrypt this one
},
};
// Verify custom_token is encrypted, workspace_id is not
});
});Documentation Updates Needed
- README.md - Add API key encryption to feature list
- database/encryption/README.md - Document module-level encryption configuration
- API_MODULE_GUIDE.md - Add encryption configuration section
- MIGRATION.md - Guide for updating existing API key integrations
Related Issues/PRs
- Related to field-level encryption implementation
- Related to API key authentication support
- Closes: (this issue will close the gap for API key encryption)
Questions for Maintainers
- Preferred solution? Quick fix (Solution 1), Full solution (Solution 2), or Hybrid (Solution 3)?
- API preference for module-level encryption? Option A (nested), Option B (separate object), or Option C (boolean flag)?
- Backward compatibility concerns? Should we require module encryption config or make it optional with smart defaults?
- Timeline? Can we target 72-hour turnaround for at least Solution 1?
Additional Context
This issue was discovered while implementing the Quo (OpenPhone) API integration, which uses API key authentication. The current workaround is storing the API key as access_token to get automatic encryption, but this is semantically incorrect and confusing for developers.
A proper fix would:
- ✅ Improve security posture for all API key integrations
- ✅ Reduce developer confusion about field naming
- ✅ Enable better encryption control for module developers
- ✅ Align with industry best practices for credential storage
Reporter: @lefnire (via Claude Code TDD implementation)
Assignee: @frigg-maintainers
Labels: enhancement, security, good-first-issue (for Solution 1)