How the Impersonation Cookie Flows Between Edge Middleware and Database-Backed Support Sessions in DeskcommCRM

The impersonation cookie flow uses a signed HttpOnly cookie that is created server-side, verified in the Edge middleware, and then read by database helpers to enforce tenant isolation without modifying the underlying Supabase session.

DeskcommCRM implements a secure platform-admin impersonation feature that allows administrators to act on behalf of tenants without altering the original authentication state. This article breaks down the complete impersonation cookie flow between the Next.js Edge middleware and the database-backed support session, referencing the actual implementation in the melgarafael/DeskcommCRM repository.

When a platform admin initiates impersonation via the POST /api/v1/admin/tenants/[id]/impersonate route, the server generates a cryptographically signed token containing the tenant ID, admin ID, and expiry timestamp. The signImpersonateCookie function in lib/impersonate/cookie.ts constructs this token using HMAC-SHA-256.

The payload format follows a strict structure: <base64url(payload)>.<base64url(hmac)>. The function retrieves the IMPERSONATE_COOKIE_SECRET from environment variables, validates it meets the minimum 32-character requirement, and produces a signature that prevents tampering.

// lib/impersonate/cookie.ts – signImpersonateCookie
export function signImpersonateCookie(payload: ImpersonatePayload): string {
  const secret = env.IMPERSONATE_COOKIE_SECRET;
  if (!isSecretConfigured(secret)) {
    throw new Error("IMPERSONATE_COOKIE_SECRET missing or <32 chars");
  }
  const json = JSON.stringify(payload);
  const payloadB64 = b64urlEncode(Buffer.from(json, "utf8"));
  const sigB64 = b64urlEncode(hmac(payloadB64, secret));
  return `${payloadB64}.${sigB64}`;
}

The resulting token is stored in an HttpOnly, Secure, SameSite-Lax cookie named deskcomm-impersonate. This configuration prevents JavaScript access while ensuring the cookie only transmits over HTTPS and restricts cross-site request risks.

Edge Middleware Verification

Every request matching /app/* passes through the Next.js Edge middleware defined in proxy.ts. This middleware extracts the deskcomm-impersonate cookie and validates it using the verifyImpersonateCookieEdge function from lib/impersonate/cookie-edge.ts, which implements Web Crypto API equivalents for the Edge runtime.

The verification process uses a manual constantTimeEqual comparison to prevent timing attacks, matching the security posture of the server-side implementation. If validation succeeds, the middleware injects impersonation metadata directly into the request context, making the override visible to downstream handlers without touching the Supabase session.

// proxy.ts – part of the middleware chain
import { verifyImpersonateCookieEdge } from "@/lib/impersonate/cookie-edge";

export async function middleware(request) {
  const token = request.cookies.get(IMPERSONATE_COOKIE_NAME_EDGE);
  const result = await verifyImpersonateCookieEdge(token?.value ?? "", env.IMPERSONATE_COOKIE_SECRET);
  if (!result.valid) {
    // clear invalid cookie, continue as normal user
    request.cookies.delete(IMPERSONATE_COOKIE_NAME_EDGE);
  } else {
    // inject impersonation data for downstream handlers
    request.context.impersonating = true;
    request.context.tenantIdOverride = result.payload!.tenantId;
    request.context.platformAdminId = result.payload!.platformAdminId;
  }
  return NextResponse.next();
}

This Edge-runtime verification ensures that impersonation context is available immediately at the request boundary, allowing subsequent server components and API routes to access the override without re-parsing the cookie manually.

Database Session Resolution with Support Context

Server-side code that requires tenant isolation or audit logging consumes the impersonation state through the readSupportContext helper in lib/impersonate/support.ts. This function reads the cookie from request headers, runs the classic verifyImpersonateCookie verification, and returns a SupportContext object containing the tenantId and platformAdminId.

Database queries then use this context to enforce Row-Level Security (RLS) policies and override organization_id parameters. All audit logs generated during impersonation include acting_as_platform_admin = true, creating a complete forensic trail of administrative actions.

// lib/impersonate/support.ts (simplified)
export async function readSupportContext(req: NextRequest): Promise<SupportContext | null> {
  const token = req.cookies.get(IMPERSONATE_COOKIE_NAME)?.value;
  if (!token) return null;
  const { valid, payload } = verifyImpersonateCookie(token);
  return valid ? { tenantId: payload!.tenantId, platformAdminId: payload!.platformAdminId } : null;
}

// Example usage in an API handler
export async function POST(req) {
  const support = await readSupportContext(req);
  if (support?.tenantId) {
    // override tenant for DB query
    const rows = await supabase.from("orders")
      .select("*")
      .eq("organization_id", support.tenantId);
    // audit as platform admin
    await auditLog({ action: "order.list", acting_as_platform_admin: true });
    return ok(rows);
  }
  // normal handling…
}

The support session remains additive—it supplements rather than replaces the existing Supabase authentication—ensuring that the platform admin retains their original identity while operating within the tenant's data scope.

Security Implementation Details

The impersonation cookie flow implements several critical security controls to prevent privilege escalation and session hijacking:

  • Constant-time comparison: Both server (timingSafeEqual) and Edge (constantTimeEqual) verifications use constant-time string comparison to prevent timing side-channel attacks.
  • Ephemeral tokens: Expiry validation occurs after signature verification; correctly signed but expired cookies are rejected with an explicit "expired" reason.
  • Secrets management: The IMPERSONATE_COOKIE_SECRET must be at least 32 characters; the application throws if this requirement is not met.
  • Cookie attributes: HttpOnly prevents XSS theft, Secure enforces HTTPS transmission, and SameSite-Lax mitigates CSRF vectors.

Summary

  • signImpersonateCookie in lib/impersonate/cookie.ts creates HMAC-SHA-256 signed tokens combining a JSON payload with a base64url signature.
  • The deskcomm-impersonate cookie uses HttpOnly, Secure, and SameSite-Lax attributes to protect the token in transit and at rest.
  • verifyImpersonateCookieEdge in lib/impersonate/cookie-edge.ts validates cookies in the Next.js Edge runtime using Web Crypto API primitives.
  • proxy.ts middleware injects impersonation context into request.context, making tenant overrides available to downstream handlers.
  • readSupportContext in lib/impersonate/support.ts provides server-side code with validated SupportContext for database queries and audit logging.
  • The flow maintains additive authorization, leaving the underlying Supabase session untouched while enforcing tenant isolation through explicit context overrides.

Frequently Asked Questions

How does the Edge middleware pass impersonation data to API routes?

The Edge middleware in proxy.ts attaches impersonation metadata directly to the request.context object, setting impersonating to true, tenantIdOverride to the payload's tenant ID, and platformAdminId to the admin identifier. Downstream API routes and server components read this context without needing to re-verify the cookie signature, though they may call readSupportContext to explicitly fetch the SupportContext object for database operations.

The verification logic in both verifyImpersonateCookieEdge and verifyImpersonateCookie checks expiry after validating the HMAC signature. An expired but correctly signed cookie returns a validation result with valid: false and reason "expired", causing the middleware to clear the invalid cookie via request.cookies.delete(). The user continues under their original authentication context without the tenant override, preventing access to stale impersonation sessions.

Why does DeskcommCRM use separate verification functions for Edge and Node.js runtimes?

The Edge runtime (cookie-edge.ts) uses the Web Crypto API (crypto.subtle), while the Node.js server implementation (cookie.ts) uses Node's native crypto module and Buffer utilities for base64url encoding. Both implementations share identical payload formats and HMAC-SHA-256 logic, but the separation ensures compatibility with Next.js Edge Middleware constraints while maintaining performance in server-side Node.js environments.

How does the system prevent platform admins from impersonating arbitrary tenants without authorization?

While the cookie mechanism itself only validates cryptographic integrity and expiry, authorization checks occur at the route level before signImpersonateCookie is ever called. The POST /api/v1/admin/tenants/[id]/impersonate endpoint validates that the requesting user has platform-admin privileges and that the target tenant ID exists in the admin's authorized scope before issuing the signed cookie. Additionally, RLS policies enforce that queries using the tenantId from SupportContext can only access rows where organization_id matches, preventing cross-tenant data leakage even if a cookie were somehow forged.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →