feat(capability): per-scope BYO provider credentials + Twilio SMS egress
IIOS becomes the tenant's integration/credential hub for anything it sends. New IiosProviderCredential (one row per scope+providerType) seals the secret with AES-256-GCM under IIOS_CRED_KEY (secret-crypto.ts); only non-secret displayHints are ever read back. ProviderCredentialService upsert/resolve/status; TwilioSmsProvider resolves the caller's own creds per scope at send time and POSTs to Twilio (fail-closed NOT_CONFIGURED when unset). Registered over the SMS sandbox only when the platform key + store are present. PUT/GET /v1/providers/:type/credentials (scope-fenced, masked reads). 16 new unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -317,10 +317,33 @@ model IiosScope {
|
||||
callbacks IiosCallbackRequest[]
|
||||
notificationSubscriptions IiosNotificationSubscription[]
|
||||
messageTemplates IiosMessageTemplate[]
|
||||
providerCredentials IiosProviderCredential[]
|
||||
|
||||
@@index([orgId, appId, tenantId])
|
||||
}
|
||||
|
||||
/// A tenant's own credentials for an egress provider (BYO: Twilio SMS, own SMTP, …).
|
||||
/// The secret is sealed with AES-256-GCM under the platform key (IIOS_CRED_KEY); only
|
||||
/// `displayHints` (non-secret, e.g. from-number / SID last-4) is ever read back out.
|
||||
/// One row per (scope, providerType). Resolved at send time by the channel's provider.
|
||||
model IiosProviderCredential {
|
||||
id String @id @default(cuid())
|
||||
scopeId String
|
||||
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
||||
providerType String // TWILIO_SMS | SMTP | WHATSAPP_CLOUD | …
|
||||
cipherText String // base64(AES-256-GCM ciphertext of the JSON config)
|
||||
iv String // base64 nonce
|
||||
authTag String // base64 GCM auth tag
|
||||
keyVersion Int @default(1)
|
||||
displayHints Json? // non-secret display fields (fromNumber, sidLast4, …)
|
||||
enabled Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([scopeId, providerType])
|
||||
@@index([scopeId])
|
||||
}
|
||||
|
||||
/// A channel-specific handle (phone/email/portal user). NOT identity — stays
|
||||
/// UNVERIFIED until MDM resolves it; no silent merge.
|
||||
model IiosSourceHandle {
|
||||
|
||||
Reference in New Issue
Block a user