Skip to content

Auth Model

Decision: Email-Primary over Wallet-First

The VelaPay auth model uses email as the primary identity with wallet connections as secondary. This was a strategic pivot in v1.3 (originally wallet-first in v1.0–v1.2).

Why Email-Primary

FactorWallet-FirstEmail-PrimaryWhy Email Wins
Merchant familiarityWeb3-native onlyFamiliar to all merchantsMost merchants are traditional businesses. They expect email login.
Session persistenceWallet connect per sessionPersistent sessions via cookiesMerchants stay logged in across browser sessions. No re-connecting wallet every time.
Team accessOne wallet = one accountEmail + org membershipMultiple team members can access the same merchant account.
Security granularityAll-or-nothing wallet accessRole-based access controlAdmin can manage billing without treasury access.
Onboarding frictionInstall wallet, fund wallet, connectEnter email, verify, doneLower barrier to entry for non-crypto-native merchants.
Notification deliveryNo email = no notificationsEmail enables notificationsBilling alerts, payment failures, subscription changes all need email.
RecoverySeed phrase = recoveryEmail-based recoveryLost wallet doesn't mean lost account. Multiple recovery paths.

Why Wallet-First Failed for Merchants

Wallet-first auth makes sense for DeFi traders who live in Phantom. It fails for merchants because:

  1. Merchants are business operators, not DeFi power users. They want to log in with their work email, not their hardware wallet.
  2. Treasury and operations should be separate. The wallet that holds funds shouldn't be the same one used for daily dashboard access. Email auth decouples them.
  3. Team access requires multi-user auth. A business has multiple employees who need dashboard access. Wallets are single-user.
  4. Compliance requires email. KYC, invoicing, tax reporting — all require email addresses. A wallet-only auth model creates friction at every compliance touchpoint.

Better Auth Selection

Why Better Auth

RequirementBetter AuthAuth.jsClerk
D1 adapter✅ Native❌ No❌ Not applicable
Drizzle adapter✅ Native❌ No❌ Not applicable
Hono middleware✅ First-class❌ Express only❌ SDK-based
Self-hosted✅ Yes✅ Yes❌ Hosted service
Email/password✅ Built-in✅ Built-in✅ Built-in
Google SSO✅ Built-in✅ Built-in✅ Built-in
TOTP 2FA✅ Built-in❌ Requires plugin✅ Built-in
Org membership✅ Built-in❌ Custom✅ Built-in
SIWS (Sign-In with Solana)✅ Custom provider❌ Not supported❌ Not supported

Better Auth Architecture

┌──────────────────┐     ┌──────────────────┐     ┌──────────────┐
│  Browser          │────→│  Hono Worker      │────→│  D1 Database  │
│  (React SPA)      │     │  (Better Auth)    │     │  (sessions,   │
│                   │←────│                   │←────│   users, orgs)│
└──────────────────┘     └──────────────────┘     └──────────────┘

                               │ verifies

                         ┌──────────────┐
                         │  D1 batch()  │
                         │  for atomic  │
                         │  operations  │
                         └──────────────┘

Key implementation details:

  • Better Auth receives the D1 binding directly (env.DB).
  • D1 doesn't support interactive transactions — Better Auth uses D1's batch() API for atomicity.
  • Drizzle adapter co-locates auth schema with business schema in the same D1 database.
  • Hono middleware provides auth context to all API routes.

Org Model

1 Org = 1 Merchant Entity

Organization (merchant entity)
├── Members (email-based users with roles)
│   ├── Owner (created the org)
│   ├── Admin (can manage members and billing)
│   └── Member (can view and manage plans/subscribers)
├── Wallets (linked Solana addresses)
│   ├── Primary treasury wallet
│   ├── Operations wallet
│   └── Backup wallet
└── Billing data (plans, subscribers, analytics)

Why Org-Based

  • Multi-user access: Multiple employees can access the same merchant account with different permission levels.
  • Wallet flexibility: An org can link multiple wallets (treasury, operations, backup) without creating multiple accounts.
  • Team management: Owner can invite team members via email, assign roles, and revoke access.
  • Audit trail: Every action is attributed to a specific user within the org.

Sign-In with Solana (SIWS) is not used for authentication. Instead, it's repurposed as wallet-link verification:

  1. Merchant creates account via email/password or Google SSO.
  2. Merchant navigates to "Link Wallet" in dashboard settings.
  3. Dashboard generates a SIWS challenge (nonce + domain + statement).
  4. Merchant signs the challenge with their wallet.
  5. Dashboard verifies the signature and links the wallet address to the org.

Why Not SIWS for Auth

  • Single-device limitation: SIWS requires wallet access on the current device. Email auth works from any device.
  • No session persistence: Wallet auth requires re-signing on session expiry. Email auth uses persistent cookies.
  • No team access: SIWS binds to a single wallet. Org model requires multi-user auth.
  • Recovery: Lost wallet = lost access (with SIWS). Email has recovery flows.

SIWS Message Format

typescript
const message = `${domain} wants you to sign in with your Solana account:
${publicKey}

${statement}

Nonce: ${nonce}
Issued At: ${issuedAt}
Expiration Time: ${expiresAt}`;

The nonce is stored in D1 and validated server-side to prevent replay attacks.

Google SSO Integration

Google SSO is offered as an alternative to email/password for merchants who prefer it:

  • Implementation: Better Auth's built-in Google provider.
  • Account linking: Google SSO creates a Better Auth account with email verified automatically.
  • Org membership: Google SSO users go through the same org invitation flow.
  • Fallback: Email/password is always available as a fallback if Google is down or the merchant prefers not to use it.

Migration Path for Legacy Wallet-Only Users

v1.0–v1.2 used wallet-first auth. v1.3 introduced email-primary. The migration path:

  1. Wallet-based accounts still work: Existing wallet-linked accounts are preserved in D1.
  2. Email association flow: On first login after v1.3, wallet-only users are prompted to associate an email address.
  3. Backward compatibility: SIWS login still works for existing users who haven't migrated.
  4. Gradual migration: No forced migration. Wallet-only auth is deprecated but not removed.

Migration Steps

Legacy user (wallet-only)


Dashboard detects no email associated


Prompt: "Add email for notifications and team access"


User enters email → verification email sent


Email verified → org created with wallet as linked address


User can now log in via email or wallet

Security Model

Rate Limiting

EndpointRate LimitStrategy
Login attempts5 per minute per emailProgressive backoff
Magic link requests3 per minute per emailIP + email combination
SIWS challenges10 per minute per walletNonce-based deduplication
API calls (authenticated)100 per minute per sessionToken bucket
API calls (unauthenticated)20 per minute per IPFixed window

Session Management

PropertyConfiguration
Session duration7 days (configurable per org)
Session storageD1 (via Better Auth Drizzle adapter)
Session invalidationOn password change, role change, or manual logout
Concurrent sessionsAllowed (up to 5 per user)
Idle timeout24 hours of inactivity
Cookie flagsHttpOnly, Secure, SameSite=Lax

TOTP 2FA

Two-factor authentication using Time-based One-Time Passwords:

  • Implementation: Better Auth's built-in TOTP support.
  • Setup: QR code displayed in dashboard settings. User scans with authenticator app (Google Authenticator, Authy, etc.).
  • Enforcement: Optional per org. Admin can require 2FA for all org members.
  • Recovery: Backup codes generated during setup. Stored hashed in D1.
  • Verification: Required for sensitive operations (wallet linking, org ownership transfer, billing changes).

Threat Model

ThreatMitigation
Credential stuffingRate limiting + progressive backoff
Session hijackingHttpOnly + Secure cookies, session rotation
CSRFSameSite=Lax + origin validation
PhishingMagic link emails include user agent and IP
Wallet theft2FA for wallet operations, org-based recovery
Insider threatRole-based access, audit trail, admin-only sensitive ops
D1 compromiseBetter Auth hashes passwords with bcrypt. No plaintext credentials in D1.

Internal knowledge base for the Vela Labs workspace.