#Sign-in and account access
Module id: MOD-AUTH
Doc profile: system
Platform auth for TradeForce: email/password registration, verification, sign-in, and password reset, plus the post-auth redirects that send each role into onboarding or their home surface. Target stack is Supabase Auth with a profiles row; Bubble native LogIn / SignUp / SendPasswordResetEmail / ResetPassword are the evidence for behavior, not the runtime provider.
#Purpose
Contractors and companies must be able to create an account, prove email ownership, sign in, and recover a forgotten password so every later authenticated journey can run. Admins sign in on the same surface and go straight to the admin home. This module owns the public auth UI on / and /reset-password, the session client, profile defaults used for routing, and the redirect matrix. It does not own onboarding wizard content, workspace chrome, or shift/marketplace pages.
#Scope boundaries
| In scope | Out of scope |
|---|---|
| Index auth modes: sign-in, sign-up, reset request, verify message panel | Onboarding step content and uploads (MOD-ONBOARDING) |
| Reset-password confirm page | Header / side menus (MOD-SHELL) |
Supabase Auth cutover + profiles fields used for redirects |
Social IdPs not evidenced on index / reset_pw |
Post-login and already-logged-in PageLoaded redirects from / |
Company / contractor / admin product pages after landing |
| ACC-AUTH-SURFACE preserve-vs-replace activation decision (ADR) | Inventing a third activation path beyond that ADR |
#Actors
| Actor | System behavior |
|---|---|
| Visitor (signed out) | Sees auth modes on / via nav (signin default, signup, reset, verify). Can register as Contractor or Company only. |
| Contractor | After active + onboarding complete, lands on contractor shifts; incomplete onboarding goes to onboarding. |
| Company | After onboarding complete, lands on company shifts. PageLoaded company home does not require active; post-login still sends inactive users to the verify panel first. |
| Admin | PageLoaded or post-login with role admin → admin home. Admin is not offered in the signup role dropdown. |
| Session client | Browser Supabase client (anon key only) + session helpers; never ships service-role secrets. |
#Auth composition
Auth is one public surface with mode switching, not four separate apps. The index page is a split layout (branding + form column). Mode is the nav query param from NavigationIndex. Casing matters: the source writes and compares the option display — SignIn | SignUp | Reset | Verify — not the lowercase db_value; every index visibility condition is GetParamFromUrl("nav") = OptionValue(navigationindex, …):display, and MOD-ONBOARDING follows the same convention with nav=Contractor|Company. Mode names are written lowercase below as internal mode ids; the target must parse nav case-insensitively so live links from Bubble emails and bookmarks keep working, whichever casing it emits. Primary submit shares one button; its workflow branches on nav. After credentials succeed, redirects read profiles.active, profiles.onboarding_complete, and profiles.role.
Bubble evidence for composition and redirects is listed under Traceability / IR evidence.
#Primary journeys
Journeys are reconstructed from index and reset_pw workflows (triggers → auth actions → ChangePage / SendEmail). Where activation continues on the onboarding page, that page body is out of this module’s implement scope but the activation contract is named here because signup email deep-links into it.
#J1 — Sign in and route by profile
- Visitor opens
/(default ornav=signin) and sees Sign In with Email, Password, Forgot Password?, and Sign Up link. - Visitor submits valid email and password; the app calls sign-in and loads the profile used for routing.
- If the profile is inactive, the app stays on
/withnav=verify; if active and onboarding incomplete, it goes to onboarding; otherwise it routes by role to company shifts, contractor shifts, or admin. - Invalid credentials show an inline/page error and do not navigate.
#J2 — Sign up, verify email, and activate
- Visitor opens
/?nav=signup, chooses Contractor or Company (Admin excluded), enters email and a password of at least 8 characters, and submits. - The app creates the auth user, stores role on the profile with approved false and active false, sends the verification email, and shows the verify panel (
nav=verify, signup tab copy). - Visitor follows the activation link from the email (onboarding URL with
active=yesand role innavwhen preserving Bubble semantics, or the ADR-chosen Supabase confirm path when replacing). - Password shorter than 8 characters or signup failure blocks success and keeps the user on the form with a clear error.
#J3 — Reset password from email link
- Visitor opens
/?nav=reset, enters email, and chooses Send reset link; the app requests a reset email and shows the reset-specific verify panel. - Visitor opens
/reset-passwordfrom the email with a recovery session and enters New password and Confirm new password. - When both fields match, the app updates the password and navigates to onboarding (not back to sign-in).
- Mismatched passwords or expired recovery show a clear error and do not change the password.
#J4 — Already signed-in visit to the auth index
- A user who already has a session opens
/. - PageLoaded rules redirect: non-admin with active and onboarding incomplete → onboarding; admin → admin; contractor with active and onboarding complete → contractor shifts; company with onboarding complete → company shifts (company rule does not require active).
#Navigation contracts
URL values below are internal mode ids; on the wire the source spells them SignIn / SignUp / Reset / Verify (option display) — see Auth composition.
| Label / trigger | Behavior |
|---|---|
Default / or nav=signin |
Sign In mode: email + password; hide role dropdown and verify panel. |
| Sign Up link | Sets nav=signup. |
| Forgot Password? | Sets nav=reset. |
| Already have an account? / Back to login | Sets nav=signin. |
| After successful signup | nav=verify and signup tab → “Verify your email” / activate-account body. |
| After reset request | nav=verify and reset tab → “Reset Password” / reset-link body. |
| Post-login inactive | nav=verify (and verify tab) on index. |
| Recovery email | Lands on /reset-password; success → /onboarding. |
#Vertical slices (decompose)
| Item | Product outcome |
|---|---|
| AUTH-FOUNDATION | Supabase Auth + profiles (role, active, onboarding_complete, approved), RLS own-row, session helpers; ACC-AUTH-SURFACE ADR for provider cutover and signup activation preserve vs replace. Infrastructure enabler — not a user journey by itself. |
| AUTH-SIGNIN | Sign-in form, password reveal, Forgot/Sign Up mode links, post-login redirect matrix, already-logged-in PageLoaded redirects. |
| AUTH-SIGNUP | Role dropdown (Contractor | Company), password length gate, signUp + profile fields, verify panel after signup, verification email per ACC-AUTH-SURFACE. |
| AUTH-RESET | Reset request on index, reset verify copy, /reset-password confirm-when-match, then onboarding. |
#Side-effect contracts
| Trigger | Side effect | Owner |
|---|---|---|
| Sign in success | Session JWT; redirect from profile flags/role | AUTH-SIGNIN |
| Sign up success | Auth user + profiles row (active false, approved false, role set); verification email; verify panel | AUTH-SIGNUP + AUTH-FOUNDATION |
| Activation (preserve) | Email deep-link to onboarding with active=yes; onboarding PageLoaded sets active true when param present and currently inactive |
ACC-AUTH-SURFACE + MOD-ONBOARDING page load |
| Activation (replace) | Supabase confirm-email / ADR-named flip of profiles.active |
ACC-AUTH-SURFACE ADR |
| Reset request | Reset email with redirectTo /reset-password; verify panel |
AUTH-RESET |
| Reset confirm (match) | Password update; navigate to onboarding | AUTH-RESET |
| Index PageLoaded while logged in | ChangePage to onboarding / admin / contractor / company per predicates below | AUTH-SIGNIN |
#Post-login redirect matrix (after LogIn)
Ordered from the login workflow actions:
activefalse → index with verify nav/tabactivetrue and onboarding incomplete → onboarding- role company and onboarding complete → company shifts
- role contractor and onboarding complete → contractor shifts
- role admin → admin
#PageLoaded predicates on index (already logged in)
| Rule | Predicate (product terms) | Destination |
|---|---|---|
| Onboarding gate | logged in, role ≠ admin, onboarding incomplete, active true | Onboarding |
| Admin | role = admin | Admin |
| Contractor home | logged in, active true, role contractor, onboarding complete | Contractor shifts |
| Company home | logged in, role company, onboarding complete (no active check) | Company shifts |
#Style and fidelity
Auth uses the auth_flow shell (section → container → form card). Transcribe visible copy and data-bubble-id on migrated roots (page, form card, primary button, reset card). Shared tokens come from the app-level stylesheet that SHELL-LAYOUT (MOD-SHELL) delivers out of IR styles (ir/slices/styles____part_all.json — 49 styles, 8 color tokens, 1 font token); this cycle's module map has no separate design-system module, so do not defer a token to one. Narrow layout: center the auth column around ≤640px on index; tighten padding around ≤768px on reset.
#Failure modes
| Case | Expected behavior |
|---|---|
| Wrong password / unknown user | User-visible error; stay on sign-in; no redirect. |
| Signup duplicate email / provider error | User-visible error; do not open verify panel. |
| Password < 8 characters on signup | Block submit; show “Password must be at least 8 characters long.” |
| Reset email failure | Error on reset form; do not pretend success. |
| Confirm passwords differ | Do not call update password; show validation error. |
| Expired recovery session | Message + path back to /?nav=reset. |
| Inactive user signs in | Verify panel, not a role home. |
| ACC-AUTH-SURFACE undecided | Implement blocked on activation and credential cutover (HQ-1). |
#Needs human clarification
| Id | Question | Why unclear (plain + IR) | Impact |
|---|---|---|---|
| HQ-1 | For ACC-AUTH-SURFACE, do we preserve Bubble signup activation (confirm email off + custom Verify email deep-link to onboarding with active=yes) or replace it with Supabase Auth confirm-email, and what is the cutover plan for existing Bubble passwords? |
Plain: Behavior of signup, verify email, and when profiles.active becomes true is encoded clearly in the export, but the target provider and cutover are a product/platform choice the metadata cannot make. IR: SignUp sets send_confirm_email false and writes role + approve false only (bTHpx1); SendEmail subject “Verify” body links onboarding?nav={role}&active=yes (bTHqT1); onboarding PageLoaded sets active true when param active=yes and currently inactive (bTHsZ1 / bTHse1). ACC-AUTH-SURFACE is a needs_adr decision (preserve vs replace + forced reset / dual-run / IdP cutover), not catalog evidence; the record is docs/adr/ADR-002-acc-auth-surface.md, status open. |
blocks_implement |
#Traceability
| Product concept | Target (route / component) | Bubble evidence |
|---|---|---|
| Auth index page | /, AuthIndexPage |
bTGYf |
| Reset password page | /reset-password, ResetPasswordPage |
AAL |
| Sign-in submit | SignInForm + session signIn |
Workflow bTHjl1 / action bTHop1 (LogIn) |
| Post-login redirects | postLoginRedirect |
Actions bTNMx0, bTHov1, bTNNE0, bTNNJ0, bTNNL0 |
| Sign-up submit | SignUpForm + signUp |
Workflow bTHDg0 / bTHpx1 (SignUp) |
| Verify email send | Edge or Auth templates per ADR | bTHqT1 (SendEmail) |
| After signup → verify panel | /?nav=verify |
bTHqJ1 |
| Reset request | ResetRequestForm |
Workflow bTHjd1 / bTHqz1 |
| Reset confirm | ResetPasswordForm |
Workflow bTHrF1 / bTHrP1 → bTHrQ1 → onboarding |
| PageLoaded redirects | Index load guards | bTHqV1, bTKna, bTKnB, bTKnO |
| Nav modes | navMode / ?nav= (parse case-insensitively) |
navigationindex — db_values signin/signup/reset/verify, URL carries displays SignIn/SignUp/Reset/Verify |
| Roles | profiles.role enum |
role / bTHAB0 contractor, bTHAC0 company, bTIrk admin |
| Profile flags | profiles.active, onboarding_complete, approved |
active__boolean, onboarding_boolean, approve1_boolean, role_option_role |
| Auth provider cutover | ADR ACC-AUTH-SURFACE (needs_adr) |
bTHpx1, bTHqT1, bTHsZ1 / bTHse1 (activation chain); cutover options are ADR-only |
#IR evidence
#Module evidence roots
bTGYf— index page (/pages/bTGbC), auth modes and login/signup/reset/verify UI + workflowsAAL— reset_pw page (/pages/AAW), new/confirm password + ResetPassword
Supporting segments: ir/slices/bTGbC____root.json, ir/slices/AAW____root.json.
#Path-key ↔ inventory id aliases
Prefer inventory ids in evidence_refs; path keys are locators when chasing the sanitized export.
| Path key / pointer | Inventory id | Notes |
|---|---|---|
/pages/bTGbC |
bTGYf |
index |
/pages/AAW |
AAL |
reset_pw |
/pages/bTGbC/workflows/bTHoj1 |
bTHjl1 |
Sign-in ButtonClicked (nav not signup/reset/verify) |
/pages/bTGbC/workflows/bTHjk1 |
bTHDg0 |
Sign-up ButtonClicked |
/pages/bTGbC/workflows/bTHjj1 |
bTHjd1 |
Reset-request ButtonClicked |
/pages/AAW/workflows/bTHrK1 |
bTHrF1 |
Confirm when passwords equal |
/pages/bTGbC/workflows/bTHqZ1 |
bTHqV1 |
PageLoaded → onboarding |
/pages/bTGbC/workflows/bTKnh |
bTKna |
PageLoaded → admin |
/pages/bTGbC/workflows/bTKnN |
bTKnB |
PageLoaded → contractor shifts |
/pages/bTGbC/workflows/bTKnZ |
bTKnO |
PageLoaded → company shifts |
/pages/bTHDr0/workflows/bTHsd1 |
bTHsZ1 / action bTHse1 |
Activation sets active__boolean true (MOD-ONBOARDING page; cited for ACC chain only) |
#Signup activation (chased)
bTHpx1SignUp:send_confirm_emailfalse; changesrole_option_role← role dropdown,approve1_boolean← false; does not setactive__boolean.bTHqT1SendEmail: subject “Verify”, sender “TradeForce”, body deep-linksonboarding?nav=+ current user’s role display +&active=yes.bTHqJ1ChangePage → index withnav= display ofnavigationindex/verify(i.e.Verify) andtab= display of the signup value — both url_parameters interpolate:display, notdb_value.- Preserve path: onboarding PageLoaded when URL
active=yesand user still inactive →ChangeThingactive__booleantrue (bTHse1). Replace path requires ADR naming the Supabase confirm mechanism instead.
#Privacy & authorization
- Target: RLS on
profiles— authenticated users read/update own row only; service role confined to migrations/triggers; SPA uses anon key + user JWT only. - Bubble privacy roles for wider app pages are out of this module; auth pages are public entry points. No
privacy_role*matrix is required for the public auth forms themselves beyond the profiles RLS contract above.
#Option sets used for routing / UI
navigationindex: db_valuessignin,signup,reset,verify; displaysSignIn,SignUp,Reset,Verify— the URL carries the display (theverifyvalue also carries atabattribute used for reset vs signup message copy).role:contractor,company,admin— signup dropdown excludes admin.