Credential Security

Credentials — API keys, OAuth tokens, webhook signing secrets, and integration credentials — are the most sensitive data in the integrations system. A compromised credential can grant an attacker access to connected services, financial data, or customer records. Otesse employs multiple layers of protection to ensure credentials are secure at rest, in transit, and throughout their lifecycle.

Encryption at Rest

All integration credentials are encrypted before storage using AES-256-GCM (Advanced Encryption Standard with Galois/Counter Mode).

How It Works

  1. When a credential is created (API key entry or OAuth token exchange), the plaintext value is passed to the encryption service
  2. The encryption key is derived from the INTEGRATIONENCRYPTIONKEY environment variable — this key is never stored in the database
  3. AES-256-GCM encryption produces ciphertext plus an authentication tag
  4. The ciphertext is stored in IntegrationCredential.credentialValueEncrypted
  5. The plaintext is immediately discarded from memory — it exists only during the encrypt operation

Security Properties

PropertyGuarantee
ConfidentialityAES-256 is computationally infeasible to crack with current technology
IntegrityGCM mode includes an authentication tag that detects tampering
Non-recoverabilityWithout the environment-variable-based encryption key, ciphertext is useless

On Disconnection

When an integration is disconnected, credentials receive defense-in-depth treatment:

  1. The credentialValueEncrypted field is overwritten with an empty string
  2. The record is then soft-deleted (isDeleted = true, isActive = false)
  3. This ensures that even if a soft-deleted record is accessed, the encrypted value is already destroyed

API Key Hashing

Otesse-generated API keys (the ones used by external applications to call the Otesse API) receive different treatment from integration credentials:

  1. The full API key is generated and displayed to the user once
  2. The key is immediately hashed using SHA-256
  3. Only the hash is stored in the database
  4. When an API request arrives, the provided key is hashed and compared to stored hashes
  5. The plaintext key never exists in the database at any point after generation

This means that even a complete database compromise cannot reveal API keys — only their hashes.

What Is Never Stored or Logged

The following data never appears in the database, logs, or API responses:

DataProtection
Plaintext API keysOnly the SHA-256 hash is stored. Last 4 characters stored separately for display
Plaintext OAuth tokensOnly AES-256-GCM encrypted ciphertext is stored
Plaintext integration credentialsOnly encrypted ciphertext is stored
SSN valuesEncrypted at rest, last 4 digits stored separately for display
Webhook signing secretsOnly encrypted ciphertext is stored

Log Sanitization

All logging is sanitized to exclude credential values:

  • API request logs record the endpoint, method, status code, and response time — never the Authorization header value
  • Integration connection logs record the provider name and action type — never the credential values
  • Audit logs record who performed an action and when — never the credential content

Access Controls

Who Can Access Credentials

ActionPermission Required
View integration connectionssettings.view
Connect/disconnect integrationssettings.edit
Generate API keyssettings.edit
Revoke API keyssettings.edit
View credential detailsNever — credentials are never displayed after creation

Credential Isolation

  • Integration credentials are scoped to a specific connection (one user's authentication)
  • API keys are scoped to the organization
  • One organization cannot access another organization's credentials
  • One user's OAuth tokens cannot be accessed by another user in the same organization

Audit Trail

Every credential-related action is logged in the audit trail:

ActionLogged Details
Credential createdProvider, auth type, user, timestamp, IP address
Credential refreshed (OAuth)Provider, old token expiry, new token issued, timestamp
Credential revokedProvider, revoking user, timestamp, IP address
Credential deleted (disconnect)Provider, user, timestamp, whether OAuth revocation succeeded
API key generatedKey name, scope level, scopes granted, user, timestamp
API key revokedKey name, revoking user, timestamp, last usage info

Security Best Practices for Users

  1. Use the minimum scope needed — When generating API keys, select only the permissions the application actually requires. A reporting dashboard needs read-only access, not admin.
  1. Set expiration dates — Avoid "Never" expiration. 90-day rotation is recommended for most use cases. This limits the damage window if a key is compromised.
  1. Use separate keys per application — Each external application or integration should have its own API key. This enables independent revocation and usage tracking.
  1. Store credentials in environment variables — Never hard-code API keys in source code, commit them to version control, or share them in plaintext via email or chat.
  1. Monitor usage logs — Review API usage logs regularly for unexpected endpoints, unfamiliar IP addresses, or unusual request volumes that could indicate a compromised key.
  1. Revoke immediately if compromised — If a credential is exposed (accidentally committed to a public repository, shared in a chat message, etc.), revoke it immediately and generate a replacement.
  1. Rotate proactively — Do not wait for keys to expire. Generate replacements before the old key's expiration date to avoid service disruption.