fixes / launch-ready

How I Would Fix exposed API keys and missing auth in a Cursor-built Next.js internal admin app Using Launch Ready.

The symptom is usually obvious: someone finds an admin page, the browser source exposes an API key, and there is no real login gate in front of sensitive...

How I Would Fix exposed API keys and missing auth in a Cursor-built Next.js internal admin app Using Launch Ready

The symptom is usually obvious: someone finds an admin page, the browser source exposes an API key, and there is no real login gate in front of sensitive actions. In business terms, that means anyone with the URL can view customer data, trigger writes, burn through third-party API spend, or break production from a laptop on public Wi-Fi.

The most likely root cause is that the app was built fast in Cursor with secrets placed in client-side code or checked into the repo, and auth was skipped to keep momentum. The first thing I would inspect is the deployed app and the repo history together: `.env` usage, `NEXT_PUBLIC_` variables, server action boundaries, middleware, and whether any admin routes are actually protected on the server instead of just hidden in the UI.

Triage in the First Hour

1. Confirm exposure scope.

  • Open the live app in an incognito browser.
  • View page source and DevTools Network tab.
  • Check whether keys appear in HTML, JS bundles, inline config, or network requests.

2. Check whether secrets are already public.

  • Search recent commits for API keys, tokens, service account JSON, webhook secrets, and private URLs.
  • Review GitHub secret scanning alerts if enabled.
  • Check whether `.env`, `.env.local`, or backup files were committed.

3. Verify auth behavior at the edge and server.

  • Hit admin routes directly without logging in.
  • Test POST, PATCH, DELETE endpoints manually.
  • Confirm whether middleware blocks access before rendering.

4. Inspect deployment environment variables.

  • Compare local `.env.local` to Vercel, Cloudflare Pages, Railway, or other host settings.
  • Verify which values are public versus server-only.
  • Look for any secret accidentally prefixed with `NEXT_PUBLIC_`.

5. Review logs and third-party dashboards.

  • Check Vercel function logs or hosting logs for unauthorized requests.
  • Inspect provider dashboards for unusual usage spikes.
  • Look for failed auth attempts or high-volume API calls.

6. Freeze risky changes.

  • Pause new deploys until access control is fixed.
  • Rotate any exposed keys if there is even a small chance they were leaked.

7. Capture evidence before editing code.

  • Save screenshots of exposed values and open endpoints.
  • Note affected routes and environment names.
  • This makes rollback and postmortem much easier.

A fast diagnostic command I would run locally:

grep -RIn --exclude-dir=node_modules --exclude-dir=.next \
  -E "NEXT_PUBLIC_|sk_live|sk_test|api[_-]?key|secret|token|bearer" .

Root Causes

1. Secret placed in client-side code.

  • Common pattern: `NEXT_PUBLIC_API_KEY` used for something that should only exist on the server.
  • Confirm by searching the repo and built bundle for the value or variable name.

2. Missing server-side authorization checks.

  • The UI hides buttons, but API routes still accept requests from anyone who knows the endpoint.
  • Confirm by calling protected endpoints directly without a session or role claim.

3. Auth implemented only in frontend state.

  • The app checks `localStorage` or React state to decide if someone is "logged in."
  • Confirm by refreshing pages or bypassing UI navigation and still reaching admin data.

4. Middleware not covering all sensitive routes.

  • Some paths are protected while nested routes, API routes, or server actions are missed.
  • Confirm by listing every route under `/admin`, `/api`, and any dynamic segments.

5. Environment variables misclassified during deployment.

  • A secret intended for server use was added as a public env var in the host dashboard.
  • Confirm by comparing deployment settings with what appears in browser bundles.

6. Overbroad service credentials.

  • One key has access to read and write everything across prod systems when it should be scoped narrowly.
  • Confirm by reviewing provider permissions and what that key can actually do.

The Fix Plan

I would fix this in layers so we stop exposure first, then repair access control properly.

1. Rotate anything exposed immediately.

  • Assume leaked keys are compromised if they were visible in browser code or public repos.
  • Revoke old keys before shipping the fix if they can write data or call paid APIs.

2. Move secrets to server-only storage.

  • Remove sensitive values from client components entirely.
  • Use plain environment variables on the server only, never `NEXT_PUBLIC_` for secrets.

3. Put auth at the server boundary.

  • Protect admin pages with middleware plus server-side checks inside route handlers or server actions.
  • Do not rely on hidden links or frontend redirects as security controls.

4. Add role-based authorization for admin actions.

  • A logged-in user should not automatically be an admin user.
  • Check role claims on every destructive endpoint like create, update, delete, export, or invite.

5. Lock down route access patterns.

  • Protect `/admin`, `/api/admin/*`, server actions used by admin UI, and any data export endpoints.
  • If a route returns sensitive data, it needs auth even if it is "internal."

6. Reduce blast radius of credentials.

  • Replace one wide key with separate scoped keys per integration where possible.
  • Give each key only the permissions needed for that specific job.

7. Add secure defaults to deployment settings: | Area | Fix | |---|---| | DNS | Verify domain ownership and remove unused records | | SSL | Force HTTPS everywhere | | Redirects | Redirect apex to canonical domain | | CORS | Allow only known origins | | Caching | Avoid caching authenticated responses | | Monitoring | Alert on 401 spikes and unusual API usage |

8. Clean up build output risk. ```ts // Good: secret stays on server const apiKey = process.env.INTERNAL_API_KEY;

// Bad: this ends up in browser bundles const apiKey = process.env.NEXT_PUBLIC_INTERNAL_API_KEY; ```

9. Add audit logging for sensitive actions. This should log who did what, when they did it, and from which IP or session context where appropriate. Keep logs free of raw secrets and full customer payloads.

10. Ship behind a controlled redeploy window. I would deploy after rotation is done and confirm old credentials no longer work. That avoids a half-fixed state where both old and new access paths remain open.

Regression Tests Before Redeploy

I would not ship until these checks pass:

  • Anonymous users cannot reach any admin page directly.
  • Anonymous users cannot call any protected API route successfully.
  • Logged-in non-admin users cannot perform admin writes or exports.
  • Server-rendered pages do not expose secret values in HTML source or JS bundles.
  • No secret appears in build artifacts, logs, screenshots, error pages, or analytics events.

Acceptance criteria I would use:

  • 0 exposed secrets in browser source after build review
  • 100 percent of admin routes require auth at the server boundary
  • 100 percent of destructive actions enforce role checks
  • 0 unauthorized successful requests during manual testing
  • p95 response time stays under 300 ms for auth checks on normal traffic
  • No increase in 5xx errors after deployment

Test plan: 1. Try direct URL access as anonymous user on desktop and mobile viewport sizes. 2. Test refresh behavior after login session expires mid-flow. 3. Submit invalid payloads to ensure validation still blocks bad input after auth changes are added laterally into handlers not UI only flows now 4. Verify redirects do not create loops between login page and protected pages because those become support tickets fast 5. Run a quick smoke test on every role level you support: owner, admin, editor if applicable

Prevention

I would put guardrails around this so it does not come back two weeks later after another Cursor sprint.

  • Security review checklist before merge:
  • No secrets in client components
  • No public env vars containing private data
  • Auth enforced at route handler level
  • Role checks on all write endpoints
  • Logging excludes tokens and raw PII
  • Code review rules:

The reviewer must check behavior first: who can access what, which data leaves the server, and what happens when auth fails. Style-only review misses the kind of bug that gets you breached.

  • CI gates:

Run secret scanning, typecheck, linting, unit tests around auth helpers, and a basic route-access test suite on every pull request. Block deploys if any secret pattern is detected.

  • Monitoring:

Alert on unusual spikes in 401s/403s, sudden increases in outbound API calls, login failures from one IP range, and unexpected changes to admin traffic volume.

  • UX guardrails:

Make permission states obvious so founders do not ask teams to "just hide it" instead of securing it properly. Show clear empty states for denied access rather than broken screens that encourage workarounds.

  • Performance guardrails:

Keep auth lightweight so security does not make the app feel broken; aim for sub-300 ms auth middleware checks and avoid loading heavy third-party scripts on admin pages unless absolutely necessary.

When to Use Launch Ready

Use Launch Ready when you need me to cleanly close this kind of security gap without turning your app into a bigger rebuild project.

This sprint fits best when you already have a working Cursor-built Next.js internal app but need it made safe enough to expose to staff or clients without embarrassing failures like leaked keys or open admin routes. What I need from you before kickoff is simple: repo access with deploy permissions removed from everyone except me during the sprint if possible; list of current env vars; hosting provider access; third-party service accounts involved; screenshots of broken flows; and one person who can confirm business rules like who should be able to see what.

If you want me to scope it quickly before we touch code again: https://cal.com/cyprian-aarons/discovery

Delivery Map

References

  • https://roadmap.sh/cyber-security
  • https://roadmap.sh/api-security-best-practices
  • https://roadmap.sh/code-review-best-practices
  • https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
  • https://vercel.com/docs/environment-variables

---

Take the next step

If this is a problem in your product right now, here is what to do next:

  • [Use the free Cyprian tools](/tools) - estimate cost, score app risk, check launch readiness, or pick the right service sprint.
  • [Book a discovery call](/contact) - I will tell you honestly whether you need a sprint or if you can DIY the next step.

*Written by Cyprian Tinashe Aarons - senior full-stack and AI engineer helping founders rescue, launch, automate, and scale AI-built products.*

Next steps
About the author

Cyprian Tinashe AaronsSenior Full Stack & AI Engineer

Cyprian helps founders rescue, secure, deploy, and automate AI-built apps with production-grade engineering, launch systems, and AI integration.