Appearance
Authentication Data Models
Detailed schema documentation for the current Better Auth database tables. Better Auth records and Lucid domain records use UUID v7 values generated in application code.
User Table
Primary user account table managed by Better Auth.
sql
CREATE TABLE users (
id UUID PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(254) NOT NULL UNIQUE,
email_verified BOOLEAN NOT NULL DEFAULT false,
image VARCHAR(255),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP,
active_workspace_id UUID,
theme VARCHAR(255) NOT NULL DEFAULT 'system'
);Columns
id: UUID v7 primary key generated by Better Authname: User's display name (optional)email: Login email address (unique)email_verified: Email verification statusimage: Profile image URL (optional)created_at: Account creation timestampupdated_at: Last profile updateactive_workspace_id: Optional user-level workspace preferencetheme: UI theme preference (systemby default)
Indexes
- Primary key on
id - Unique index on
email
Session Table
Database schema available for Better Auth session records with organization context. Under the current auth configuration, runtime sessions are stored in Redis instead of this table because secondaryStorage is configured and session.storeSessionInDatabase is not enabled.
sql
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token VARCHAR(255) NOT NULL UNIQUE,
expires_at TIMESTAMPTZ NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
active_organization_id UUID REFERENCES organizations(id) ON DELETE SET NULL,
active_team_id UUID REFERENCES teams(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);Columns
id: UUID v7 session record IDuser_id: Reference to usertoken: Unique opaque session tokenexpires_at: Session expiration timeip_address: Client IP for securityuser_agent: Browser/client infoactive_organization_id: Current workspace contextactive_team_id: Current team contextcreated_at: Session start timeupdated_at: Last activity time
Indexes
- Primary key on
id - Foreign key index on
user_id - Unique index on
token - Index on
expires_atfor cleanup - Index on
active_organization_id - Index on
active_team_id
Account Table
Credential records managed by Better Auth. Email/password registration creates a credential account whose Argon2 hash is stored in password. The token columns support Better Auth account types but no social providers are configured in Jubiloop.
sql
CREATE TABLE accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
account_id VARCHAR(255) NOT NULL,
provider_id VARCHAR(255) NOT NULL,
access_token TEXT,
refresh_token TEXT,
access_token_expires_at TIMESTAMPTZ,
refresh_token_expires_at TIMESTAMPTZ,
scope TEXT,
id_token TEXT,
password TEXT,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE(provider_id, account_id)
);Columns
id: Account record IDuser_id: Reference to useraccount_id: Provider-specific account identifierprovider_id: Better Auth provider identifier; email/password accounts usecredentialaccess_token: Optional provider access tokenrefresh_token: Optional provider refresh tokenaccess_token_expires_at: Optional access-token expiryrefresh_token_expires_at: Optional refresh-token expiryscope: Optional provider scopesid_token: Optional provider identity tokenpassword: Argon2 password hash for credential accounts; excluded from serializationcreated_at: Account link timeupdated_at: Last token refresh
Indexes
- Primary key on
id - Foreign key index on
user_id - Unique composite index on
(provider_id, account_id)
Verification Table
Database schema available for short-lived Better Auth verification records. The current password-reset flow stores verification values in Redis because secondaryStorage is configured and verification.storeInDatabase is not enabled. Email verification is not required for sign-up or sign-in.
sql
CREATE TABLE verifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
identifier VARCHAR(255) NOT NULL,
value VARCHAR(255) NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);Columns
id: Verification record IDidentifier: Email or user IDvalue: Better Auth verification valueexpires_at: Token expirationcreated_at: Token generation timeupdated_at: Last update
Indexes
- Primary key on
id - Index on
identifier - Index on
expires_atfor cleanup
Password Storage
There is no passwords table. Better Auth stores the Argon2 hash in accounts.password on the credential account. The Lucid model marks that field serializeAs: null, so API serialization does not expose it.
Security Implementation
Password Storage
- Better Auth calls the Argon2 hash and verify functions configured in
app/lib/auth.ts - Password hashes live in
accounts.password, never inusers
Session Management
- Better Auth stores runtime sessions and password-reset verification values in Redis
- The PostgreSQL
sessionsandverificationstables remain available but are not written by the current Better Auth storage configuration - A signed cookie cache can satisfy session checks for up to 5 minutes
- Sessions expire after 7 days and refresh after 1 day of age
- Active organization context tracked per session