Auth & Security
Ezvento's authentication layer combines Supabase Auth (PKCE), custom JWT claims, 2FA (TOTP), PIN-based cashier login, manager PIN approvals, and tamper-evident audit logging. Every route is protected by either API-level or page-level guards.
Signup Flow
New users create an account, verify their email, and get an organization + trial subscription automatically.
Login Flow
Returning users authenticate, complete 2FA if required, and are routed to their persona's landing page.
2FA Setup & Verification
Roles requiring 2FA: Organization Owner, Regional Manager, Store Manager, Inventory Staff, Warehouse Manager, Accountant, and Auditor. Only Cashiers are exempt (they use PIN-fast login instead).
Password Reset
Users who forget their password can request a reset via email. The flow uses Supabase's built-in recovery mechanism with Resend for email delivery.
Session Management
Sessions are created lazily, validated on every request, and can be revoked individually or in bulk. The permission version system ensures role changes take effect immediately.
PIN-Based Cashier Auth
Cashiers log in with a 4–6 digit PIN instead of a password. This enables fast shift handovers at the POS without typing long passwords.
Security: PINs are stored as bcrypt hashes with a work factor of 12. The manager PIN is separate from the cashier PIN (stored in managerPin on the User model). PINs can be reset by Store Managers and Owners via USER_RESET_PIN.
JWT Claims
Ezvento uses a custom Supabase access token hook to inject tenant-specific claims into the JWT. This eliminates DB round-trips for common lookups.
Decoding JWT Claims
The decodeJwtClaims() helper in src/lib/api.ts base64-decodes the JWT payload without a network call:
export function decodeJwtClaims(accessToken: string) {
const payload = JSON.parse(
Buffer.from(accessToken.split('.')[1], 'base64').toString('utf8')
)
return {
tenantId: payload.tenantId,
appUserId: payload.appUserId,
isOwner: payload.isOwner,
isPlatformAdmin: payload.isPlatformAdmin,
permissionVersion: payload.permissionVersion,
jti: payload.jti,
exp: payload.exp,
}
}This is used in both API routes and proxy middleware to make fast auth decisions without hitting the database.
Security Features
Defense-in-depth measures across the proxy, API layer, and database.