Skip to main content

Overview

The RedisAuth class provides persistent authentication storage using Redis. It enables distributed bot deployments by storing encrypted session data in a centralized Redis server, allowing multiple bot instances to share or migrate sessions seamlessly.

Key Features

  • Distributed Storage: Share sessions across multiple servers
  • Encrypted Storage: All session data encrypted with AES-256-GCM
  • High Availability: Leverage Redis replication and clustering
  • Fast Access: In-memory storage for sub-millisecond latency
  • Session Migration: Move sessions between servers effortlessly
  • Automatic Expiry: Optional TTL support for session management
RedisAuth is ideal for production environments with multiple servers, load balancing, or high-availability requirements.

Installation

RedisAuth requires the ioredis package:
npm install ioredis

Constructor

new RedisAuth(uuid, redis, prefix)

Creates a new RedisAuth instance for managing bot authentication in Redis.
uuid
UUID
required
A valid UUID (v4) that uniquely identifies this bot instance. This UUID is used as the encryption key and as part of the Redis key namespace.
redis
Redis
required
An ioredis client instance. This can be a standalone Redis connection, a cluster, or a sentinel configuration.
prefix
string
required
A string prefix for Redis keys. This helps organize keys and avoid collisions. The final key format is {prefix}:{uuid}:{hash}.

Example

import { Bot, RedisAuth } from "wapi";
import { Redis } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Redis({
  host: "localhost",
  port: 6379,
});

const uuid = randomUUID();
const auth = new RedisAuth(uuid, redis, "wapi");

const bot = new Bot({ auth });

Throws

  • Error: If the provided UUID is not a valid UUID v4 format

Properties

uuid

uuid
UUID
The UUID associated with this authentication instance. This is the same UUID provided to the constructor.
const auth = new RedisAuth(uuid, redis, "wapi");
console.log(auth.uuid); // "550e8400-e29b-41d4-a716-446655440000"

Methods

init()

Initializes the authentication system by loading or creating credentials and setting up the key store.
init(): Promise<IBotAuthInit>

Behavior

  1. Credential Loading: Attempts to load existing credentials from Redis
  2. New Session: If no credentials exist, generates new authentication credentials
  3. Key Store Setup: Initializes the Signal protocol key store for WhatsApp encryption
  4. Cache Population: Populates the in-memory cache with encrypted data

Returns

Example

import { RedisAuth } from "wapi";
import { Redis } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Redis();
const auth = new RedisAuth(randomUUID(), redis, "wapi");

// Initialize authentication
const { creds, keys } = await auth.init();

console.log("Credentials loaded:", !!creds);
console.log("Keys initialized:", !!keys);
This method is called automatically by the Bot class during initialization. You rarely need to call it directly.

save()

Persists the current authentication credentials to Redis.
save(): Promise<void>

Behavior

  1. Validation: Ensures credentials have been loaded via init()
  2. Serialization: Converts credentials to JSON format
  3. Encryption: Encrypts the data using AES-256-GCM
  4. Persistence: Writes the encrypted data to Redis
  5. Cache Update: Updates the in-memory cache

Throws

  • Error: If credentials haven’t been initialized (“Credentials not loaded.”)

Example

import { RedisAuth } from "wapi";
import { Redis } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Redis();
const auth = new RedisAuth(randomUUID(), redis, "wapi");

// Initialize and save
await auth.init();
await auth.save();

console.log("Session saved to Redis");
The Bot class automatically saves credentials when authentication state changes. Manual calls to save() are typically unnecessary.

remove()

Deletes all authentication data from Redis and clears the cache.
remove(): Promise<void>

Behavior

  1. Key Discovery: Finds all Redis keys matching the session prefix
  2. Batch Deletion: Deletes all matching keys in a single operation
  3. Cache Clearing: Clears the in-memory cache

Example

import { RedisAuth } from "wapi";
import { Redis } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Redis();
const uuid = randomUUID();
const auth = new RedisAuth(uuid, redis, "wapi");

// Remove session data
await auth.remove();
console.log("Session removed from Redis");
This operation is irreversible. After calling remove(), the bot will need to re-authenticate (scan QR code or enter OTP) on next startup.

Storage Details

Key Structure

Redis keys follow this pattern:
{prefix}:{uuid}:{md5_hash}
Example:
wapi:550e8400-e29b-41d4-a716-446655440000:5f4dcc3b5aa765d61d8327deb882cf99

Key Naming

  • Each data key (e.g., “creds”, “pre-key-1”) is MD5-hashed
  • The hash becomes the final segment of the Redis key
  • Example: credswapi:550e8400-...:5f4dcc3b5aa765d61d8327deb882cf99

Value Format

  • Values are stored as binary buffers
  • Retrieved using getBuffer() for efficiency
  • Encrypted with AES-256-GCM before storage

Complete Examples

import { Bot, RedisAuth } from "wapi";
import { Redis } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Redis({
  host: process.env.REDIS_HOST || "localhost",
  port: parseInt(process.env.REDIS_PORT || "6379"),
});

const uuid = randomUUID();
const auth = new RedisAuth(uuid, redis, "wapi");
const bot = new Bot({ auth });

bot.on("qr", (qr) => {
  console.log("Scan this QR code:", qr);
});

bot.on("open", (account) => {
  console.log("Connected as:", account.name);
});

bot.command("hello", (ctx) => {
  ctx.reply("Hello from RedisAuth!");
});

bot.launch();

Advanced Configuration

Redis Sentinel

For high availability, use Redis Sentinel:
import { Bot, RedisAuth } from "wapi";
import { Redis } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Redis({
  sentinels: [
    { host: "sentinel-1", port: 26379 },
    { host: "sentinel-2", port: 26379 },
    { host: "sentinel-3", port: 26379 },
  ],
  name: "mymaster",
});

const auth = new RedisAuth(
  randomUUID(),
  redis,
  "wapi"
);

const bot = new Bot({ auth });
bot.launch();

Redis Cluster

For horizontal scaling:
import { Bot, RedisAuth } from "wapi";
import { Cluster } from "ioredis";
import { randomUUID } from "node:crypto";

const redis = new Cluster([
  { host: "node1.redis.example.com", port: 7000 },
  { host: "node2.redis.example.com", port: 7001 },
  { host: "node3.redis.example.com", port: 7002 },
], {
  redisOptions: {
    password: process.env.REDIS_PASSWORD,
  },
});

const auth = new RedisAuth(
  randomUUID(),
  redis,
  "wapi"
);

const bot = new Bot({ auth });
bot.launch();

Advantages

✅ Horizontal scaling
✅ Load balancing support
✅ Multiple bot instances
✅ Session sharing
✅ Zero downtime deployments

Best Practices

Follow these recommendations for optimal performance and reliability:

1. Connection Management

// ✅ Good: Reuse Redis connections
const redis = new Redis();
const auth1 = new RedisAuth(uuid1, redis, "wapi");
const auth2 = new RedisAuth(uuid2, redis, "wapi");

// ❌ Bad: Create new connection per bot
const auth1 = new RedisAuth(uuid1, new Redis(), "wapi");
const auth2 = new RedisAuth(uuid2, new Redis(), "wapi");

2. Error Handling

const redis = new Redis();

redis.on("error", (err) => {
  console.error("Redis connection error:", err);
  // Implement retry logic or alerting
});

redis.on("close", () => {
  console.warn("Redis connection closed");
});

3. Prefix Naming

// ✅ Good: Descriptive, environment-aware prefixes
const prefix = `wapi:${process.env.NODE_ENV}:v1`;
const auth = new RedisAuth(uuid, redis, prefix);

// ❌ Bad: Generic prefix
const auth = new RedisAuth(uuid, redis, "bot");

4. Graceful Shutdown

process.on("SIGTERM", async () => {
  await bot.close();
  await redis.quit(); // Graceful disconnect
  process.exit(0);
});

Troubleshooting

Connection Issues

Performance Issues


When to Use

✅ Multi-server deployments
✅ Load-balanced environments
✅ High-availability requirements
✅ Session migration needs
✅ Existing Redis infrastructure
✅ Fast session access required

See Also

Build docs developers (and LLMs) love