xtablo-source/docs/superpowers/specs/2026-04-18-client-password-invite-flow-design.md
2026-04-18 09:03:53 +02:00

11 KiB

Client Password Invite Flow

Date: 2026-04-18 Status: Draft Supersedes: docs/superpowers/specs/2026-04-15-client-magic-links-design.md

Overview

The current client invite flow is built around a magic-link callback path. That model is no longer the target.

apps/clients should become a normal password-based portal for invited client users. Invitations should bootstrap account access, not serve as the long-term authentication mechanism.

The revised flow is:

  • a client is invited by email from app.xtablo.com
  • if this email has not completed onboarding yet, the email contains a one-time password setup link
  • the client sets a password once
  • that setup link becomes invalid immediately after successful use
  • all later access goes through a normal login form in apps/clients
  • clients can reset their password themselves from the client login page

Client accounts are reused across multiple tablos by email. If a client who already has a password-based account is invited to another tablo, they should receive an access notification email instead of another password-setup link.

Problem Statement

The current magic-link callback flow creates the wrong steady-state model for the client portal.

Current issues

  1. The invite email behaves as an authentication mechanism instead of a one-time onboarding step.
  2. apps/clients does not provide a standard login form for later access.
  3. The current callback-style flow is a poor fit for a client portal meant to feel like a stable authenticated product.
  4. Reinviting the same email is awkward because the current model is centered around link acceptance rather than an account reused across multiple tablos.
  5. The current flow does not express a strong boundary between apps/main users and apps/clients users.

Goals

  • Replace callback-style magic-link onboarding with one-time password setup
  • Make apps/clients a normal email/password application after onboarding
  • Reuse one client account per email across multiple tablos
  • Allow self-service password reset from the client login page
  • Support direct notification links to clients.xtablo.com/tablo/:tabloId
  • Keep clients restricted to apps/clients only
  • Reuse the main login page visual design through a shared auth UI surface

Non-Goals

  • Permanent bearer links that grant direct tablo access without authentication
  • Self-service client signup without invitation
  • Creating a separate custom auth system outside Supabase
  • Granting client-portal users access to apps/main
  • Preserving the current callback-based onboarding as the primary flow

Hard Requirements

  • Client users must not have access to apps/main
  • The password-setup link must be one-time use
  • The setup link must become invalid immediately after successful password creation
  • The same email must map to one reusable client account across multiple tablos
  • Existing onboarded clients invited to another tablo must receive an access notification email, not a new setup link
  • The notification email must link directly to clients.xtablo.com/tablo/:tabloId

Chosen Approach

Keep Supabase as the underlying authentication provider, but move invitation control into an invite lifecycle owned by the backend.

The backend creates or reuses a client auth user by email, grants access to the target tablo, and then chooses between two email modes:

  • onboarding email with a one-time setup token
  • access notification email for an already-onboarded client

apps/clients becomes a normal authenticated app with:

  • a login page
  • a one-time set-password page
  • a forgot-password flow
  • protected routes that redirect unauthenticated users to login and then resume their intended destination

User Classes And App Boundary

The system should treat main-app users and client-portal users as distinct user classes.

Main-app users

  • collaborators
  • internal users
  • users who are allowed to access app.xtablo.com

Client-portal users

  • external client users invited to tablos
  • users who are allowed to access clients.xtablo.com
  • users who must not be able to use apps/main

Boundary rule

Sharing auth UI does not mean sharing authorization.

Client accounts must be rejected by apps/main even if they hold a valid authenticated session. This boundary must be enforced in backend authorization as well as frontend routing.

Auth Model

apps/clients becomes a normal password-based portal.

Steady state

  • one client account per email
  • reused across multiple tablos
  • normal email/password login after onboarding
  • standard self-service password reset via "mot de passe oublié"

Invite role

The invite email is no longer the long-term access credential. It is only the bootstrap mechanism for first-time password setup.

Invite Lifecycle

Invite creation should branch based on whether the email already belongs to an onboarded client account.

First invite for an email without a password-based client account

  • create or reuse the client auth user for that email
  • create or confirm the tablo access grant
  • create a one-time setup token
  • send a setup email to clients.xtablo.com

Later invite for an existing onboarded client account

  • create or confirm the tablo access grant
  • do not create a setup token
  • send a "you now have access" notification email

This keeps onboarding single-use while allowing account reuse across many tablos.

End-To-End Flows

First-time onboarding flow

  1. Admin invites a client from app.xtablo.com.
  2. Backend creates or reuses the client auth user.
  3. Backend grants access to the target tablo.
  4. Backend creates a one-time setup token.
  5. Email sends a setup URL into clients.xtablo.com.
  6. Client opens the link and validates the token.
  7. Client sets a password.
  8. Backend invalidates the token immediately.
  9. Client is signed in and redirected into the client portal.

Additional tablo access for an already-onboarded client

  1. Admin invites the same email to another tablo.
  2. Backend reuses the same client account.
  3. Backend grants access to the target tablo.
  4. Email sends a notification link to clients.xtablo.com/tablo/:tabloId.
  5. If the client already has a session, the tablo opens directly.
  6. If not authenticated, apps/clients redirects to login and returns to that tablo after successful login.

Frontend Design

apps/clients should expose three auth surfaces:

  • LoginPage
  • SetPasswordPage
  • existing authenticated portal routes

LoginPage

Requirements:

  • minimal standalone auth screen
  • visually matches the main login page
  • built from a shared auth UI package instead of importing directly from apps/main
  • email and password fields
  • forgot-password entry point
  • no self-service signup

SetPasswordPage

Requirements:

  • dedicated route for one-time invite setup
  • validates token before allowing password creation
  • handles invalid, expired, and already-used tokens clearly
  • on success, invalidates token and transitions into an authenticated client session

Protected route behavior

  • unauthenticated access to clients.xtablo.com/tablo/:tabloId redirects to login
  • login preserves and resumes the intended destination
  • fallback destination remains the client tablo list if no target route was captured

Shared Auth UI

The login page in apps/clients should look like the main login page, but this should be done through extraction, not duplication.

Recommended ownership split:

  • shared package owns auth shell, layout, form framing, banners, and visual treatment
  • apps/main and apps/clients own submit handlers, route targets, and app-specific copy

This keeps visual parity durable without coupling apps/clients directly to apps/main internals.

Backend Design

client_invites should remain the lifecycle/control record, but its meaning changes.

Previous role

  • pending invite accepted through callback-style magic-link flow

New role

  • one-time password-setup authorization record for first-time onboarding

Backend responsibilities

POST /client-invites/:tabloId

  • create or reuse client auth user by email
  • create or confirm tablo access
  • decide whether this email needs onboarding or only an access notification
  • send the correct email type

Token validation endpoint:

  • used by SetPasswordPage
  • verifies token exists, is pending, and is still valid

Password setup completion endpoint:

  • verifies token again
  • sets password for the underlying auth user
  • invalidates token immediately
  • completes the onboarding transition cleanly

Admin visibility and cancellation endpoints:

  • remain available for operational control
  • cancelling a pending setup invite invalidates that setup path immediately

Authorization Model

Authorization must reflect the split between apps.

Required behavior

  • client-portal users can access apps/clients resources they were granted
  • client-portal users cannot use apps/main flows
  • main-app authorization cannot assume that every authenticated user is a main-app user

This must be enforced on the backend, not only in the frontend shell.

Error Handling

Frontend

SetPasswordPage must handle:

  • invalid token
  • expired token
  • already-used token
  • password policy failure

LoginPage must handle:

  • wrong credentials
  • reset email sent state
  • reset failure

Protected routes must:

  • redirect unauthenticated users to login
  • preserve intended destination
  • resume navigation after login

Backend

Invite creation must distinguish:

  • first-time onboarding invite
  • additional-access notification

Token completion must fail cleanly on:

  • expired token
  • reused token
  • cancelled token

Testing Strategy

API tests

  • first invite for a new client creates a setup token and sends setup email
  • second invite for an already-onboarded client skips setup token creation and sends access notification
  • setup token can be used exactly once
  • expired or reused setup token is rejected
  • client-only accounts are rejected by main-app authorization paths

Frontend tests

  • login page renders through shared auth UI and submits email/password flow
  • forgot-password flow is reachable from the client login page
  • set-password page handles success, invalid token, expired token, and reused token states
  • protected tablo/:tabloId route redirects to login and resumes correctly after authentication
  • access notification deep-link opens the intended tablo after login

Manual verification

  • first invite email for a new client leads to one-time setup, then normal login
  • second invite for the same client leads to access notification only
  • clients.xtablo.com/tablo/:tabloId works both with and without an existing session
  • client user cannot enter app.xtablo.com

Migration Notes

  • the current apps/clients/src/pages/AuthCallback.tsx route should be removed or reduced to legacy compatibility once this flow is live
  • existing frontend code that assumes invitation equals magic-link acceptance should be replaced with setup-token and login flows
  • because the feature is not yet live, no legacy client-user migration path is required