Encrypt and store data in Redis. Uses AES-256-GCM encryption with unique IVs per entry.
npm install secure-store-redisimport SecureStore, { SecretValidator } from "secure-store-redis";
const store = new SecureStore({
uid: "myApp",
secret: SecretValidator.generate(),
redis: { url: "redis://localhost:6379" },
});
await store.connect();
await store.save("key", { foo: "bar" });
const data = await store.get("key");
await store.disconnect();new SecureStore(config: SecureStoreConfig)| Option | Type | Required | Description |
|---|---|---|---|
uid |
string | Yes | Unique prefix for Redis keys (e.g., "myApp", "myApp:sessions") |
secret |
string | Yes | 32-character encryption secret. Use SecretValidator.generate(). |
redis |
RedisOptions | { url: string } | { client: Redis | Cluster } | Yes | Redis connection config or existing client |
allowWeakSecrets |
boolean | No | Bypass secret strength validation (default: false) |
You can pass your own ioredis Redis or Cluster client instead of connection options. This enables connection sharing, pre-configured clients, and integration with existing Redis infrastructure.
import { Redis } from "ioredis";
import SecureStore, { SecretValidator } from "secure-store-redis";
const redis = new Redis({ host: "localhost", port: 6379 });
const store = new SecureStore({
uid: "myApp",
secret: SecretValidator.generate(),
redis: { client: redis },
});
await store.connect();
await store.save("key", "value");
await store.disconnect();
// Your client is still connected - you manage its lifecycle
console.log(redis.status); // "ready"
await redis.quit();import { Cluster } from "ioredis";
import SecureStore, { SecretValidator } from "secure-store-redis";
const cluster = new Cluster([
{ host: "node1", port: 6379 },
{ host: "node2", port: 6379 },
]);
const store = new SecureStore({
uid: "myApp",
secret: SecretValidator.generate(),
redis: { client: cluster },
});
await store.connect();Multiple SecureStore instances can share a single Redis connection:
const redis = new Redis();
const sessionsStore = new SecureStore({
uid: "sessions",
secret: sessionSecret,
redis: { client: redis },
});
const cacheStore = new SecureStore({
uid: "cache",
secret: cacheSecret,
redis: { client: redis },
});
await sessionsStore.connect();
await cacheStore.connect();
// Both stores use the same connection
// Disconnecting either store does NOT close the Redis clientImportant notes:
- When using an external client,
disconnect()will NOT close the Redis connection - you are responsible for callingredis.quit()when done - The client can be in any connectable state (ready, connecting, or lazyConnect);
connect()will wait for it to be ready - If the client is already closed,
connect()will throw aConnectionError
Connect to Redis. Must be called before other operations.
Encrypt and store data.
Retrieve and decrypt data. Returns null if not found or decryption fails.
Delete data. Returns count of deleted keys.
Close Redis connection. Optionally pass a specific Redis client to disconnect.
Create a typed namespace for organizing data. See Namespaces for details.
client: RedisClient | undefined- The underlying ioredis client (Redis or Cluster)isConnected: boolean- Connection status
Namespaces allow you to organize data with type-safe operations. Each namespace acts as an isolated partition within the same store.
const store = new SecureStore({
uid: "myApp",
secret: SecretValidator.generate(),
redis: { url: "redis://localhost:6379" },
});
await store.connect();
// Create a typed namespace
interface UserSchema {
profile: { name: string; age: number };
settings: { theme: string; notifications: boolean };
}
const users = store.namespace<UserSchema>("users");
// Type-safe operations
await users.save("profile", { name: "John", age: 30 });
await users.save("settings", { theme: "dark", notifications: true });
const profile = await users.get("profile"); // { name: string; age: number } | null
const settings = await users.get("settings");
await users.delete("profile");Each namespace provides the same core operations as the store:
get<K>(key: K): Promise<T[K] | null>save<K>(key: K, data: T[K]): Promise<void>delete<K>(key: K): Promise<number>
Data in different namespaces is completely isolated:
const users = store.namespace("users");
const sessions = store.namespace("sessions");
await users.save("data", "user-data");
await sessions.save("data", "session-data");
await users.get("data"); // "user-data"
await sessions.get("data"); // "session-data"
await store.get("data"); // null (root store is separate)Utility class for generating and validating encryption secrets.
Generate a cryptographically secure secret. Defaults to 32 characters if no length specified.
Validate secret strength against security requirements.
Secrets must:
- Be exactly 32 characters
- Have sufficient entropy (Shannon entropy ≥ 4.0)
- Not contain weak patterns (repeated chars, sequential numbers, etc.)
- Contain at least 3 of: uppercase, lowercase, numbers, special characters
All errors extend SecureStoreError and include a code property for programmatic handling.
import {
SecureStoreError,
ConnectionError,
EncryptionError,
ValidationError,
} from "secure-store-redis";
try {
await store.connect();
} catch (err) {
if (err instanceof ConnectionError) {
console.error(`Connection failed (${err.code}):`, err.message);
// err.code === "CONNECTION_ERROR"
}
}| Error Class | Code | When Thrown |
|---|---|---|
ConnectionError |
CONNECTION_ERROR |
Redis connection failures |
EncryptionError |
ENCRYPTION_ERROR |
Encryption/decryption failures |
ValidationError |
VALIDATION_ERROR |
Invalid configuration, keys, or data |
- Store secrets in environment variables, not code
- Use
SecretValidator.generate()for cryptographically secure secrets - Enable Redis authentication and TLS in production
- Use unique
uidvalues per application/environment to prevent data collisions - Rotate secrets periodically (requires re-encryption of existing data)
| Change | Migration |
|---|---|
uid required |
Add explicit uid to constructor |
secret required |
Use SecretValidator.generate() or provide your own |
connect() required |
Call await store.connect() before use |
| AES-256-GCM | Re-encrypt existing data (format changed) |
| Secret validation | Use strong secrets or set allowWeakSecrets: true |
// v3.x
const store = new SecureStore({ redis: { url: "..." } });
await store.init();
// v4.0
const store = new SecureStore({
uid: "myApp",
secret: SecretValidator.generate(),
redis: { url: "..." },
});
await store.connect();Full TypeScript support with exported types:
import SecureStore, {
SecureStoreConfig,
TypedNamespace,
RedisClient,
SecretValidator,
SecureStoreError,
ConnectionError,
EncryptionError,
ValidationError,
} from "secure-store-redis";MIT