User Storage Modes
Auth always owns security state. The database mode controls who owns user profile records.
Mode Summary
Section titled “Mode Summary”| Mode | Best for | Auth-owned tables |
|---|---|---|
managed | Apps that want Auth to own the user profile and metadata shape. | layeron_auth_users, layeron_auth_identities, layeron_auth_credentials, layeron_auth_sessions, layeron_auth_refresh_tokens, layeron_auth_oauth_states |
managed_core | Apps that want Auth to own identity basics and keep application profile data elsewhere. | layeron_auth_users, layeron_auth_identities, layeron_auth_credentials, layeron_auth_sessions, layeron_auth_refresh_tokens, layeron_auth_oauth_states |
mapped | Apps that store user profiles in a Database product table. | layeron_auth_identities, layeron_auth_credentials, layeron_auth_sessions, layeron_auth_refresh_tokens, layeron_auth_oauth_states |
custom | Apps that already own a user table and need custom lifecycle hooks. | layeron_auth_identities, layeron_auth_credentials, layeron_auth_sessions, layeron_auth_refresh_tokens, layeron_auth_oauth_states |
external | Apps that resolve user profiles through a function and want Auth to own login state. | layeron_auth_identities, layeron_auth_credentials, layeron_auth_sessions, layeron_auth_refresh_tokens, layeron_auth_oauth_states |
All Auth tables live in an internal Layeron Database Product store with
namespace: "layeron".
Managed Fields
Section titled “Managed Fields”managed stores the complete Auth user shape:
| Field | Column |
|---|---|
id | id |
email | email |
emailVerifiedAt | email_verified_at |
phone | phone |
phoneVerifiedAt | phone_verified_at |
primaryIdentityId | primary_identity_id |
username | username |
displayName | display_name |
avatarUrl | avatar_url |
defaultTenantId | default_tenant_id |
isAnonymous | is_anonymous |
roles | roles |
scopes | scopes |
attributes | attributes |
status | status |
appMetadata | app_metadata |
userMetadata | user_metadata |
createdAt | created_at |
updatedAt | updated_at |
lastSignInAt | last_sign_in_at |
disabledAt | disabled_at |
Use managed when the Auth user record can be the main user profile for the
backend.
const appAuth = auth({ database: { mode: "managed", },})Managed Core Fields
Section titled “Managed Core Fields”managed_core stores only the stable identity fields:
| Field | Column |
|---|---|
id | id |
email | email |
emailVerifiedAt | email_verified_at |
phone | phone |
phoneVerifiedAt | phone_verified_at |
primaryIdentityId | primary_identity_id |
username | username |
displayName | display_name |
avatarUrl | avatar_url |
status | status |
createdAt | created_at |
updatedAt | updated_at |
lastSignInAt | last_sign_in_at |
disabledAt | disabled_at |
Use managed_core when application data owns tenant membership, roles, profile
metadata, and domain-specific user settings.
const appAuth = auth({ database: { mode: "managed_core", },})Custom User Store
Section titled “Custom User Store”custom stores session records and calls your functions for user records.
const appAuth = auth({ database: { mode: "custom", async getUser({ userId }) { return await users.findById(userId) }, async createUser({ email, emailVerifiedAt, user }) { return await users.create({ email, emailVerifiedAt, displayName: user.displayName, }) }, async isUserEnabled({ user }) { return user.status !== "disabled" }, async updateLastSignIn({ userId, signedInAt }) { await users.updateLastSignIn(userId, signedInAt) }, async updateEmailVerifiedAt({ userId, emailVerifiedAt }) { await users.updateEmailVerifiedAt(userId, emailVerifiedAt) }, },})The getUser function must return the same id that Auth requested. Auth
rejects the session when the user is missing, disabled, or returned with a
different id.
Email sign-up in custom mode requires createUser. OTP verification in
custom mode requires updateEmailVerifiedAt.
Mapped User Table
Section titled “Mapped User Table”mapped stores the user profile in your Database product and stores Auth
security state in Auth’s internal Database product store. Auth queries the
mapped table by idColumn whenever it creates, verifies, refreshes, or resolves
a session.
import { auth, db } from "@layeron/modules"
const usersDb = db({ name: "users", namespace: "app",})
const appAuth = auth({ database: { mode: "mapped", product: usersDb, users: { table: "profiles", idColumn: "id", emailColumn: "email", displayNameColumn: "display_name", metadataColumn: "metadata", }, },})Mapped table and column names must be SQL identifiers. Email/password sign-up in
mapped mode requires userId; the mapped user’s email must match the sign-up
email.
External User Resolver
Section titled “External User Resolver”external stores login state in Auth and resolves user profiles through
resolveUser.
const appAuth = auth({ database: { mode: "external", async resolveUser({ userId, request }) { return await users.resolveProfile({ userId, request, }) }, },})The resolver must return the same id that Auth requested. Email/password
sign-up in external mode requires userId; the resolved user’s email must
match the sign-up email.
Identity Fields
Section titled “Identity Fields”Every mode stores email/password login identities in layeron_auth_identities.
The identity row separates login providers from the user record.
| Field | Column |
|---|---|
id | id |
userId | user_id |
provider | provider |
providerUserId | provider_user_id |
email | email |
phone | phone |
verifiedAt | verified_at |
linkedAt | linked_at |
lastSignInAt | last_sign_in_at |
identityData | identity_data |
Session Fields
Section titled “Session Fields”Every mode stores the same session fields:
| Field | Column |
|---|---|
id | id |
userId | user_id |
| access token hash | access_token_hash |
device | device |
createdAt | created_at |
expiresAt | expires_at |
lastUsedAt | last_used_at |
revokedAt | revoked_at |
Refresh Token Fields
Section titled “Refresh Token Fields”Every mode stores refresh tokens as hashes. Refresh token rotation is enabled by default. Reuse detection can revoke the whole token family.
| Field | Column |
|---|---|
id | id |
userId | user_id |
sessionId | session_id |
| refresh token hash | token_hash |
| family id | family_id |
| parent token hash | parent_token_hash |
createdAt | created_at |
expiresAt | expires_at |
lastUsedAt | last_used_at |
revokedAt | revoked_at |
replacedAt | replaced_at |
reusedAt | reused_at |
device | device |