Schema Overview
All tables are defined insrc/db/schema.ts:
src/db/schema.ts
- user: Core user information and profile data
- session: Active user sessions with metadata
- account: OAuth provider accounts linked to users
- verification: Temporary tokens for email verification
- jwks: JSON Web Key Set for JWT signing and verification
Tables
User Table
Stores core user profile information:src/db/schema.ts
| Column | Type | Constraints | Description |
|---|---|---|---|
id | text | PRIMARY KEY | Unique user identifier (generated by Better Auth) |
name | text | NOT NULL | User’s display name |
email | text | NOT NULL, UNIQUE | User’s email address |
emailVerified | boolean | NOT NULL, DEFAULT false | Whether the email has been verified |
image | text | nullable | URL to user’s profile picture (from OAuth providers) |
createdAt | timestamp | NOT NULL, DEFAULT NOW | When the user account was created |
updatedAt | timestamp | NOT NULL, DEFAULT NOW | Last time user data was updated |
The
emailVerified field is automatically set to true when users sign in via OAuth providers like Google, since the provider has already verified the email.Session Table
Tracks active user sessions with security metadata:src/db/schema.ts
| Column | Type | Constraints | Description |
|---|---|---|---|
id | text | PRIMARY KEY | Unique session identifier |
expiresAt | timestamp | NOT NULL | When the session expires |
token | text | NOT NULL, UNIQUE | Secure session token (stored in HTTP-only cookie) |
createdAt | timestamp | NOT NULL | When the session was created |
updatedAt | timestamp | NOT NULL | Last session activity time |
ipAddress | text | nullable | IP address of the client (for security auditing) |
userAgent | text | nullable | Browser/client user agent (for device tracking) |
userId | text | NOT NULL, FK → user.id | The user this session belongs to |
userIdreferencesuser.idwithON DELETE CASCADE— deleting a user automatically deletes all their sessions
Account Table
Stores OAuth provider accounts linked to users:src/db/schema.ts
| Column | Type | Constraints | Description |
|---|---|---|---|
id | text | PRIMARY KEY | Unique account identifier |
accountId | text | NOT NULL | Provider-specific account ID (e.g., Google user ID) |
providerId | text | NOT NULL | OAuth provider name (e.g., “google”, “github”) |
userId | text | NOT NULL, FK → user.id | The user this account belongs to |
accessToken | text | nullable | OAuth access token for API calls |
refreshToken | text | nullable | OAuth refresh token for renewing access |
idToken | text | nullable | OpenID Connect ID token |
accessTokenExpiresAt | timestamp | nullable | When the access token expires |
refreshTokenExpiresAt | timestamp | nullable | When the refresh token expires |
scope | text | nullable | OAuth scopes granted |
password | text | nullable | Hashed password (for email/password accounts) |
createdAt | timestamp | NOT NULL | When the account was linked |
updatedAt | timestamp | NOT NULL | Last time account data was updated |
userIdreferencesuser.idwithON DELETE CASCADE— deleting a user removes all their linked accounts
- OAuth Account
- Email/Password Account
When a user signs in with Google:
Verification Table
Stores temporary verification tokens for email verification and password resets:src/db/schema.ts
| Column | Type | Constraints | Description |
|---|---|---|---|
id | text | PRIMARY KEY | Unique verification identifier |
identifier | text | NOT NULL | What is being verified (email address) |
value | text | NOT NULL | The verification token/code |
expiresAt | timestamp | NOT NULL | When the token expires |
createdAt | timestamp | DEFAULT NOW | When the token was created |
updatedAt | timestamp | DEFAULT NOW | Last update time |
- Email verification when a new user signs up
- Password reset tokens
- Email change confirmations
- Magic link authentication (if enabled)
Tokens are automatically deleted after use or expiration to keep the table clean and secure.
JWKS Table
Stores JSON Web Key Sets for signing and verifying JWT tokens:src/db/schema.ts
| Column | Type | Constraints | Description |
|---|---|---|---|
id | text | PRIMARY KEY | Key identifier (kid in JWT header) |
publicKey | text | NOT NULL | RSA public key for JWT verification |
privateKey | text | NOT NULL | RSA private key for JWT signing |
createdAt | timestamp | NOT NULL | When the key pair was generated |
- Better Auth generates RSA key pairs automatically on first use
- The private key is used to sign JWTs when users make API requests
- The public key is exposed via the
/api/auth/jwksendpoint for backend verification - Keys can be rotated for enhanced security
Relationships
Here’s how the tables relate to each other:- A user can have multiple sessions (e.g., logged in on phone and laptop)
- A user can have multiple accounts (e.g., linked both Google and email/password)
- Verification and JWKS tables are independent
Database Migrations
The boilerplate uses Drizzle ORM for database migrations. To apply the schema:Querying the Database
You can query the database using Drizzle ORM:Data Retention
Session Cleanup
Session Cleanup
Expired sessions should be periodically cleaned up to prevent database bloat:Consider running this as a daily cron job.
Verification Token Cleanup
Verification Token Cleanup
Expired verification tokens are automatically removed by Better Auth after use or expiration.
User Deletion
User Deletion
When a user is deleted, cascading deletes automatically remove:
- All user sessions (via
ON DELETE CASCADE) - All linked accounts (via
ON DELETE CASCADE)
Security Best Practices
Encryption at Rest
Enable PostgreSQL encryption for sensitive fields like passwords and OAuth tokens.
Access Control
Restrict database access to only the application server. Use strong passwords and firewall rules.
Regular Backups
Implement automated, encrypted backups with point-in-time recovery.
Audit Logging
Enable PostgreSQL audit logging to track access to sensitive tables.
Next Steps
Authentication
Learn how Better Auth uses these tables for authentication
JWT Tokens
Understand how the JWKS table enables JWT signing