mastra-ai/mastra · error

Invalid encrypted session data

Error message

Invalid encrypted session data

What it means

Google SSO session data is encrypted as base64(AES-GCM(payload)) with a leading salt (SALT_LENGTH bytes) and IV (IV_LENGTH bytes). decryptSession base64-decodes the blob and throws if the decoded buffer is shorter than salt+IV+1 bytes, because no valid ciphertext could exist — the input is truncated or not actually an encrypted session produced by this library.

Source

Thrown at auth/google/src/auth-provider.ts:130

}

async function encryptSession(data: unknown, password: string): Promise<string> {
  const encoder = new TextEncoder();
  const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));
  const key = await deriveKey(password, salt, 'encrypt');
  const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
  const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoder.encode(JSON.stringify(data)));
  const combined = new Uint8Array(salt.length + iv.length + new Uint8Array(encrypted).length);
  combined.set(salt);
  combined.set(iv, salt.length);
  combined.set(new Uint8Array(encrypted), salt.length + iv.length);
  return btoa(String.fromCharCode(...combined));
}

async function decryptSession(encrypted: string, password: string): Promise<unknown> {
  const combined = Uint8Array.from(atob(encrypted), c => c.charCodeAt(0));
  if (combined.length < SALT_LENGTH + IV_LENGTH + 1) {
    throw new Error('Invalid encrypted session data');
  }
  const salt = combined.slice(0, SALT_LENGTH);
  const iv = combined.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
  const data = combined.slice(SALT_LENGTH + IV_LENGTH);
  const key = await deriveKey(password, salt, 'decrypt');
  const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, data);
  return JSON.parse(new TextDecoder().decode(decrypted));
}

async function hmacSign(data: string, secret: string): Promise<string> {
  const encoder = new TextEncoder();
  const cryptoKey = await crypto.subtle.importKey(
    'raw',
    encoder.encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign'],
  );

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Delete/re-issue the session: redirect the user to re-authenticate so fresh session data is encrypted and stored.
  2. Verify the stored value is the exact output of encryptSession (base64, salt+IV prefixed), not plaintext or a JWT.
  3. Check cookie size limits; if the blob is near 4KB, store session server-side and keep only a small reference in the cookie.
  4. Confirm the same cookiePassword/encryption format is used across versions and instances (mismatched formats produce garbage or short blobs).

Example fix

// before
const session = await decryptSession(cookies.get('session') ?? '', password);
// after
const raw = cookies.get('session');
if (!raw || raw.length < 44) { // ~min salt+IV+ciphertext base64
  return redirectToLogin(); // no/invalid session, re-auth instead of throwing
}
const session = await decryptSession(raw, password);
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeEncryptedSession(v: unknown): v is string {
  if (typeof v !== 'string' || v.length === 0) return false;
  try {
    const bytes = Uint8Array.from(atob(v), c => c.charCodeAt(0));
    return bytes.length >= 16 + 12 + 1; // SALT_LENGTH + IV_LENGTH + ciphertext
  } catch {
    return false;
  }
}
if (!looksLikeEncryptedSession(cookieValue)) return reauthenticate();

Type guard

function isEncryptedSession(v: unknown): v is string {
  return typeof v === 'string' && v.length >= 40 && /^[A-Za-z0-9+/]+=*$/.test(v);
}

Prevention

When it happens

Trigger: Calling decryptSession (or accessing the `sessionData` getter backed by it) with a string that isn't base64 of a salted+IV-prefixed AES-GCM blob — e.g. an empty string, a plaintext token, a JWT, or a value truncated by storage limits.

Common situations: Cookie truncated by the browser's ~4KB limit or by a CDN/proxy; storing plaintext session JSON instead of the encrypted form; decoding the base64 twice or passing a base64url variant with padding stripped; old sessions encrypted with a different format after a library upgrade.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/9a3901fa884548fb. Report an issue: GitHub.