Make email and phone unique per business, not platform-wide
One person could not work for two businesses, and signup and staff creation told anyone whether an email or phone was registered anywhere on the platform. - User email and phone are now unique per business: indexes on (businessId, email) and (businessId, phoneNumber). Duplicate checks in signup and branch-manager creation are scoped to the business, so they no longer reveal other businesses' users. - Login finds the email in every business and checks the password against each account. One match signs straight in; several return 300 with the businesses to choose from, and the login page shows a picker. A wrong password returns the same 401 as before and reveals nothing; failures count against each candidate account, and a correct password only resets the accounts it opens. - Platform-operator access now also needs a platformAdmin flag on the account. With per-business emails, an email match alone would have let any owner create a staff account with the operator's email and open the cross-tenant console. The flag is only set by the seed and the migration; removing the email from PLATFORM_ADMIN_EMAILS still revokes. - npm run migrate:identity flags today's operators (while each listed email still matches one account) and syncs the User indexes, dropping the old unique email_1 and phoneNumber_1. Checked against a database in the old shape; safe to re-run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+26
-2
@@ -131,9 +131,33 @@ prices read-only.
|
||||
mismatch.
|
||||
- The Products page always shows default prices.
|
||||
|
||||
## People in more than one business
|
||||
|
||||
Email and phone are unique **within a business**, not across the platform,
|
||||
so one person can work for two businesses. Each account has its own
|
||||
password and role.
|
||||
|
||||
- Login looks the email up in every business. If the password matches one
|
||||
account, it signs straight in; if it matches several, the login page asks
|
||||
which business. Someone with a wrong password learns nothing about which
|
||||
businesses an email belongs to, and a wrong guess never resets the
|
||||
failed-login count of another business's account.
|
||||
- Signup and staff creation only check for duplicates inside the business,
|
||||
so they no longer reveal whether an email or phone is registered
|
||||
elsewhere.
|
||||
- Platform-operator access now needs the `platformAdmin` flag on the
|
||||
account as well as the email in `PLATFORM_ADMIN_EMAILS`. Without the
|
||||
flag, any owner could create a staff account with the operator's email
|
||||
in their own business. The flag is only set by `npm run seed` and
|
||||
`npm run migrate:identity`, never through the app.
|
||||
|
||||
**Deploying this change:** run `npm run migrate:identity` straight after
|
||||
the deploy. It flags the existing operator accounts and swaps the old
|
||||
platform-wide unique indexes for per-business ones. Until it runs,
|
||||
operators cannot open the Platform console and a second business cannot
|
||||
reuse an email. It is safe to re-run.
|
||||
|
||||
## Still open
|
||||
- **Global email and phone uniqueness.** One person cannot work for two
|
||||
businesses, and signup reveals whether an address is already registered.
|
||||
- **Isolation by convention.** Tenant filters are still written per route.
|
||||
The tests cover the sales, stock, transfer and access paths, not every
|
||||
route.
|
||||
|
||||
Reference in New Issue
Block a user