@momentumcms/auth 0.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ ## 0.1.0 (2026-02-16)
2
+
3
+ ### 🚀 Features
4
+
5
+ - add password reset flow with E2E tests ([#6](https://github.com/DonaldMurillo/momentum-cms/pull/6))
6
+ - **ui:** enhance command palette with autofocus, filtering, and keyboard nav ([#2](https://github.com/DonaldMurillo/momentum-cms/pull/2))
7
+ - Add authentication, UI library, and theme system ([0d38720](https://github.com/DonaldMurillo/momentum-cms/commit/0d38720))
8
+ - Implement admin UI with API integration and SSR hydration ([9ed7b2b](https://github.com/DonaldMurillo/momentum-cms/commit/9ed7b2b))
9
+ - Initialize Momentum CMS foundation ([f64f581](https://github.com/DonaldMurillo/momentum-cms/commit/f64f581))
10
+
11
+ ### 🩹 Fixes
12
+
13
+ - address security vulnerabilities from code review ([#9](https://github.com/DonaldMurillo/momentum-cms/pull/9))
14
+
15
+ ### ❤️ Thank You
16
+
17
+ - Claude Opus 4.5
18
+ - Claude Opus 4.6
19
+ - Donald Murillo @DonaldMurillo
package/CLAUDE.md ADDED
@@ -0,0 +1,130 @@
1
+ # Auth Library (`@momentumcms/auth`)
2
+
3
+ Better Auth integration for Momentum CMS. Plugin-based architecture that manages auth collections, session resolution, and sub-plugin composition.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```
8
+ momentumAuth(config) ← Plugin factory (auth-plugin.ts)
9
+ ├── BASE_AUTH_COLLECTIONS ← 5 auth collections (auth-collections.ts)
10
+ ├── createMomentumAuth() ← Better Auth instance (auth.ts)
11
+ └── sub-plugins[] ← Composable extensions (plugins/)
12
+ ├── authTwoFactor()
13
+ ├── authAdmin()
14
+ └── authOrganization()
15
+ ```
16
+
17
+ ### Key Files
18
+
19
+ | File | Purpose |
20
+ | ----------------------------- | ---------------------------------------------------------------------- |
21
+ | `auth-plugin.ts` | `momentumAuth()` factory — creates the `MomentumPlugin` |
22
+ | `auth.ts` | `createMomentumAuth()` — wraps `betterAuth()` with Momentum defaults |
23
+ | `auth-collections.ts` | Base auth collections (user, session, account, verification, api-keys) |
24
+ | `plugins/sub-plugin.types.ts` | `MomentumAuthSubPlugin` interface |
25
+ | `plugins/two-factor.ts` | Example sub-plugin pattern |
26
+
27
+ ### Plugin Lifecycle
28
+
29
+ 1. `momentumAuth(config)` is called at config time — collects sub-plugin collections/fields
30
+ 2. `plugin.collections` is read by admin routes for static route data (browser-safe)
31
+ 3. `plugin.onInit(context)` runs during server init — injects collections, creates Better Auth instance
32
+ 4. `initializeMomentum()` in server-express detects the auth plugin, auto-creates middleware
33
+
34
+ ## Collection Slugs and DB Tables
35
+
36
+ Auth collections use `auth-` prefixed slugs to avoid conflicts with user-defined collections.
37
+
38
+ | Slug | DB Table | Sidebar | Access |
39
+ | ------------------- | -------------- | --------------------------- | --------------------------------------------------- |
40
+ | `auth-user` | `user` | Yes (group: Authentication) | Admin only (read/write) |
41
+ | `auth-session` | `session` | No (hidden) | Admin read, no write |
42
+ | `auth-account` | `account` | No (hidden) | No read (contains OAuth tokens/passwords), no write |
43
+ | `auth-verification` | `verification` | No (hidden) | No access |
44
+ | `auth-api-keys` | `_api_keys` | Yes (group: Authentication) | Any authenticated user (scoped by `defaultWhere`) |
45
+
46
+ Internal collections (`auth-session`, `auth-account`, `auth-verification`) are `managed: true` — the API blocks write operations (POST/PATCH/DELETE return 403). Better Auth owns the data. `auth-user` and `auth-api-keys` are NOT managed: `auth-user` allows admin CRUD, `auth-api-keys` blocks writes via access control and routes deletion through the dedicated `/api/auth/api-keys/:id` endpoint with ownership checks.
47
+
48
+ ## Critical: Session and Role Handling
49
+
50
+ ### No Cookie Cache
51
+
52
+ The Better Auth session config intentionally has **no `cookieCache`**. This was removed because:
53
+
54
+ - Cookie cache stores session data (including `role`) in a signed cookie
55
+ - When a user's role changes (e.g., setup flow creates admin), the cached cookie retains the stale role
56
+ - All subsequent requests read stale `role: 'user'` from the cookie instead of `role: 'admin'` from the DB
57
+ - This causes auth collection access checks (`req.user?.role === 'admin'`) to fail with 403
58
+
59
+ **Never re-enable `cookieCache`** unless you also solve stale role propagation (e.g., invalidate sessions on role change).
60
+
61
+ ### Role Update Flow
62
+
63
+ The setup flow (`POST /setup/create-admin`) does:
64
+
65
+ 1. `auth.api.signUpEmail()` — creates user with `role: 'user'` (Better Auth default)
66
+ 2. `updateUserRolePostgres()` — SQL update to set `role: 'admin'`
67
+
68
+ The role column is an `additionalFields` entry in Better Auth's user config, not a native field. Better Auth reads it from the DB on every `getSession()` call (since there's no cookie cache), so role changes take effect immediately.
69
+
70
+ ### Seeding and `onConflict: 'skip'`
71
+
72
+ Seeds use `authUser()` helper which calls `signUpEmail()` + `adapter.update()` for role. But seeds are tracked by `seedId` — if the seed was already processed, `onConflict: 'skip'` returns early **without updating the role**. This means:
73
+
74
+ - Changing a seed's `role` from `'user'` to `'admin'` has no effect on existing databases
75
+ - You must either: (a) delete the `_seed_tracking` row for that seed, (b) use a new seedId, or (c) update the role via direct SQL
76
+
77
+ ## Sub-Plugin Pattern
78
+
79
+ Sub-plugins extend auth without modifying the core. Each sub-plugin provides:
80
+
81
+ ```typescript
82
+ export interface MomentumAuthSubPlugin {
83
+ name: string; // For logging
84
+ betterAuthPlugin: unknown; // The Better Auth plugin instance
85
+ collections?: CollectionConfig[]; // Managed collections to inject
86
+ userFields?: Field[]; // Fields to add to auth-user
87
+ sessionFields?: Field[]; // Fields to add to auth-session
88
+ }
89
+ ```
90
+
91
+ ### Creating a Sub-Plugin
92
+
93
+ Follow the `two-factor.ts` pattern:
94
+
95
+ 1. Create `plugins/my-plugin.ts`
96
+ 2. Define any managed collections with `defineCollection({ managed: true, ... })`
97
+ 3. Export a factory function returning `MomentumAuthSubPlugin`
98
+ 4. Use `admin: { hidden: true }` for internal collections users shouldn't see
99
+ 5. Set restrictive `access` rules — most auth data shouldn't be directly accessible
100
+
101
+ ### Sub-Plugin Collection Rules
102
+
103
+ - Use `auth-` prefix for slugs (e.g., `auth-two-factor`)
104
+ - Set `dbName` to match Better Auth's expected table name
105
+ - Always set `managed: true` — Better Auth owns the data
106
+ - Match field definitions exactly to Better Auth's schema
107
+
108
+ ## Browser-Safe Imports
109
+
110
+ The auth-collections are importable in browser code via:
111
+
112
+ ```typescript
113
+ import { BASE_AUTH_COLLECTIONS } from '@momentumcms/auth/collections';
114
+ ```
115
+
116
+ This path alias (`tsconfig.base.json`) points to `auth-collections.ts`, which only imports from `@momentumcms/core` (no Node.js dependencies). This is used by `momentumAdminRoutes()` for static route generation.
117
+
118
+ **Never add Node.js imports** (pg, better-auth, etc.) to `auth-collections.ts`.
119
+
120
+ ## Common Pitfalls
121
+
122
+ 1. **403 on auth collections after role change**: Role is stale. Check `req.user.role` — if it's `'user'` when it should be `'admin'`, the DB role wasn't updated or the session is stale. Clear cookies and re-login, or check the DB directly.
123
+
124
+ 2. **Seed doesn't update role**: Seeds with `onConflict: 'skip'` won't re-process. See "Seeding and onConflict" above.
125
+
126
+ 3. **New sub-plugin fields not appearing**: Better Auth needs the field in `additionalFields`. The plugin factory merges `userFields` into the auth config automatically — make sure the sub-plugin returns them.
127
+
128
+ 4. **Collection slug vs DB table name**: Auth collections use `auth-user` as the slug (for API routes, admin UI) but `user` as the DB table (`dbName`). The Drizzle adapter's `resolveTableName()` handles the mapping. Don't use raw table names in API calls.
129
+
130
+ 5. **E2E tests and role timing**: E2E fixtures use `signUpEmail()` + direct DB role update + fresh `signIn()`. The separate sign-in ensures the new session reads the correct role. Don't rely on the session from `signUpEmail()` after a role change.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Momentum CMS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # auth
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build auth` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test auth` to execute the unit tests via [Jest](https://jestjs.io).