@nextsparkjs/ai-workflow 0.1.0-beta.100 → 0.1.0-beta.102

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.
@@ -331,6 +331,90 @@ CORE_USERS = {
331
331
  | `team-tmt-001` | Everpoint Labs | Primary test team |
332
332
  | `team-tmt-002` | Ironvale Global | Secondary test team |
333
333
 
334
+ ## Registration Control
335
+
336
+ Registration behavior is configurable at the theme level via `auth` in `app.config.ts`.
337
+
338
+ ### Registration Modes
339
+
340
+ | Mode | Email Signup | Google OAuth Signup | Email Login | Google Login | Signup Page |
341
+ |------|-------------|-------------------|-------------|-------------|-------------|
342
+ | `open` (default) | Yes | Yes | Yes | Yes | Visible |
343
+ | `domain-restricted` | No | Only allowed domains | Yes | Yes | Hidden |
344
+ | `invitation-only` | Via invite | Via invite | Yes | Configurable | Invite only |
345
+
346
+ ### Theme Configuration
347
+
348
+ ```typescript
349
+ // contents/themes/my-theme/config/app.config.ts
350
+
351
+ // Option 1: Open registration (default, no config needed)
352
+ auth: {
353
+ registration: { mode: 'open' },
354
+ }
355
+
356
+ // Option 2: Only Google OAuth for specific domains
357
+ auth: {
358
+ registration: {
359
+ mode: 'domain-restricted',
360
+ allowedDomains: ['nextspark.dev', 'mycompany.com'],
361
+ },
362
+ }
363
+
364
+ // Option 3: Invitation-only (integrates with single-tenant teams mode)
365
+ auth: {
366
+ registration: { mode: 'invitation-only' },
367
+ }
368
+ ```
369
+
370
+ ### Types
371
+
372
+ ```typescript
373
+ // packages/core/src/lib/config/types.ts
374
+ type RegistrationMode = 'open' | 'domain-restricted' | 'invitation-only'
375
+
376
+ interface AuthConfig {
377
+ registration: {
378
+ mode: RegistrationMode
379
+ allowedDomains?: string[] // For 'domain-restricted' mode
380
+ }
381
+ providers?: {
382
+ google?: { enabled?: boolean }
383
+ }
384
+ }
385
+ ```
386
+
387
+ ### Helper Functions
388
+
389
+ ```typescript
390
+ import {
391
+ isRegistrationOpen,
392
+ isDomainAllowed,
393
+ isGoogleAuthEnabled,
394
+ shouldBlockSignup,
395
+ getPublicAuthConfig,
396
+ } from '@/core/lib/auth/registration-helpers'
397
+ ```
398
+
399
+ ### Enforcement Points
400
+
401
+ 1. **Route handler** (`app/api/auth/[...all]/route.ts`): Blocks email signup for `domain-restricted` and `invitation-only` modes
402
+ 2. **Database hook** (`auth.ts` → `databaseHooks.user.create.before`): Validates email domain for `domain-restricted` mode; blocks signup in `invitation-only` mode when team exists
403
+ 3. **Signup page** (`app/(auth)/signup/page.tsx`): Redirects to `/login` for `domain-restricted` and `invitation-only`
404
+ 4. **LoginForm**: Hides Google OAuth button and signup link based on mode
405
+ 5. **SignupForm**: Hides Google button when disabled
406
+
407
+ ### Client-Side Config
408
+
409
+ Use `PUBLIC_AUTH_CONFIG` (from `config-sync.ts`) in client components. This strips `allowedDomains` for security.
410
+
411
+ ```typescript
412
+ import { PUBLIC_AUTH_CONFIG } from '@/core/lib/config/config-sync'
413
+
414
+ // PUBLIC_AUTH_CONFIG.registration.mode → 'open' | 'domain-restricted' | 'invitation-only'
415
+ // PUBLIC_AUTH_CONFIG.providers.google.enabled → boolean
416
+ ```
417
+
334
418
  ## Authentication Flows
335
419
 
336
420
  ### Email/Password Registration
@@ -506,6 +590,7 @@ export default async function DashboardPage() {
506
590
  | `INVALID_API_KEY` | 401 | API key invalid or expired |
507
591
  | `INVALID_CREDENTIALS` | 401 | Wrong email/password |
508
592
  | `EMAIL_NOT_VERIFIED` | 401 | User hasn't verified email |
593
+ | `SIGNUP_RESTRICTED` | 403 | Invitation-only / single-tenant mode (registration requires invite) |
509
594
  | `INSUFFICIENT_PERMISSIONS` | 403 | User lacks required scope |
510
595
  | `TEAM_CONTEXT_REQUIRED` | 400 | Missing x-team-id header |
511
596
  | `SESSION_EXPIRED` | 401 | Session no longer valid |