How customers log in
Storefront authentication is deliberately simple: email + password, a
cryptographically signed session cookie, 7-day session. No OAuth, no SAML,
no "magic link" today. This page walks through every path a customer can
take to get from /store/{slug} to a signed-in session.
There are three entry points on the login page:
- Sign in — existing customer or contact with a password.
- Create one — self-serve registration as a brand-new customer.
- Set your password — for contacts who were invited by a staff member or a customer admin.
The login page
URL: /store/{slug}/login.
Layout:
- Company name at the top with the first-letter tile.
- Email + password inputs.
- Sign in button.
- Below, two secondary links:
Don't have an account? Create one→/store/{slug}/registerInvited as a contact? Set your password→/store/{slug}/setup-password
What Distribu checks on sign-in
When the form submits, Distribu tries two lookups in sequence:
- Primary customer login — find a customer in this company whose email matches and whose stored password matches the one provided.
- Contact login — if there's no customer match, look for a contact with that email and matching password, then resolve back to its parent customer.
If either succeeds, Distribu writes a customer_session cookie and
redirects to /store/{slug}/catalog. The cookie payload includes the
customer ID, company ID + slug, the contact role (if it was a contact
login), and the display name.
If the customer's status is BLOCKED, sign-in is rejected with:
Your account has been suspended. Please contact the store.
All other failures return the generic:
Invalid email or password.
(Deliberately vague — we don't confirm whether an email exists on the system.)
Session duration
Sessions last 7 days from the moment of sign-in. The cookie is:
HttpOnly— not accessible to JavaScript.Securein production.SameSite=Lax.- Cryptographically signed so it can't be forged.
There's no "Remember me" checkbox — every login gets a 7-day cookie.
After expiry, the customer is bounced back to /store/{slug}/login on
their next request.
Logging out
Every authenticated storefront page has a Sign out button in the
top-right header. Clicking it clears the customer_session cookie and
redirects to /store/{slug}/login. No confirmation dialog.
Self-registration (new customer)
URL: /store/{slug}/register.
This is how a new buyer becomes a customer without you lifting a finger:
- They open the storefront URL.
- Click Create one on the login page.
- Fill in: name, email, password (min 8 chars), confirm password.
- Submit.
On success, Distribu creates a new customer account with:
status = ACTIVEcreditLimit = 0- No price overrides.
- No shipping addresses (they'll add one before checkout).
Then it writes a session cookie and drops them at the catalog.
Errors they might see
Email already registered. Please sign in instead.— there's already a customer (or a pending contact) with this email on your store.Passwords don't match.— the confirm field didn't match.Password must be at least 8 characters.— shorter than 8.This store isn't accepting new customer sign-ups right now. Please contact the store.— shown when registration is disabled for the company (not a toggle in the dashboard today; we can flip it on request).
Invited contact flow
URL: /store/{slug}/setup-password.
When you add a contact to a customer (see Customer contacts) or when a customer admin adds their colleague, Distribu creates a contact with no password. The contact is in a pending state — they exist, but they can't sign in yet.
To finish onboarding, they go to /store/{slug}/setup-password (the Set
your password link on the login page) and:
- Enter the email that was invited.
- Enter a new password.
- Confirm it.
Distribu looks up the contact by email, securely stores the new password, and signs them in — dropping them at the catalog with the same session cookie as a normal login.
If no matching pending contact exists, they see:
No pending account found for this email. Ask your account admin to invite you.
If the contact already has a password set, the same error fires — once
you've set a password, use /store/{slug}/login like everyone else.
Deep-linking the email field
The setup-password URL accepts an ?email= query param which prefills the
email field. If you paste the link into an invite email — Distribu
doesn't send invitations automatically, so you'd send your own — use:
https://your-domain.com/store/{slug}/setup-password?email=them@acme.com
This saves the contact one step.
Two tiers of storefront users
A logged-in session can belong to either a primary customer or a contact. They look the same in the UI — same cart, same checkout, same orders list — but they differ in two ways:
| Primary customer | Contact | |
|---|---|---|
| Record type | Customer account | Contact under a customer |
| Roles | N/A | ADMIN, BUYER, VIEWER |
| Can place orders | Yes | ADMIN + BUYER yes; VIEWER no |
| Can manage contacts | N/A (dashboard only) | N/A (dashboard only) |
Session contactRole | null | ADMIN / BUYER / VIEWER |
The header shows the role as a small gray pill next to the contact's name — so a contact always knows which hat they're wearing.
VIEWER contacts see the full catalog and cart UI but get this error when
they try to check out:
Your account does not have permission to place orders.
Password management
Today:
- Changing your password — not supported from the storefront UI. There's no "Account" page. If a customer needs to reset theirs, you (the distributor) can set a new password on their behalf via the REST API, or delete and reinvite the contact.
- Forgot password — no self-serve reset flow. Same workaround. Email reset is on the roadmap.
- Password storage — passwords are securely hashed with a per-customer salt; plaintext is never stored.
If you need either of those and they're blocking a customer, email support@distribu.app — we can manually reset a password for any customer or contact on your account.
Session boundaries
A customer signed into acme-distributors who opens beta-foods/catalog
will be bounced to beta-foods/login — sessions are scoped to a single
slug. If they need to shop from both stores they need separate accounts
(different email is fine; even the same email works, since customers are
scoped per-company).
