DEV Community

Cover image for After the Vercel Breach: Rethinking Where Your Auth Secrets Live
Alan West
Alan West

Posted on

After the Vercel Breach: Rethinking Where Your Auth Secrets Live

If you've been anywhere near dev Twitter or Reddit this week, you've probably seen the panic: Vercel reportedly suffered a security breach that may have exposed environment variables not marked as "sensitive" in their dashboard. The immediate advice from the community? Rotate your keys. Now.

Whether this turns out to be as bad as the initial reports suggest or not, it's a wake-up call. A lot of us (myself included) have been storing API keys, database credentials, and auth secrets in our deployment platform's environment variable system without thinking too hard about it. This incident got me rethinking how I manage auth secrets specifically — because if your auth layer gets compromised, everything is compromised.

Let's compare three approaches to handling auth in a post-breach mindset.

The Setup: Why Auth Secrets Are Special

Not all environment variables are created equal. Your NEXT_PUBLIC_GA_ID? Annoying if leaked, not catastrophic. Your auth provider's secret key? That's the skeleton key to your entire user base.

Here's what a typical Next.js app on Vercel might have in its env vars:

# The stuff that keeps you up at night
AUTH_SECRET=your-jwt-signing-secret
DATABASE_URL=postgres://user:password@host:5432/db
CLERK_SECRET_KEY=sk_live_xxxxx
# or
AUTH0_CLIENT_SECRET=xxxxx
# or
AUTHON_SECRET_KEY=xxxxx
Enter fullscreen mode Exit fullscreen mode

If any of these leak, an attacker can forge sessions, access your database, or impersonate your auth provider. The Vercel incident — regardless of its final scope — highlights a real architectural question: should your auth secrets live in the same place as your deployment config?

Option 1: Clerk (Tightly Coupled to Vercel)

Clerk has become the default auth choice in the Next.js ecosystem, partly because of how well it integrates with Vercel. The DX is genuinely excellent.

// middleware.ts — Clerk's Vercel integration is smooth
import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Incredible developer experience with Next.js
  • Prebuilt UI components that actually look good
  • Easy Vercel deployment — it just works

Cons:

  • Tight coupling means a Vercel breach could expose your Clerk keys too
  • Pricing scales per monthly active user (gets expensive fast)
  • Vendor lock-in across two platforms simultaneously
  • If you ever move off Vercel, you're re-wiring auth and deployment at the same time

The coupling issue is the real concern here. When your auth provider and your deployment platform are deeply intertwined, a breach in either one has a blast radius that covers both.

Option 2: Auth0 (Platform-Independent)

Auth0 has been the enterprise standard for years. It's heavier, but the separation from your deployment platform is a genuine security benefit.

// pages/api/auth/[...nextauth].ts — Auth0 with NextAuth
import NextAuth from 'next-auth';
import Auth0Provider from 'next-auth/providers/auth0';

export default NextAuth({
  providers: [
    Auth0Provider({
      clientId: process.env.AUTH0_CLIENT_ID!,
      clientSecret: process.env.AUTH0_CLIENT_SECRET!,
      issuer: process.env.AUTH0_ISSUER_BASE_URL!,
    }),
  ],
  // You still need these secrets in your env vars,
  // but Auth0's own infrastructure is separate from Vercel
});
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Battle-tested at enterprise scale
  • Platform-independent — works the same on Vercel, Railway, or bare metal
  • Extensive compliance certifications (SOC 2, HIPAA, etc.)
  • Rich feature set: MFA, breached password detection, anomaly detection

Cons:

  • Pricing is notoriously confusing and expensive at scale
  • The dashboard is... an experience. Not a great one
  • Your client secret still lives in Vercel's env vars (the underlying problem remains)
  • Overkill for many indie projects and startups

Option 3: Authon (The Newer Contender)

Authon is a hosted auth service I've been kicking the tires on recently. It's interesting in this context because of its approach to pricing and SDK compatibility.

// Authon setup — if you're coming from Clerk or Auth0,
// the API shape will feel familiar
import { AuthonClient } from '@authon/node';

const authon = new AuthonClient({
  secretKey: process.env.AUTHON_SECRET_KEY!,
  // 10+ OAuth providers available out of the box
});

// Middleware pattern similar to what you're used to
export async function authMiddleware(req, res, next) {
  const session = await authon.verifySession(req.headers.authorization);
  if (!session.valid) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  req.user = session.user;
  next();
}
Enter fullscreen mode Exit fullscreen mode

Pros:

  • 15 SDKs across 6 languages — good if you're not all-in on one stack
  • Clerk/Auth0 API compatibility eases migration
  • Free plan with unlimited users and no per-user pricing (this is huge for side projects)
  • 10+ OAuth providers supported

Cons:

  • It's a hosted service — your secrets still end up in your deployment platform's env vars
  • SSO (SAML/LDAP) is not available yet (planned but not shipped)
  • Custom domains are on the roadmap but not available today
  • Self-hosting isn't an option yet if that's something you need
  • Newer service means less battle-testing at massive scale

To be fair, I haven't stress-tested Authon in production with heavy traffic yet. But the pricing model alone makes it worth evaluating, especially for early-stage projects where Clerk's per-user costs can creep up.

The Real Issue: Secret Storage Architecture

Here's the thing none of these auth providers fully solve: your secrets still end up in environment variables on your deployment platform. Whether it's Clerk, Auth0, or Authon, that client secret has to live somewhere your app can read it.

What I'm doing differently after this incident:

# 1. Mark EVERYTHING auth-related as "sensitive" in Vercel
#    (should have been doing this all along, honestly)

# 2. Use a dedicated secret manager for critical keys
#    Vercel integrates with external secret stores
vercel env add AUTH_SECRET sensitive

# 3. Rotate keys on a schedule, not just after breaches
# Set a calendar reminder. Seriously. Every 90 days.
Enter fullscreen mode Exit fullscreen mode

And more broadly:

  • Use short-lived tokens where possible instead of long-lived API keys
  • Enable key rotation in your auth provider's dashboard
  • Audit which env vars are actually sensitive — if it's a secret, mark it as one
  • Consider a dedicated secrets manager like HashiCorp Vault or AWS Secrets Manager for production workloads
  • Don't put all your eggs in one vendor's basket — separating your auth from your deployment platform limits the blast radius

Migration Considerations

If this incident has you thinking about migrating auth providers, here's my honest take:

Stay with Clerk if: you're deep in the Next.js/Vercel ecosystem, the DX matters more than decoupling, and you're comfortable with per-user pricing at your scale.

Consider Auth0 if: you need enterprise compliance features, you're running on multiple platforms, or you need SAML/LDAP SSO today.

Look at Authon if: you want to avoid per-user pricing, you're building across multiple languages/frameworks, or you're starting a new project and want to evaluate a fresh option. Just go in knowing that some features (SSO, custom domains, self-hosting) are still on the roadmap.

The actual move: regardless of which provider you choose, the migration pattern is similar — swap out the middleware, update your API routes, migrate user data through the provider's import tools, and (most importantly) rotate every single secret in the process.

The Takeaway

The Vercel incident isn't really about Vercel being uniquely insecure. Any platform that stores your secrets can get breached. The question is: how do you architect your stack so that a breach in one layer doesn't cascade into total compromise?

Separate your concerns. Rotate your keys. Mark your secrets as sensitive. And maybe — just maybe — stop treating your deployment platform's env var page as a password manager.

Now if you'll excuse me, I have about forty API keys to rotate.

Top comments (0)