FesaComponents

Auth

Autenticación sobre NextAuth v5 con tablas drizzle, bloqueo por intentos, revocación de sesiones, 2FA y SSO.

@fesa/components/auth-v2 trae las piezas para montar NextAuth v5 con credenciales sobre MySQL y drizzle. Requiere next-auth y drizzle-orm en la app.

db/schema.ts
import { createAuthTables } from '@fesa/components/auth-v2'

export const ROLES = ['admin', 'usuario'] as const

export const { users, authTokens, revokedSessions, activeSessions } = createAuthTables({
  roles: ROLES,
  defaultRole: 'usuario',
})
lib/auth.ts
import NextAuth from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import {
  createAuthCallbacks,
  createCredentialsAuthorize,
  createDrizzleAuthStorage,
  createRevocation,
  createSessionUserCache,
} from '@fesa/components/auth-v2'
import { activeSessions, authTokens, revokedSessions, users } from '@/db/schema'
import { db } from '@/lib/db'

const authStorage = createDrizzleAuthStorage(db, { users, authTokens, revokedSessions, activeSessions })
const revocation = createRevocation({ storage: authStorage })

const callbacks = createAuthCallbacks({
  getCachedSessionUser: createSessionUserCache(authStorage),
  getFreshSessionUser: (userId) => authStorage.getSessionUser(userId),
  isSessionRevoked: revocation.isSessionRevoked,
  claimsFromSignInUser: (user) => ({ role: user.role }),
  claimsFromSessionUser: (user) => ({ role: user.role, name: user.name, email: user.email }),
  sessionUserFromToken: (token) => ({ role: token.role, name: token.name, email: token.email }),
})

export const { handlers, auth, signIn, signOut } = NextAuth({
  session: { strategy: 'jwt' },
  providers: [
    Credentials({
      credentials: { email: {}, password: {} },
      authorize: createCredentialsAuthorize({ storage: authStorage }),
    }),
  ],
  callbacks: { jwt: callbacks.jwt, session: callbacks.session },
})

Ejemplo completo en producción: lib/auth.ts de developers.fesa.com.pa.