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
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_key and apiKey to 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_key or apiKey fields
- 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_token workaround)
- 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
Full Solution (Solution 3) - Estimated 4-6 hours
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)
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
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, butapi_keyandapiKeyfields are not automatically encrypted, creating a security gap for integrations using API key authentication.Current State:
Proposed Solutions
Solution 1: Add API Key Fields to Core Schema (Quick Fix)
File:
packages/core/database/encryption/encryption-schema-registry.jsPros:
Cons:
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.jsOption A:
encryptflag inapiPropertiesToPersistOption B: Separate
apiPropertiesToEncryptobject (More Explicit)Option C: Simple boolean flag (Encrypt All)
Implementation Notes:
apiPropertiesToPersistregisterCustomSchema()during module initializationPros:
Cons:
Solution 3: Hybrid Approach (Best of Both)
Implement both solutions:
api_keyandapiKeyto core schema (immediate security for common case)This provides:
Use Cases
Use Case 1: API Key Integration (AxisCare, Quo)
Use Case 2: Custom Bearer Token
Use Case 3: Multiple Secret Fields
Impact
Affected Integrations:
api_keyorapiKeyfieldsSecurity Risk Without Fix:
access_tokenworkaround)Implementation Checklist
Quick Fix (Solution 1) - Estimated 30 minutes
'data.api_key'toCORE_ENCRYPTION_SCHEMA.Credential.fields'data.apiKey'toCORE_ENCRYPTION_SCHEMA.Credential.fieldsFull Solution (Solution 3) - Estimated 4-6 hours
module.jsencryption-schema-registry.jsto merge module schemasTesting Requirements
Documentation Updates Needed
Related Issues/PRs
Questions for Maintainers
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_tokento get automatic encryption, but this is semantically incorrect and confusing for developers.A proper fix would:
Reporter: @lefnire (via Claude Code TDD implementation)
Assignee: @frigg-maintainers
Labels:
enhancement,security,good-first-issue(for Solution 1)