# Securely Managing Application Secrets in Logto: The Complete Guide to KEK and DEK Encryption

> Learn to securely manage application secrets in Logto. Understand KEK and DEK encryption to protect OAuth tokens and sensitive data at rest.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**Logto protects OAuth tokens and sensitive data using a hierarchical encryption system where a Key Encryption Key (KEK) encrypts per-request Data Encryption Keys (DEKs), ensuring secrets remain confidential at rest.**

Securely managing application secrets in Logto relies on a robust **secret vault** architecture implemented in the `logto-io/logto` repository. This system encrypts sensitive data such as OAuth token sets before persistence, using AES-256-GCM encryption with a two-tier key hierarchy. The implementation ensures that even if database storage is compromised, secrets remain inaccessible without the environment-specific KEK.

## Understanding Logto's Secret Vault Architecture

Logto's encryption model follows the envelope encryption pattern, separating the duties of key management from data encryption. This architecture is defined in [`packages/core/src/utils/secret-encryption.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/utils/secret-encryption.ts) and relies on environment configuration managed through [`packages/shared/src/node/env/GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/packages/shared/src/node/env/GlobalValues.ts).

### The Key Encryption Key (KEK)

The **Key Encryption Key (KEK)** serves as the root of trust for the entire secret vault. Logto reads this from the `SECRET_VAULT_KEK` environment variable, exposed via `EnvSet.values.secretVaultKek` in [`GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/GlobalValues.ts) (line 268). This 256-bit key must be generated once during deployment and remains constant for the application lifecycle.

### The Data Encryption Key (DEK) per Request

For every encryption operation, Logto generates a unique **Data Encryption Key (DEK)** using `crypto.randomBytes(32)`. This creates a fresh 256-bit key for AES-256-GCM encryption. The DEK encrypts the actual secret payload, while the KEK encrypts the DEK itself. This approach ensures that even if a single DEK is compromised, other secrets remain secure, and it enables secure key rotation without re-encrypting all data.

## How Encryption Works in Logto's Source Code

The encryption workflow in [`packages/core/src/utils/secret-encryption.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/utils/secret-encryption.ts) follows five distinct steps:

1. **Fetch the KEK** – The system retrieves `EnvSet.values.secretVaultKek` from the process environment.
2. **Generate a DEK** – A new 256-bit key is created via `crypto.randomBytes(32)`.
3. **Encrypt the secret** – The plaintext (e.g., a JSON-encoded token set) is encrypted with the DEK, producing an initialization vector (IV), ciphertext, and authentication tag.
4. **Encrypt the DEK** – The DEK itself is encrypted using the KEK, forming an `encryptedDek` structure containing IV, encrypted key material, and auth tag.
5. **Return an EncryptedSecret** – The final object contains `{ iv, authTag, ciphertext, encryptedDek }`.

Decryption reverses this process: the KEK decrypts the DEK, then the DEK decrypts the secret payload. The `serializeEncryptedSecret` and `deserializeEncryptedSecret` functions handle conversion to and from base64 strings for database storage.

## Configuration and Environment Setup

Before utilizing the secret vault, you must configure the KEK environment variable. Logto validates this configuration at runtime before enabling secret-dependent features.

Generate a secure 256-bit key and encode it as base64:

```bash
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

```

Set the output as the `SECRET_VAULT_KEK` environment variable in your deployment configuration (Docker Compose, `.env` file, or cloud provider secret manager):

```bash
SECRET_VAULT_KEK=<base64-32-byte-key>

```

The `EnvSet` loader in [`packages/core/src/env-set/index.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/env-set/index.ts) makes these values globally available across modules, ensuring consistent access to `secretVaultKek` throughout the application.

## Practical Implementation Examples

### Encrypting OAuth Token Sets

When storing tokens after a successful OAuth flow, use the `encryptTokens` function from [`packages/core/src/utils/secret-encryption.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/utils/secret-encryption.ts):

```typescript
import { encryptTokens, serializeEncryptedSecret } from '#src/utils/secret-encryption.js';

const tokenSet = {
  access_token: 'abc123',
  refresh_token: 'def456',
  // Additional fields like id_token, scope, etc.
};

const encrypted = encryptTokens(tokenSet);
const storedBase64 = serializeEncryptedSecret(encrypted);
// Store `storedBase64` in your database

```

### Decrypting Stored Secrets

Retrieve and decrypt secrets using the corresponding deserialization and decryption functions:

```typescript
import { deserializeEncryptedSecret, decryptTokens } from '#src/utils/secret-encryption.js';

// Assume `storedBase64` was read from the database
const encrypted = deserializeEncryptedSecret(storedBase64);
const decryptedTokenSet = decryptTokens(encrypted);
// `decryptedTokenSet` matches the original `tokenSet` structure

```

### Using the Helper with Metadata

For convenience, Logto provides `encryptAndSerializeTokenResponse` which handles both encryption and metadata extraction:

```typescript
import { encryptAndSerializeTokenResponse } from '#src/utils/secret-encryption.js';
import type { TokenResponse } from '@logto/connector-kit';

const tokenResponse: TokenResponse = {
  access_token: 'xyz',
  refresh_token: 'refresh-token-value',
  scope: 'read write'
};

const encryptedTokenSet = encryptAndSerializeTokenResponse(tokenResponse);
// Store `encryptedTokenSet.encryptedTokenSetBase64` in the database
// Use `encryptedTokenSet.metadata` for quick lookup without decryption

```

## Security Guards and Validation

Logto enforces KEK presence before allowing secret storage operations. In [`packages/core/src/routes/sso-connector/index.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/sso-connector/index.ts) (line 31), the SSO connector routes check for `secretVaultKek` before enabling token storage functionality. This guard ensures that secret-related features are disabled if the vault is improperly configured, preventing accidental plaintext storage of sensitive credentials.

The encrypted payload structure ensures **confidentiality at rest** and **portability**—the base64-encoded `EncryptedSecret` can be migrated between environments or backed up without exposing cleartext data, provided the KEK remains secure.

## Summary

- Logto implements a **two-tier encryption hierarchy** using a static Key Encryption Key (KEK) and per-request Data Encryption Keys (DEK).
- The `SECRET_VAULT_KEK` environment variable must be set to a 256-bit base64-encoded key before enabling secret storage features.
- Encryption logic resides in **[`packages/core/src/utils/secret-encryption.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/utils/secret-encryption.ts)**, supporting AES-256-GCM with authentication tags.
- **Serialization utilities** convert encrypted objects to base64 strings for safe database persistence.
- Runtime guards in **[`packages/core/src/routes/sso-connector/index.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/sso-connector/index.ts)** prevent secret storage when the vault is unconfigured.
- Secrets are only recoverable by services possessing the correct `SECRET_VAULT_KEK`, ensuring confidentiality even if database storage is compromised.

## Frequently Asked Questions

### What is the difference between KEK and DEK in Logto?

The **Key Encryption Key (KEK)** is a long-term master key stored in the `SECRET_VAULT_KEK` environment variable, while the **Data Encryption Key (DEK)** is a temporary, randomly generated key created for each encryption operation. The DEK encrypts the actual secret data, and the KEK encrypts the DEK. This separation allows for secure key rotation and limits exposure if a single encryption key is compromised.

### How do I generate a secure SECRET_VAULT_KEK?

Generate a cryptographically secure 256-bit key using Node.js crypto and encode it as base64: `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`. Store this value in your environment configuration as `SECRET_VAULT_KEK`. This key must be consistent across all Logto instances sharing the same database and should be backed up securely—if lost, encrypted secrets cannot be recovered.

### Where does Logto store the encrypted secrets?

Logto stores the **serialized encrypted secrets** in the configured database (PostgreSQL by default) as base64-encoded strings. The actual encryption and decryption occur in memory within the application layer using [`packages/core/src/utils/secret-encryption.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/utils/secret-encryption.ts). The database only ever sees the encrypted payload containing the ciphertext, IV, auth tag, and encrypted DEK—never the plaintext or the KEK.

### What happens if the SECRET_VAULT_KEK is lost?

If the `SECRET_VAULT_KEK` is lost or changed without migrating existing data, **all encrypted secrets become permanently inaccessible**. The KEK is required to decrypt the DEK, which in turn decrypts the secret payload. There is no recovery mechanism or backdoor—this design ensures security through true cryptographic separation. Always maintain secure backups of your KEK in a hardware security module (HSM) or enterprise secret manager.