39 lines
2.2 KiB
Markdown
39 lines
2.2 KiB
Markdown
# Security
|
|
|
|
## Dashboard sessions
|
|
|
|
- Passwords hashed with bcrypt (cost 12), at least 8 characters; emails stored lowercased.
|
|
- Sign-in sets two httpOnly, SameSite=Lax cookies (Secure in production): a JWT access token for 15 minutes, and a
|
|
refresh token for 30 days, sent only to `/api/v1/auth`. No script can read either.
|
|
- A refresh token works once: refreshing revokes it and issues a new pair. Signing out revokes it.
|
|
- Every request looks the user up: a deleted user, a changed role or a forced sign-out (`sessionsRevokedAt`) applies at
|
|
once instead of when the access token expires.
|
|
- Sign-in answers the same, in the same time, for an unknown email and a wrong password.
|
|
|
|
## Terminals
|
|
|
|
- Paired with a single-use registration code that expires after 10 minutes.
|
|
- Token: `kt_` + 48 random bytes; only its SHA-256 hash is stored, and it is shown once. It expires after 90 days.
|
|
- Unpairing on the dashboard stops the token at once.
|
|
|
|
## Roles
|
|
|
|
- `manager` runs the venue: tabs, customers, reminders, products and stock.
|
|
- `admin` also manages the venue's users and terminals.
|
|
- `superadmin` runs KoolTap and belongs to no venue: the admin page (`/admin` endpoints), and any venue they view, where
|
|
they act as its admin. The database enforces that superadmins, and only they, have no venue. Only a superadmin can make
|
|
someone a superadmin; the first one is made with `node dist/cli/superadmin.js <email> --create "<name>" <password>`
|
|
(or by promoting a user). Viewing a venue is recorded in that venue's audit log, and every admin action is audited.
|
|
|
|
Every venue route checks that the user belongs to the venue in the URL (or is a superadmin). Product pictures are
|
|
public by id, a SHA-256 hash that can't be guessed.
|
|
|
|
## API
|
|
|
|
- Every body and query is validated with a zod schema; unknown fields are dropped.
|
|
- Helmet headers; CORS only for the dashboard's origin.
|
|
- Rate limits: 10 sign-ins or pairings per minute per client, 100 requests per minute otherwise. Terminal syncs are
|
|
not limited (they are authenticated by the device token).
|
|
- Errors are `{ error: { code, message } }`; internal errors are logged and never shown.
|
|
- The realtime socket authenticates with the session cookie and only carries topic names, never data.
|