Authentication and accounts
Every server call except registration, login, and the bootstrap claim runs behind authentication. The server verifies a bearer token, resolves it to a user, and authorizes the call against that user’s role.
The account
Section titled “The account”One account holds the identity of one person:
| Field | Meaning |
|---|---|
username |
Unique, 1–30 characters, chosen at registration |
email |
Unique, used as a login identifier |
password_hash |
bcrypt hash; NULL means the account has never been claimed |
role |
Exactly one role, guest by default |
timezone |
IANA timezone such as Europe/Bratislava, used to display temporal values |
phone_country |
ISO 3166-1 alpha-2 country such as SK, used to interpret national phone numbers |
The server stores only a bcrypt hash, never the password. Hashing and verification run on a small bounded worker pool, so password processing has a hard concurrency ceiling and can refuse work when it is saturated.
Registration and login
Section titled “Registration and login”Register creates an account on the guest role. The role is never taken from the request, because letting a caller choose their own role would let anyone register as an administrator. Every new account therefore starts with nothing and receives permissions only through an administrative act.
Registration accepts a username, an email, a password, a timezone, and a phone country. The username and email must be different, the password must match its confirmation, and the identifiers must not already be in use.
Login accepts either the username or the email as the identifier. A successful login returns:
- an access token and its type (
Bearer); - the user id, username, and role;
- the user’s timezone and phone country;
- an authorization snapshot listing every permission the role currently holds.
The snapshot is computed at login time. A token keeps working until it expires even if grants change afterward; see Roles and permissions for how changes take effect.
Both login and registration are rate-limited per source address: 30 login attempts and 5 registrations per 20-second window. A caller that exceeds a limit receives a resource-exhausted error.
Access tokens
Section titled “Access tokens”A successful login mints a signed JWT:
- algorithm: HS256, signed with the
JWT_SECRETenvironment value; - claims:
sub(the user id),iat, andexp; - lifetime: 24 hours.
Tokens are stateless. The server keeps no session table and does not validate tokens against the database on every call; it only verifies the signature and expiry.
Clients send the token in every request header:
Authorization: Bearer <token>Every gRPC service except AuthService itself requires this header. AuthService must stay reachable without a token because registration and login are how a caller obtains one; its administrative calls authenticate themselves internally.
Bootstrapping a fresh deployment
Section titled “Bootstrapping a fresh deployment”A fresh database ships with two accounts that have no password: superadmin and admin. An account without a password cannot log in. The only call it accepts is SetInitialPassword, which sets a password once and turns the account into an ordinary one.
The password_hash IS NULL guard makes the call a one-time claim rather than a password reset: an account that already has a password refuses it.
Reading your own authorization
Section titled “Reading your own authorization”GetAuthorization returns the caller’s role and its effective permission snapshot without requiring a fresh login. SetTimezone updates the caller’s stored timezone so temporal values are rendered in the right zone.
Continue with permissions
Section titled “Continue with permissions”Roles and permissions explains the two-plane permission model: structural versus data roles, grantable objects, role inheritance, and the audit log.