or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-providers.mdcore-authentication.mddatabase-integration.mdindex.mdjwt-management.mdmiddleware-protection.mdreact-integration.mdserver-side-sessions.md

database-integration.mddocs/

0

# Database Integration

1

2

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.

3

4

## Capabilities

5

6

### Adapter Interface

7

8

Main interface for database adapters that handle user and session persistence.

9

10

```typescript { .api }

11

/**

12

* Database adapter interface for NextAuth.js persistence

13

*/

14

interface Adapter {

15

/** Create a new user in the database */

16

createUser?: (user: Omit<AdapterUser, "id">) => Awaitable<AdapterUser>;

17

/** Get user by unique identifier */

18

getUser?: (id: string) => Awaitable<AdapterUser | null>;

19

/** Get user by email address */

20

getUserByEmail?: (email: string) => Awaitable<AdapterUser | null>;

21

/** Get user by provider account information */

22

getUserByAccount?: (

23

providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">

24

) => Awaitable<AdapterUser | null>;

25

/** Update existing user information */

26

updateUser?: (

27

user: Partial<AdapterUser> & Pick<AdapterUser, "id">

28

) => Awaitable<AdapterUser>;

29

/** Delete user from database (future implementation) */

30

deleteUser?: (

31

userId: string

32

) => Promise<void> | Awaitable<AdapterUser | null | undefined>;

33

/** Link provider account to user */

34

linkAccount?: (

35

account: AdapterAccount

36

) => Promise<void> | Awaitable<AdapterAccount | null | undefined>;

37

/** Unlink provider account from user (future implementation) */

38

unlinkAccount?: (

39

providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">

40

) => Promise<void> | Awaitable<AdapterAccount | undefined>;

41

/** Create session for authenticated user */

42

createSession?: (session: {

43

sessionToken: string;

44

userId: string;

45

expires: Date;

46

}) => Awaitable<AdapterSession>;

47

/** Get session and associated user data */

48

getSessionAndUser?: (

49

sessionToken: string

50

) => Awaitable<{ session: AdapterSession; user: AdapterUser } | null>;

51

/** Update session expiration or properties */

52

updateSession?: (

53

session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">

54

) => Awaitable<AdapterSession | null | undefined>;

55

/** Delete session from database */

56

deleteSession?: (

57

sessionToken: string

58

) => Promise<void> | Awaitable<AdapterSession | null | undefined>;

59

/** Create email verification token */

60

createVerificationToken?: (

61

verificationToken: VerificationToken

62

) => Awaitable<VerificationToken | null | undefined>;

63

/** Use and delete verification token */

64

useVerificationToken?: (params: {

65

identifier: string;

66

token: string;

67

}) => Awaitable<VerificationToken | null>;

68

}

69

```

70

71

### User Management

72

73

Adapter methods for managing user accounts and profile information.

74

75

```typescript { .api }

76

/**

77

* User data structure for database storage

78

*/

79

interface AdapterUser extends User {

80

id: string;

81

email: string;

82

emailVerified: Date | null;

83

}

84

85

interface User extends DefaultUser {

86

id: string;

87

name?: string | null;

88

email?: string | null;

89

image?: string | null;

90

}

91

92

interface DefaultUser {

93

id: string;

94

name?: string | null;

95

email?: string | null;

96

image?: string | null;

97

}

98

```

99

100

**Usage Examples:**

101

102

```typescript

103

// Prisma adapter implementation example

104

import { PrismaAdapter } from "@next-auth/prisma-adapter";

105

import { PrismaClient } from "@prisma/client";

106

107

const prisma = new PrismaClient();

108

109

export default NextAuth({

110

adapter: PrismaAdapter(prisma),

111

providers: [...],

112

});

113

114

// Custom adapter implementation

115

const customAdapter: Adapter = {

116

async createUser(user) {

117

const newUser = await db.user.create({

118

data: {

119

name: user.name,

120

email: user.email,

121

image: user.image,

122

emailVerified: user.emailVerified,

123

},

124

});

125

return newUser;

126

},

127

128

async getUser(id) {

129

const user = await db.user.findUnique({ where: { id } });

130

return user;

131

},

132

133

async getUserByEmail(email) {

134

const user = await db.user.findUnique({ where: { email } });

135

return user;

136

},

137

138

async updateUser(user) {

139

const updatedUser = await db.user.update({

140

where: { id: user.id },

141

data: user,

142

});

143

return updatedUser;

144

},

145

146

// Implement other required methods...

147

};

148

149

export default NextAuth({

150

adapter: customAdapter,

151

providers: [...],

152

});

153

```

154

155

### Account Linking

156

157

Methods for managing provider account associations with users.

158

159

```typescript { .api }

160

/**

161

* Provider account data structure

162

*/

163

interface AdapterAccount extends Account {

164

userId: string;

165

}

166

167

interface Account extends Partial<TokenSetParameters> {

168

providerAccountId: string;

169

userId?: string;

170

provider: string;

171

type: ProviderType;

172

access_token?: string;

173

expires_at?: number;

174

id_token?: string;

175

refresh_token?: string;

176

scope?: string;

177

session_state?: string;

178

token_type?: string;

179

}

180

```

181

182

**Usage Examples:**

183

184

```typescript

185

// Account linking implementation

186

const adapter: Adapter = {

187

async linkAccount(account) {

188

await db.account.create({

189

data: {

190

userId: account.userId,

191

provider: account.provider,

192

providerAccountId: account.providerAccountId,

193

type: account.type,

194

access_token: account.access_token,

195

expires_at: account.expires_at,

196

id_token: account.id_token,

197

refresh_token: account.refresh_token,

198

scope: account.scope,

199

session_state: account.session_state,

200

token_type: account.token_type,

201

},

202

});

203

},

204

205

async getUserByAccount({ provider, providerAccountId }) {

206

const account = await db.account.findUnique({

207

where: {

208

provider_providerAccountId: {

209

provider,

210

providerAccountId,

211

},

212

},

213

include: { user: true },

214

});

215

216

return account?.user ?? null;

217

},

218

219

async unlinkAccount({ provider, providerAccountId }) {

220

await db.account.delete({

221

where: {

222

provider_providerAccountId: {

223

provider,

224

providerAccountId,

225

},

226

},

227

});

228

},

229

};

230

```

231

232

### Session Management

233

234

Database session storage for persistent authentication state.

235

236

```typescript { .api }

237

/**

238

* Database session data structure

239

*/

240

interface AdapterSession {

241

/** Randomly generated session token */

242

sessionToken: string;

243

/** User ID associated with session */

244

userId: string;

245

/** Session expiration date */

246

expires: Date;

247

}

248

```

249

250

**Usage Examples:**

251

252

```typescript

253

// Session management implementation

254

const adapter: Adapter = {

255

async createSession({ sessionToken, userId, expires }) {

256

const session = await db.session.create({

257

data: {

258

sessionToken,

259

userId,

260

expires,

261

},

262

});

263

return session;

264

},

265

266

async getSessionAndUser(sessionToken) {

267

const sessionAndUser = await db.session.findUnique({

268

where: { sessionToken },

269

include: { user: true },

270

});

271

272

if (!sessionAndUser) return null;

273

274

const { user, ...session } = sessionAndUser;

275

return { user, session };

276

},

277

278

async updateSession({ sessionToken, ...session }) {

279

const updatedSession = await db.session.update({

280

where: { sessionToken },

281

data: session,

282

});

283

return updatedSession;

284

},

285

286

async deleteSession(sessionToken) {

287

const session = await db.session.delete({

288

where: { sessionToken },

289

});

290

return session;

291

},

292

};

293

294

// Configure NextAuth to use database sessions

295

export default NextAuth({

296

adapter,

297

session: {

298

strategy: "database",

299

maxAge: 30 * 24 * 60 * 60, // 30 days

300

updateAge: 24 * 60 * 60, // 24 hours

301

},

302

providers: [...],

303

});

304

```

305

306

### Email Verification

307

308

Token-based email verification system for passwordless authentication.

309

310

```typescript { .api }

311

/**

312

* Email verification token structure

313

*/

314

interface VerificationToken {

315

/** Email address or identifier */

316

identifier: string;

317

/** Token expiration date */

318

expires: Date;

319

/** Verification token string */

320

token: string;

321

}

322

```

323

324

**Usage Examples:**

325

326

```typescript

327

// Email verification implementation

328

const adapter: Adapter = {

329

async createVerificationToken({ identifier, expires, token }) {

330

const verificationToken = await db.verificationToken.create({

331

data: {

332

identifier,

333

expires,

334

token,

335

},

336

});

337

return verificationToken;

338

},

339

340

async useVerificationToken({ identifier, token }) {

341

try {

342

const verificationToken = await db.verificationToken.delete({

343

where: {

344

identifier_token: {

345

identifier,

346

token,

347

},

348

},

349

});

350

return verificationToken;

351

} catch (error) {

352

// Token not found or already used

353

return null;

354

}

355

},

356

};

357

358

// Email provider with verification

359

import EmailProvider from "next-auth/providers/email";

360

361

export default NextAuth({

362

adapter,

363

providers: [

364

EmailProvider({

365

server: process.env.EMAIL_SERVER,

366

from: process.env.EMAIL_FROM,

367

}),

368

],

369

});

370

```

371

372

### Popular Database Adapters

373

374

NextAuth.js provides official adapters for popular databases and ORMs.

375

376

```typescript { .api }

377

/**

378

* Popular database adapters available

379

*/

380

interface DatabaseAdapters {

381

/** Prisma ORM adapter */

382

PrismaAdapter: (prisma: PrismaClient) => Adapter;

383

/** MongoDB adapter */

384

MongoDBAdapter: (client: MongoClient, options?: MongoDBAdapterOptions) => Adapter;

385

/** DynamoDB adapter */

386

DynamoDBAdapter: (client: DynamoDBDocument, options?: DynamoDBAdapterOptions) => Adapter;

387

/** Firebase Firestore adapter */

388

FirestoreAdapter: (firestore: Firestore) => Adapter;

389

/** Supabase adapter */

390

SupabaseAdapter: (client: SupabaseClient, options?: SupabaseAdapterOptions) => Adapter;

391

/** TypeORM adapter */

392

TypeORMAdapter: (connection: Connection | DataSource, options?: TypeORMAdapterOptions) => Adapter;

393

/** Sequelize adapter */

394

SequelizeAdapter: (sequelize: Sequelize) => Adapter;

395

}

396

```

397

398

**Usage Examples:**

399

400

```typescript

401

// Prisma adapter

402

import { PrismaAdapter } from "@next-auth/prisma-adapter";

403

import { PrismaClient } from "@prisma/client";

404

405

const prisma = new PrismaClient();

406

407

export default NextAuth({

408

adapter: PrismaAdapter(prisma),

409

providers: [...],

410

});

411

412

// MongoDB adapter

413

import { MongoDBAdapter } from "@next-auth/mongodb-adapter";

414

import { MongoClient } from "mongodb";

415

416

const client = new MongoClient(process.env.MONGODB_URI!);

417

const clientPromise = client.connect();

418

419

export default NextAuth({

420

adapter: MongoDBAdapter(clientPromise),

421

providers: [...],

422

});

423

424

// DynamoDB adapter

425

import { DynamoDBAdapter } from "@next-auth/dynamodb-adapter";

426

import { DynamoDB } from "@aws-sdk/client-dynamodb";

427

import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";

428

429

const client = DynamoDBDocument.from(new DynamoDB({}), {

430

marshallOptions: {

431

convertEmptyValues: true,

432

removeUndefinedValues: true,

433

convertClassInstanceToMap: true,

434

},

435

});

436

437

export default NextAuth({

438

adapter: DynamoDBAdapter(client),

439

providers: [...],

440

});

441

442

// Supabase adapter

443

import { SupabaseAdapter } from "@next-auth/supabase-adapter";

444

import { createClient } from "@supabase/supabase-js";

445

446

const supabase = createClient(

447

process.env.NEXT_PUBLIC_SUPABASE_URL!,

448

process.env.SUPABASE_SERVICE_ROLE_KEY!

449

);

450

451

export default NextAuth({

452

adapter: SupabaseAdapter({

453

url: process.env.NEXT_PUBLIC_SUPABASE_URL!,

454

secret: process.env.SUPABASE_SERVICE_ROLE_KEY!,

455

}),

456

providers: [...],

457

});

458

```

459

460

### Custom Adapter Implementation

461

462

Guidelines and patterns for implementing custom database adapters.

463

464

```typescript { .api }

465

/**

466

* Custom adapter implementation utilities

467

*/

468

interface CustomAdapterPatterns {

469

/** Implement required methods first */

470

requiredMethods: (keyof Adapter)[];

471

/** Optional methods for enhanced functionality */

472

optionalMethods: (keyof Adapter)[];

473

/** Error handling patterns */

474

errorHandling: {

475

handleNotFound: () => null;

476

handleDuplicateKey: (error: any) => never;

477

handleConnectionError: (error: any) => never;

478

};

479

}

480

```

481

482

**Usage Examples:**

483

484

```typescript

485

// Custom MySQL adapter example

486

import mysql from 'mysql2/promise';

487

488

function MySQLAdapter(connection: mysql.Connection): Adapter {

489

return {

490

async createUser(user) {

491

const [result] = await connection.execute(

492

'INSERT INTO users (name, email, image, emailVerified) VALUES (?, ?, ?, ?)',

493

[user.name, user.email, user.image, user.emailVerified]

494

);

495

496

const [rows] = await connection.execute(

497

'SELECT * FROM users WHERE id = ?',

498

[(result as any).insertId]

499

);

500

501

return (rows as any)[0];

502

},

503

504

async getUser(id) {

505

const [rows] = await connection.execute(

506

'SELECT * FROM users WHERE id = ?',

507

[id]

508

);

509

510

return (rows as any)[0] || null;

511

},

512

513

async getUserByEmail(email) {

514

const [rows] = await connection.execute(

515

'SELECT * FROM users WHERE email = ?',

516

[email]

517

);

518

519

return (rows as any)[0] || null;

520

},

521

522

async getUserByAccount({ provider, providerAccountId }) {

523

const [rows] = await connection.execute(`

524

SELECT u.* FROM users u

525

JOIN accounts a ON u.id = a.userId

526

WHERE a.provider = ? AND a.providerAccountId = ?

527

`, [provider, providerAccountId]);

528

529

return (rows as any)[0] || null;

530

},

531

532

async updateUser(user) {

533

await connection.execute(

534

'UPDATE users SET name = ?, email = ?, image = ?, emailVerified = ? WHERE id = ?',

535

[user.name, user.email, user.image, user.emailVerified, user.id]

536

);

537

538

const [rows] = await connection.execute(

539

'SELECT * FROM users WHERE id = ?',

540

[user.id]

541

);

542

543

return (rows as any)[0];

544

},

545

546

async linkAccount(account) {

547

await connection.execute(`

548

INSERT INTO accounts (

549

userId, provider, providerAccountId, type,

550

access_token, expires_at, id_token, refresh_token,

551

scope, session_state, token_type

552

) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

553

`, [

554

account.userId, account.provider, account.providerAccountId, account.type,

555

account.access_token, account.expires_at, account.id_token, account.refresh_token,

556

account.scope, account.session_state, account.token_type

557

]);

558

},

559

560

async createSession({ sessionToken, userId, expires }) {

561

await connection.execute(

562

'INSERT INTO sessions (sessionToken, userId, expires) VALUES (?, ?, ?)',

563

[sessionToken, userId, expires]

564

);

565

566

return { sessionToken, userId, expires };

567

},

568

569

async getSessionAndUser(sessionToken) {

570

const [rows] = await connection.execute(`

571

SELECT s.*, u.* FROM sessions s

572

JOIN users u ON s.userId = u.id

573

WHERE s.sessionToken = ?

574

`, [sessionToken]);

575

576

const row = (rows as any)[0];

577

if (!row) return null;

578

579

return {

580

session: {

581

sessionToken: row.sessionToken,

582

userId: row.userId,

583

expires: row.expires

584

},

585

user: {

586

id: row.id,

587

name: row.name,

588

email: row.email,

589

image: row.image,

590

emailVerified: row.emailVerified

591

}

592

};

593

},

594

595

async updateSession({ sessionToken, ...session }) {

596

await connection.execute(

597

'UPDATE sessions SET expires = ? WHERE sessionToken = ?',

598

[session.expires, sessionToken]

599

);

600

601

const [rows] = await connection.execute(

602

'SELECT * FROM sessions WHERE sessionToken = ?',

603

[sessionToken]

604

);

605

606

return (rows as any)[0];

607

},

608

609

async deleteSession(sessionToken) {

610

const [rows] = await connection.execute(

611

'SELECT * FROM sessions WHERE sessionToken = ?',

612

[sessionToken]

613

);

614

615

await connection.execute(

616

'DELETE FROM sessions WHERE sessionToken = ?',

617

[sessionToken]

618

);

619

620

return (rows as any)[0] || null;

621

},

622

623

async createVerificationToken({ identifier, expires, token }) {

624

await connection.execute(

625

'INSERT INTO verification_tokens (identifier, expires, token) VALUES (?, ?, ?)',

626

[identifier, expires, token]

627

);

628

629

return { identifier, expires, token };

630

},

631

632

async useVerificationToken({ identifier, token }) {

633

const [rows] = await connection.execute(

634

'SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?',

635

[identifier, token]

636

);

637

638

if ((rows as any).length === 0) return null;

639

640

await connection.execute(

641

'DELETE FROM verification_tokens WHERE identifier = ? AND token = ?',

642

[identifier, token]

643

);

644

645

return (rows as any)[0];

646

},

647

};

648

}

649

650

export default NextAuth({

651

adapter: MySQLAdapter(connection),

652

providers: [...],

653

});

654

```

655

656

### Database Schema Requirements

657

658

Required database schema structure for NextAuth.js adapters.

659

660

```typescript { .api }

661

/**

662

* Required database tables and columns

663

*/

664

interface DatabaseSchema {

665

users: {

666

id: string; // Primary key

667

name?: string | null;

668

email?: string | null; // Unique

669

emailVerified?: Date | null;

670

image?: string | null;

671

};

672

673

accounts: {

674

id?: string; // Primary key (optional)

675

userId: string; // Foreign key to users.id

676

provider: string;

677

providerAccountId: string;

678

type: string;

679

access_token?: string;

680

expires_at?: number;

681

id_token?: string;

682

refresh_token?: string;

683

scope?: string;

684

session_state?: string;

685

token_type?: string;

686

// Compound unique key on (provider, providerAccountId)

687

};

688

689

sessions: {

690

id?: string; // Primary key (optional)

691

sessionToken: string; // Unique

692

userId: string; // Foreign key to users.id

693

expires: Date;

694

};

695

696

verification_tokens: {

697

identifier: string;

698

token: string;

699

expires: Date;

700

// Compound unique key on (identifier, token)

701

};

702

}

703

```

704

705

## Types

706

707

### Adapter Data Types

708

709

```typescript { .api }

710

type Awaitable<T> = T | PromiseLike<T>;

711

712

interface TokenSetParameters {

713

access_token?: string;

714

token_type?: string;

715

id_token?: string;

716

refresh_token?: string;

717

scope?: string;

718

expires_at?: number;

719

session_state?: string;

720

}

721

722

type ProviderType = "oauth" | "email" | "credentials";

723

```

724

725

### Database Connection Types

726

727

```typescript { .api }

728

interface DatabaseConnectionOptions {

729

host: string;

730

port: number;

731

database: string;

732

username: string;

733

password: string;

734

ssl?: boolean;

735

connectionLimit?: number;

736

acquireTimeout?: number;

737

timeout?: number;

738

}

739

740

interface ConnectionPool {

741

getConnection: () => Promise<any>;

742

releaseConnection: (connection: any) => void;

743

end: () => Promise<void>;

744

}

745

```