Auth Libraries skill for Codex

Pick the right auth stack, then wire it safely. Codex compares self-hosted libraries, hosted providers and framework-native auth against your actual requirements, then wires the chosen one end to end: schema migrations, env vars, route handlers, client helpers and server-side session checks at every data access, not only in middleware. It also plans migrations off NextAuth or Lucia that keep social logins matching and rehash old passwords on next sign-in.

  • Backend & APIs
  • $auth-libraries
  • 174 lines
  • 2 files
  • ~1,950 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- auth-libraries

Installs to ~/.agents/skills/auth-libraries/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $auth-libraries, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Chooses and wires an authentication framework instead of hand-rolling one - Better Auth, Auth.js (NextAuth v5), hosted providers such as Clerk, WorkOS or Auth0, and framework-native options (Django allauth, Rails 8 auth generator, Laravel starter kits) - covering database adapters, OAuth providers, route protection in Next.js, server-side session checks and migration between libraries. Use when starting auth in a new app, comparing build vs buy, adding social login or orgs, or migrating off NextAuth or Lucia.

Use it when

  • A new app needs sign-up, sign-in and social login
  • Deciding between a self-hosted library and a hosted auth provider
  • Migrating off NextAuth, Lucia or a homegrown auth system
  • Routes are protected only in middleware and need server-side checks

Use something else when

  • The project is committed to Clerk and needs advanced Clerk features: use clerk-auth
  • You are building login by hand without a framework: use session-auth

How to check the result

Unauthenticated calls to protected API routes return 401 or redirect, cross-org resource requests fail, and sign-out invalidates the session on the server.

The mistake it prevents

Relying on Next.js middleware matchers as the only authorization layer, so any route the matcher misses is public.

~/.agents/skills/auth-libraries/SKILL.md

--- name: auth-libraries description: Chooses and wires an authentication framework instead of hand-rolling one - Better Auth, Auth.js (NextAuth v5), hosted providers such as Clerk,… license: MIT ---

Done well means: the choice matches the product’s real needs (B2B orgs, SSO, compliance, budget, data residency), authorization is checked on the server at every data access, not only in middleware, and the setup is reproducible from env vars and migrations.

Choose

Ask the user, or infer from the repo: framework, database, B2C or B2B, need for SAML/SCIM, MFA, passkeys, user count and budget, and whether user data may live with a third party.

NeedDefaultWhy
TypeScript app, own database, full controlBetter AuthSelf-hosted, DB-backed sessions, plugins for orgs, 2FA, passkeys, admin
Existing Next.js app already on NextAuth/Auth.jsStay on Auth.js v5, plan a move laterMaintained for security fixes; the maintainers recommend Better Auth for new projects
Fastest path to polished UI, MFA, orgs, low opsHosted: ClerkPrebuilt components, user management, per-MAU pricing
B2B with enterprise SSO (SAML) and SCIMHosted: WorkOS, Auth0, or Clerk’s enterprise featuresSAML and directory sync are expensive to build
Djangodjango-allauthMature, social and MFA built in
Rails 8bin/rails generate authentication, add OmniAuth for socialSession-based, code lives in your app
LaravelStarter kit (Fortify-based) + Socialite; Sanctum for API tokensFirst-party
Spring BootSpring Security with oauth2Login()Native

Lucia is no longer maintained as a library (it became a learning resource in 2025). Treat it as a migration source, not a choice. Hand-rolled auth is justified only when no option fits; then follow session-auth, password-storage and oauth2-oidc closely.

Before changing anything

  1. Read AGENTS.md for package manager, framework version and test commands.
  2. rg -n "next-auth|@auth/|better-auth|@clerk|lucia|passport|devise|allauth" package.json */package.json pyproject.toml Gemfile
  3. Check the Next.js major: Next.js 16 renamed middleware.ts to proxy.ts. Use whichever the project’s version expects.
  4. Installing packages needs network; in a sandbox without it, ask for approval.

Better Auth (Next.js App Router)

// lib/auth.ts
import { betterAuth } from "better-auth";
import { Pool } from "pg";

export const auth = betterAuth({
  database: new Pool({ connectionString: process.env.DATABASE_URL }),
  emailAndPassword: { enabled: true, requireEmailVerification: true },
  socialProviders: {
    github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET! },
  },
  // plugins: [organization(), twoFactor(), passkey()]  -- add only what the product needs
});
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient();
// Server component or route handler: the real authorization point
import { headers } from "next/headers";
const session = await auth.api.getSession({ headers: await headers() });
if (!session) redirect("/sign-in");

Env: BETTER_AUTH_SECRET (32+ random bytes) and BETTER_AUTH_URL. Generate or apply the schema with the Better Auth CLI (npx @better-auth/cli generate for ORM schema files, migrate for the built-in Kysely adapter), and commit the resulting migration. With Drizzle or Prisma, use their adapters and the project’s normal migration flow.

Auth.js v5 (existing projects)

// auth.ts
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [GitHub],               // reads AUTH_GITHUB_ID / AUTH_GITHUB_SECRET
  session: { strategy: "database" },
});
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;

Requires AUTH_SECRET. The default session strategy without an adapter is an encrypted JWT cookie; it cannot be revoked server-side. Use strategy: "database" when revocation matters.

Clerk (hosted)

// middleware.ts (proxy.ts on Next.js 16)
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isProtected = createRouteMatcher(["/dashboard(.*)", "/api/private(.*)"]);
export default clerkMiddleware(async (auth, req) => { if (isProtected(req)) await auth.protect(); });
export const config = { matcher: ["/((?!_next|.*\\..*).*)", "/(api|trpc)(.*)"] };
import { auth } from "@clerk/nextjs/server";
const { userId, orgId } = await auth();

Wrap the root layout in <ClerkProvider>. Keys: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY. Mirror users into your database through Clerk webhooks (verify the signature; see webhook-receivers) and key rows by Clerk user ID. For deeper Clerk work, use clerk-auth.

Authorization: the part libraries do not do for you

  • Middleware/proxy is a coarse gate for redirects. Re-check the session in every server action, route handler and data-access function; matchers miss routes and Next.js has had middleware bypass CVEs.
  • Put ownership and role checks next to the query: where: { id, orgId: session.orgId }.
  • Never trust a user ID or org ID sent from the client.

Migrating between libraries

  1. Inventory: user table shape, password hash algorithm, linked OAuth accounts, sessions.
  2. Map users and accounts into the new schema keeping provider (provider, providerAccountId) pairs, so social logins keep matching.
  3. Passwords: if the new library cannot verify the old hash format, verify with the old algorithm on next login and rehash (password-storage). Never ask users to reset en masse unless the old hashes were weak.
  4. Sessions: accept a forced re-login unless the user insists; dual-reading two session formats is rarely worth it.
  5. Rehearse on a copy of production data. Running the migration against production needs explicit user confirmation and a backup.

Workflow checklist

  • Gather requirements and choose with the table; state the trade-off to the user
  • Install, set env vars (document them in .env.example, never commit real secrets)
  • Generate and review the schema migration
  • Wire route handler, client, and server-side session helper
  • Protect routes coarsely, then add server-side checks at each data access
  • Register OAuth redirect URIs per environment (oauth2-oidc for provider details)
  • Test sign-up, sign-in, sign-out, a protected API call without a session, and a cross-org access attempt

Verification

  • curl -si https://localhost:3000/api/private without a cookie returns 401 or a redirect.
  • A logged-in user from org A requesting org B’s resource ID gets 404 or 403.
  • Session cookie flags: HttpOnly, Secure in production, SameSite=Lax.
  • Signing out invalidates the session server-side (database strategy) rather than only deleting the cookie.

Gotchas

  • AUTH_SECRET or BETTER_AUTH_SECRET differing between instances logs users out randomly.
  • OAuth callbacks fail behind proxies when the base URL env var is missing or wrong.
  • Account linking by email across providers without verified email enables takeover.
  • Edge runtimes cannot use some database drivers; keep session lookups in Node runtime routes or use an edge-compatible driver.
  • Hosted providers rate-limit their backend APIs; do not call them on every request when a verified session token already carries the user ID.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Auth Libraries"
  short_description: "Pick the right auth stack, then wire it safely"
  default_prompt: "$auth-libraries Set up auth for this Next.js app with GitHub login and email passwords"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem