Authentication library for Next.js applications with support for OAuth, email, and credentials authentication
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Adapter interface for integrating with any database backend, supporting user management, session storage, and account linking. Enables persistent sessions and user data across application restarts.
Main interface for database adapters that handle user and session persistence.
/**
* Database adapter interface for NextAuth.js persistence
*/
interface Adapter {
/** Create a new user in the database */
createUser?: (user: Omit<AdapterUser, "id">) => Awaitable<AdapterUser>;
/** Get user by unique identifier */
getUser?: (id: string) => Awaitable<AdapterUser | null>;
/** Get user by email address */
getUserByEmail?: (email: string) => Awaitable<AdapterUser | null>;
/** Get user by provider account information */
getUserByAccount?: (
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
) => Awaitable<AdapterUser | null>;
/** Update existing user information */
updateUser?: (
user: Partial<AdapterUser> & Pick<AdapterUser, "id">
) => Awaitable<AdapterUser>;
/** Delete user from database (future implementation) */
deleteUser?: (
userId: string
) => Promise<void> | Awaitable<AdapterUser | null | undefined>;
/** Link provider account to user */
linkAccount?: (
account: AdapterAccount
) => Promise<void> | Awaitable<AdapterAccount | null | undefined>;
/** Unlink provider account from user (future implementation) */
unlinkAccount?: (
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
) => Promise<void> | Awaitable<AdapterAccount | undefined>;
/** Create session for authenticated user */
createSession?: (session: {
sessionToken: string;
userId: string;
expires: Date;
}) => Awaitable<AdapterSession>;
/** Get session and associated user data */
getSessionAndUser?: (
sessionToken: string
) => Awaitable<{ session: AdapterSession; user: AdapterUser } | null>;
/** Update session expiration or properties */
updateSession?: (
session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
) => Awaitable<AdapterSession | null | undefined>;
/** Delete session from database */
deleteSession?: (
sessionToken: string
) => Promise<void> | Awaitable<AdapterSession | null | undefined>;
/** Create email verification token */
createVerificationToken?: (
verificationToken: VerificationToken
) => Awaitable<VerificationToken | null | undefined>;
/** Use and delete verification token */
useVerificationToken?: (params: {
identifier: string;
token: string;
}) => Awaitable<VerificationToken | null>;
}Adapter methods for managing user accounts and profile information.
/**
* User data structure for database storage
*/
interface AdapterUser extends User {
id: string;
email: string;
emailVerified: Date | null;
}
interface User extends DefaultUser {
id: string;
name?: string | null;
email?: string | null;
image?: string | null;
}
interface DefaultUser {
id: string;
name?: string | null;
email?: string | null;
image?: string | null;
}Usage Examples:
// Prisma adapter implementation example
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default NextAuth({
adapter: PrismaAdapter(prisma),
providers: [...],
});
// Custom adapter implementation
const customAdapter: Adapter = {
async createUser(user) {
const newUser = await db.user.create({
data: {
name: user.name,
email: user.email,
image: user.image,
emailVerified: user.emailVerified,
},
});
return newUser;
},
async getUser(id) {
const user = await db.user.findUnique({ where: { id } });
return user;
},
async getUserByEmail(email) {
const user = await db.user.findUnique({ where: { email } });
return user;
},
async updateUser(user) {
const updatedUser = await db.user.update({
where: { id: user.id },
data: user,
});
return updatedUser;
},
// Implement other required methods...
};
export default NextAuth({
adapter: customAdapter,
providers: [...],
});Methods for managing provider account associations with users.
/**
* Provider account data structure
*/
interface AdapterAccount extends Account {
userId: string;
}
interface Account extends Partial<TokenSetParameters> {
providerAccountId: string;
userId?: string;
provider: string;
type: ProviderType;
access_token?: string;
expires_at?: number;
id_token?: string;
refresh_token?: string;
scope?: string;
session_state?: string;
token_type?: string;
}Usage Examples:
// Account linking implementation
const adapter: Adapter = {
async linkAccount(account) {
await db.account.create({
data: {
userId: account.userId,
provider: account.provider,
providerAccountId: account.providerAccountId,
type: account.type,
access_token: account.access_token,
expires_at: account.expires_at,
id_token: account.id_token,
refresh_token: account.refresh_token,
scope: account.scope,
session_state: account.session_state,
token_type: account.token_type,
},
});
},
async getUserByAccount({ provider, providerAccountId }) {
const account = await db.account.findUnique({
where: {
provider_providerAccountId: {
provider,
providerAccountId,
},
},
include: { user: true },
});
return account?.user ?? null;
},
async unlinkAccount({ provider, providerAccountId }) {
await db.account.delete({
where: {
provider_providerAccountId: {
provider,
providerAccountId,
},
},
});
},
};Database session storage for persistent authentication state.
/**
* Database session data structure
*/
interface AdapterSession {
/** Randomly generated session token */
sessionToken: string;
/** User ID associated with session */
userId: string;
/** Session expiration date */
expires: Date;
}Usage Examples:
// Session management implementation
const adapter: Adapter = {
async createSession({ sessionToken, userId, expires }) {
const session = await db.session.create({
data: {
sessionToken,
userId,
expires,
},
});
return session;
},
async getSessionAndUser(sessionToken) {
const sessionAndUser = await db.session.findUnique({
where: { sessionToken },
include: { user: true },
});
if (!sessionAndUser) return null;
const { user, ...session } = sessionAndUser;
return { user, session };
},
async updateSession({ sessionToken, ...session }) {
const updatedSession = await db.session.update({
where: { sessionToken },
data: session,
});
return updatedSession;
},
async deleteSession(sessionToken) {
const session = await db.session.delete({
where: { sessionToken },
});
return session;
},
};
// Configure NextAuth to use database sessions
export default NextAuth({
adapter,
session: {
strategy: "database",
maxAge: 30 * 24 * 60 * 60, // 30 days
updateAge: 24 * 60 * 60, // 24 hours
},
providers: [...],
});Token-based email verification system for passwordless authentication.
/**
* Email verification token structure
*/
interface VerificationToken {
/** Email address or identifier */
identifier: string;
/** Token expiration date */
expires: Date;
/** Verification token string */
token: string;
}Usage Examples:
// Email verification implementation
const adapter: Adapter = {
async createVerificationToken({ identifier, expires, token }) {
const verificationToken = await db.verificationToken.create({
data: {
identifier,
expires,
token,
},
});
return verificationToken;
},
async useVerificationToken({ identifier, token }) {
try {
const verificationToken = await db.verificationToken.delete({
where: {
identifier_token: {
identifier,
token,
},
},
});
return verificationToken;
} catch (error) {
// Token not found or already used
return null;
}
},
};
// Email provider with verification
import EmailProvider from "next-auth/providers/email";
export default NextAuth({
adapter,
providers: [
EmailProvider({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
});NextAuth.js provides official adapters for popular databases and ORMs.
/**
* Popular database adapters available
*/
interface DatabaseAdapters {
/** Prisma ORM adapter */
PrismaAdapter: (prisma: PrismaClient) => Adapter;
/** MongoDB adapter */
MongoDBAdapter: (client: MongoClient, options?: MongoDBAdapterOptions) => Adapter;
/** DynamoDB adapter */
DynamoDBAdapter: (client: DynamoDBDocument, options?: DynamoDBAdapterOptions) => Adapter;
/** Firebase Firestore adapter */
FirestoreAdapter: (firestore: Firestore) => Adapter;
/** Supabase adapter */
SupabaseAdapter: (client: SupabaseClient, options?: SupabaseAdapterOptions) => Adapter;
/** TypeORM adapter */
TypeORMAdapter: (connection: Connection | DataSource, options?: TypeORMAdapterOptions) => Adapter;
/** Sequelize adapter */
SequelizeAdapter: (sequelize: Sequelize) => Adapter;
}Usage Examples:
// Prisma adapter
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default NextAuth({
adapter: PrismaAdapter(prisma),
providers: [...],
});
// MongoDB adapter
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI!);
const clientPromise = client.connect();
export default NextAuth({
adapter: MongoDBAdapter(clientPromise),
providers: [...],
});
// DynamoDB adapter
import { DynamoDBAdapter } from "@next-auth/dynamodb-adapter";
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
const client = DynamoDBDocument.from(new DynamoDB({}), {
marshallOptions: {
convertEmptyValues: true,
removeUndefinedValues: true,
convertClassInstanceToMap: true,
},
});
export default NextAuth({
adapter: DynamoDBAdapter(client),
providers: [...],
});
// Supabase adapter
import { SupabaseAdapter } from "@next-auth/supabase-adapter";
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
export default NextAuth({
adapter: SupabaseAdapter({
url: process.env.NEXT_PUBLIC_SUPABASE_URL!,
secret: process.env.SUPABASE_SERVICE_ROLE_KEY!,
}),
providers: [...],
});Guidelines and patterns for implementing custom database adapters.
/**
* Custom adapter implementation utilities
*/
interface CustomAdapterPatterns {
/** Implement required methods first */
requiredMethods: (keyof Adapter)[];
/** Optional methods for enhanced functionality */
optionalMethods: (keyof Adapter)[];
/** Error handling patterns */
errorHandling: {
handleNotFound: () => null;
handleDuplicateKey: (error: any) => never;
handleConnectionError: (error: any) => never;
};
}Usage Examples:
// Custom MySQL adapter example
import mysql from 'mysql2/promise';
function MySQLAdapter(connection: mysql.Connection): Adapter {
return {
async createUser(user) {
const [result] = await connection.execute(
'INSERT INTO users (name, email, image, emailVerified) VALUES (?, ?, ?, ?)',
[user.name, user.email, user.image, user.emailVerified]
);
const [rows] = await connection.execute(
'SELECT * FROM users WHERE id = ?',
[(result as any).insertId]
);
return (rows as any)[0];
},
async getUser(id) {
const [rows] = await connection.execute(
'SELECT * FROM users WHERE id = ?',
[id]
);
return (rows as any)[0] || null;
},
async getUserByEmail(email) {
const [rows] = await connection.execute(
'SELECT * FROM users WHERE email = ?',
[email]
);
return (rows as any)[0] || null;
},
async getUserByAccount({ provider, providerAccountId }) {
const [rows] = await connection.execute(`
SELECT u.* FROM users u
JOIN accounts a ON u.id = a.userId
WHERE a.provider = ? AND a.providerAccountId = ?
`, [provider, providerAccountId]);
return (rows as any)[0] || null;
},
async updateUser(user) {
await connection.execute(
'UPDATE users SET name = ?, email = ?, image = ?, emailVerified = ? WHERE id = ?',
[user.name, user.email, user.image, user.emailVerified, user.id]
);
const [rows] = await connection.execute(
'SELECT * FROM users WHERE id = ?',
[user.id]
);
return (rows as any)[0];
},
async linkAccount(account) {
await connection.execute(`
INSERT INTO accounts (
userId, provider, providerAccountId, type,
access_token, expires_at, id_token, refresh_token,
scope, session_state, token_type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
account.userId, account.provider, account.providerAccountId, account.type,
account.access_token, account.expires_at, account.id_token, account.refresh_token,
account.scope, account.session_state, account.token_type
]);
},
async createSession({ sessionToken, userId, expires }) {
await connection.execute(
'INSERT INTO sessions (sessionToken, userId, expires) VALUES (?, ?, ?)',
[sessionToken, userId, expires]
);
return { sessionToken, userId, expires };
},
async getSessionAndUser(sessionToken) {
const [rows] = await connection.execute(`
SELECT s.*, u.* FROM sessions s
JOIN users u ON s.userId = u.id
WHERE s.sessionToken = ?
`, [sessionToken]);
const row = (rows as any)[0];
if (!row) return null;
return {
session: {
sessionToken: row.sessionToken,
userId: row.userId,
expires: row.expires
},
user: {
id: row.id,
name: row.name,
email: row.email,
image: row.image,
emailVerified: row.emailVerified
}
};
},
async updateSession({ sessionToken, ...session }) {
await connection.execute(
'UPDATE sessions SET expires = ? WHERE sessionToken = ?',
[session.expires, sessionToken]
);
const [rows] = await connection.execute(
'SELECT * FROM sessions WHERE sessionToken = ?',
[sessionToken]
);
return (rows as any)[0];
},
async deleteSession(sessionToken) {
const [rows] = await connection.execute(
'SELECT * FROM sessions WHERE sessionToken = ?',
[sessionToken]
);
await connection.execute(
'DELETE FROM sessions WHERE sessionToken = ?',
[sessionToken]
);
return (rows as any)[0] || null;
},
async createVerificationToken({ identifier, expires, token }) {
await connection.execute(
'INSERT INTO verification_tokens (identifier, expires, token) VALUES (?, ?, ?)',
[identifier, expires, token]
);
return { identifier, expires, token };
},
async useVerificationToken({ identifier, token }) {
const [rows] = await connection.execute(
'SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?',
[identifier, token]
);
if ((rows as any).length === 0) return null;
await connection.execute(
'DELETE FROM verification_tokens WHERE identifier = ? AND token = ?',
[identifier, token]
);
return (rows as any)[0];
},
};
}
export default NextAuth({
adapter: MySQLAdapter(connection),
providers: [...],
});Required database schema structure for NextAuth.js adapters.
/**
* Required database tables and columns
*/
interface DatabaseSchema {
users: {
id: string; // Primary key
name?: string | null;
email?: string | null; // Unique
emailVerified?: Date | null;
image?: string | null;
};
accounts: {
id?: string; // Primary key (optional)
userId: string; // Foreign key to users.id
provider: string;
providerAccountId: string;
type: string;
access_token?: string;
expires_at?: number;
id_token?: string;
refresh_token?: string;
scope?: string;
session_state?: string;
token_type?: string;
// Compound unique key on (provider, providerAccountId)
};
sessions: {
id?: string; // Primary key (optional)
sessionToken: string; // Unique
userId: string; // Foreign key to users.id
expires: Date;
};
verification_tokens: {
identifier: string;
token: string;
expires: Date;
// Compound unique key on (identifier, token)
};
}type Awaitable<T> = T | PromiseLike<T>;
interface TokenSetParameters {
access_token?: string;
token_type?: string;
id_token?: string;
refresh_token?: string;
scope?: string;
expires_at?: number;
session_state?: string;
}
type ProviderType = "oauth" | "email" | "credentials";interface DatabaseConnectionOptions {
host: string;
port: number;
database: string;
username: string;
password: string;
ssl?: boolean;
connectionLimit?: number;
acquireTimeout?: number;
timeout?: number;
}
interface ConnectionPool {
getConnection: () => Promise<any>;
releaseConnection: (connection: any) => void;
end: () => Promise<void>;
}