@fonderie/auth 1.3.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ -- ── fonderie_users ───────────────────────────────────────────────
2
+ CREATE TABLE IF NOT EXISTS fonderie_users (
3
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4
+ email TEXT NOT NULL UNIQUE,
5
+ password_hash TEXT,
6
+ first_name TEXT,
7
+ last_name TEXT,
8
+ phone TEXT,
9
+ profile_image_url TEXT,
10
+ locale TEXT NOT NULL DEFAULT 'en-US',
11
+ timezone TEXT NOT NULL DEFAULT 'UTC',
12
+ provider TEXT,
13
+ provider_id TEXT,
14
+ is_active BOOLEAN NOT NULL DEFAULT true,
15
+ last_login TIMESTAMPTZ,
16
+ preferences JSONB NOT NULL DEFAULT '{"notifications":{"email":true,"inApp":true,"sms":false,"push":false},"emailDigest":"immediate","dateFormat":"MM/DD/YYYY","timeFormat":"hh:mm A"}',
17
+ suspended BOOLEAN NOT NULL DEFAULT false,
18
+ whitelist BOOLEAN NOT NULL DEFAULT false,
19
+ ip_whitelist JSONB NOT NULL DEFAULT '[]',
20
+ mfa_enabled BOOLEAN NOT NULL DEFAULT false,
21
+ mfa_secret TEXT,
22
+ email_verified_at TIMESTAMPTZ,
23
+ deleted_at TIMESTAMPTZ,
24
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
25
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
26
+ );
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_fonderie_users_email ON fonderie_users (email);
29
+
30
+ -- ── fonderie_email_verifications ─────────────────────────────────
31
+ CREATE TABLE IF NOT EXISTS fonderie_email_verifications (
32
+ token TEXT PRIMARY KEY,
33
+ user_id UUID NOT NULL REFERENCES fonderie_users(id) ON DELETE CASCADE,
34
+ expires_at TIMESTAMPTZ NOT NULL,
35
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
36
+ );
37
+
38
+ -- ── fonderie_password_resets ─────────────────────────────────────
39
+ CREATE TABLE IF NOT EXISTS fonderie_password_resets (
40
+ user_id UUID PRIMARY KEY REFERENCES fonderie_users(id) ON DELETE CASCADE,
41
+ token TEXT NOT NULL UNIQUE,
42
+ expires_at TIMESTAMPTZ NOT NULL,
43
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
44
+ );
45
+
46
+ CREATE INDEX IF NOT EXISTS idx_fonderie_password_resets_token ON fonderie_password_resets (token);
47
+
48
+ -- ── fonderie_sessions ────────────────────────────────────────────
49
+ CREATE TABLE IF NOT EXISTS fonderie_sessions (
50
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
51
+ user_id UUID NOT NULL REFERENCES fonderie_users(id) ON DELETE CASCADE,
52
+ token TEXT NOT NULL UNIQUE,
53
+ user_agent TEXT,
54
+ ip_address TEXT,
55
+ expires_at TIMESTAMPTZ NOT NULL,
56
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
57
+ );
58
+
59
+ CREATE INDEX IF NOT EXISTS idx_fonderie_sessions_user_id ON fonderie_sessions (user_id);
60
+ CREATE INDEX IF NOT EXISTS idx_fonderie_sessions_token ON fonderie_sessions (token);
61
+ CREATE INDEX IF NOT EXISTS idx_fonderie_sessions_expires_at ON fonderie_sessions (expires_at);
62
+
63
+ -- ── fonderie_mfa_challenges ──────────────────────────────────────
64
+ CREATE TABLE IF NOT EXISTS fonderie_mfa_challenges (
65
+ token TEXT PRIMARY KEY,
66
+ user_id UUID NOT NULL REFERENCES fonderie_users(id) ON DELETE CASCADE,
67
+ expires_at TIMESTAMPTZ NOT NULL,
68
+ used_at TIMESTAMPTZ,
69
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_fonderie_mfa_challenges_user_id ON fonderie_mfa_challenges (user_id);
73
+ CREATE INDEX IF NOT EXISTS idx_fonderie_mfa_challenges_expires_at ON fonderie_mfa_challenges (expires_at);
@@ -0,0 +1,20 @@
1
+ -- Make email nullable to support phone-only users
2
+ ALTER TABLE fonderie_users ALTER COLUMN email DROP NOT NULL;
3
+
4
+ -- Track when phone was last verified via OTP
5
+ ALTER TABLE fonderie_users ADD COLUMN IF NOT EXISTS
6
+ phone_verified_at TIMESTAMPTZ;
7
+
8
+ -- Unique constraint on phone (NULLs are distinct in PostgreSQL, so existing
9
+ -- rows with NULL phone are unaffected)
10
+ ALTER TABLE fonderie_users
11
+ ADD CONSTRAINT fonderie_users_phone_unique UNIQUE (phone);
12
+
13
+ -- ── fonderie_phone_verifications ─────────────────────────────────
14
+ -- One pending OTP per phone number at a time (phone is the PK)
15
+ CREATE TABLE IF NOT EXISTS fonderie_phone_verifications (
16
+ phone TEXT PRIMARY KEY,
17
+ otp TEXT NOT NULL,
18
+ expires_at TIMESTAMPTZ NOT NULL,
19
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
20
+ );
@@ -0,0 +1,4 @@
1
+ -- No longer needed: first_name/last_name are written directly to
2
+ -- fonderie_users on registration, so they never need to travel through
3
+ -- the verifications table.
4
+ SELECT 1;
@@ -0,0 +1,5 @@
1
+ -- Drop columns added speculatively in 003 — user is created immediately
2
+ -- on registration so names never need to be carried in this table.
3
+ ALTER TABLE fonderie_phone_verifications
4
+ DROP COLUMN IF EXISTS first_name,
5
+ DROP COLUMN IF EXISTS last_name;
@@ -0,0 +1,6 @@
1
+ -- Link phone verifications to a specific user so verify can be done by JWT alone
2
+ ALTER TABLE fonderie_phone_verifications
3
+ ADD COLUMN IF NOT EXISTS user_id UUID REFERENCES fonderie_users(id) ON DELETE CASCADE;
4
+
5
+ CREATE INDEX IF NOT EXISTS idx_phone_verifications_user_id
6
+ ON fonderie_phone_verifications (user_id);
@@ -0,0 +1 @@
1
+ ALTER TABLE fonderie_users DROP COLUMN IF EXISTS phone_verified_at;
@@ -0,0 +1,14 @@
1
+ -- Swap primary key from token to user_id so concurrent users can share the
2
+ -- same 6-digit PIN without colliding. Deduplicate first (keep newest row per
3
+ -- user) in case stale data exists, then promote user_id to PK.
4
+
5
+ DELETE FROM fonderie_email_verifications e1
6
+ USING fonderie_email_verifications e2
7
+ WHERE e1.user_id = e2.user_id
8
+ AND e1.created_at < e2.created_at;
9
+
10
+ ALTER TABLE fonderie_email_verifications
11
+ DROP CONSTRAINT fonderie_email_verifications_pkey;
12
+
13
+ ALTER TABLE fonderie_email_verifications
14
+ ADD PRIMARY KEY (user_id);
@@ -0,0 +1,4 @@
1
+ ALTER TABLE fonderie_password_resets RENAME COLUMN token TO pin;
2
+
3
+ DROP INDEX IF EXISTS idx_fonderie_password_resets_token;
4
+ CREATE INDEX IF NOT EXISTS idx_fonderie_password_resets_pin ON fonderie_password_resets (pin);
@@ -0,0 +1 @@
1
+ ALTER TABLE fonderie_password_resets ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now();
@@ -0,0 +1,3 @@
1
+ ALTER TABLE fonderie_users
2
+ ADD COLUMN IF NOT EXISTS mfa_secret_pending TEXT,
3
+ ADD COLUMN IF NOT EXISTS mfa_secret_pending_expires_at TIMESTAMPTZ;
@@ -0,0 +1,10 @@
1
+ CREATE TABLE IF NOT EXISTS fonderie_mfa_backup_codes (
2
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3
+ user_id UUID NOT NULL REFERENCES fonderie_users(id) ON DELETE CASCADE,
4
+ code_hash TEXT NOT NULL,
5
+ used_at TIMESTAMPTZ,
6
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
7
+ );
8
+
9
+ CREATE INDEX IF NOT EXISTS idx_fonderie_mfa_backup_codes_user_id
10
+ ON fonderie_mfa_backup_codes (user_id);
@@ -0,0 +1 @@
1
+ ALTER TABLE fonderie_users DROP COLUMN IF EXISTS skills;
@@ -0,0 +1,5 @@
1
+ -- Session-backed access-token revocation: access tokens carry the sid of
2
+ -- the refresh session they were issued with; withSession() rejects access
3
+ -- tokens whose session row is gone (logout, rotation, password change).
4
+ ALTER TABLE fonderie_sessions ADD COLUMN IF NOT EXISTS sid UUID;
5
+ CREATE INDEX IF NOT EXISTS idx_fonderie_sessions_sid ON fonderie_sessions (sid);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonderie/auth",
3
- "version": "1.3.1",
3
+ "version": "2.0.0",
4
4
  "description": "Drop-in auth for SaaS — email/password, phone OTP, Google OAuth, stateless JWT sessions, TOTP-based MFA with backup codes, and self-service password recovery.",
5
5
  "keywords": [
6
6
  "fonderie-js",
@@ -58,11 +58,11 @@
58
58
  "jsonwebtoken": "^9.0.3",
59
59
  "qrcode": "^1.5.4",
60
60
  "zod": "^4.4.3",
61
- "@fonderie/rate-limit": "^0.1.0"
61
+ "@fonderie/rate-limit": "^1.0.0"
62
62
  },
63
63
  "peerDependencies": {
64
- "@fonderie/core": "^0.1.1",
65
- "@fonderie/events": "^1.0.1",
64
+ "@fonderie/core": "^0.2.0",
65
+ "@fonderie/events": "^2.0.0",
66
66
  "@fonderie/store": "^0.1.1"
67
67
  },
68
68
  "peerDependenciesMeta": {