83 lines
3.8 KiB
Markdown
83 lines
3.8 KiB
Markdown
# SciMesh userservice
|
|
|
|
Authentication service for SciMesh, in Go on PostgreSQL. It owns user accounts
|
|
and issues the JWTs the coordinator trusts. It is a **separate bounded context**
|
|
from the coordinator: its own database, its own binary. The only thing shared
|
|
between the two services is the JWT signing secret.
|
|
|
|
The versioned external contract is
|
|
[`docs/user-service-api-contract.md`](../docs/user-service-api-contract.md).
|
|
|
|
Built as a modular monolith following Clean Architecture — one binary, four
|
|
layers, dependencies pointing strictly inward:
|
|
|
|
```
|
|
infra config, DB pool, clock, HTTP server ← drivers
|
|
transport HTTP handlers + JWT middleware ← incoming
|
|
storage SQL repository ← outgoing
|
|
usecase Register / Login + PORTS (interfaces) ← application rules
|
|
domain User, Role, invariants ← business rules
|
|
auth bcrypt hasher, HS256 JWT issuer ← crypto adapters
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
| Method | Path | Auth | Purpose |
|
|
|--------|---------------------------|--------------|---------------------------------------------|
|
|
| GET | `/health` | none | Liveness probe (checks the database) |
|
|
| POST | `/register` | none | Create an account (always role `user`) |
|
|
| POST | `/login` | none | Verify credentials, return a signed JWT |
|
|
| GET | `/me` | Bearer JWT | Return the caller's own account |
|
|
| POST | `/users/{id}/verify` | Bearer admin | Grant the trusted-contributor badge |
|
|
| POST | `/users/{id}/unverify` | Bearer admin | Revoke the badge |
|
|
| POST | `/users/{id}/promote` | Bearer admin | Set the user's role to admin |
|
|
| POST | `/users/{id}/demote` | Bearer admin | Set the user's role back to user |
|
|
|
|
Two independent attributes live on an account:
|
|
|
|
- **`role`** — `user` or `admin`. Governs what you may do with your own jobs.
|
|
Registration always creates a `user`; promotion to `admin` is a manual
|
|
database operation, never a request.
|
|
- **`verified`** — a boolean trust badge, granted **only by an admin** (the
|
|
`/verify` endpoints above, 403 for anyone else). It tells the coordinator
|
|
whether this user's volunteer workers are trusted: a verified contributor's
|
|
results are accepted directly, an unverified one's must pass quorum
|
|
cross-checking. Defaults to false.
|
|
|
|
Both attributes ride in the JWT (`role`, `verified` claims), so the coordinator
|
|
reads them from the signed token without ever calling this service.
|
|
|
|
## How it connects to the coordinator
|
|
|
|
The coordinator never calls this service at runtime. A client logs in here, gets
|
|
a JWT, and presents it to the coordinator, which verifies the signature locally
|
|
with the same `JWT_SECRET` and reads `sub` (the user id) into `jobs.owner_id`.
|
|
|
|
That link is **off by default**: until the coordinator is given a matching
|
|
`JWT_SECRET`, it accepts only the shared worker token and stores `owner_id` as
|
|
NULL. Set the same secret (≥ 32 bytes, byte-for-byte identical) on both services
|
|
to turn it on.
|
|
|
|
## Run
|
|
|
|
```sh
|
|
# whole stack: Postgres + migrations + the service on :8081
|
|
make up
|
|
|
|
# or locally against your own Postgres
|
|
cp .env.example .env # then edit JWT_SECRET and DATABASE_URL
|
|
make run
|
|
```
|
|
|
|
## Verify
|
|
|
|
```sh
|
|
make test # unit tests
|
|
make check # vet, lint, race, integration, smoke — needs Docker
|
|
make smoke # end-to-end against a running service
|
|
```
|
|
|
|
Password hashing uses bcrypt (`golang.org/x/crypto/bcrypt`); the salt and cost
|
|
are embedded in the stored hash, so there is no separate salt column. Tokens are
|
|
HS256 (`github.com/golang-jwt/jwt/v5`).
|