NestJS 11, TypeScript, port 3000. Four routes, one credential store, and the only place in the platform where a password exists in a readable form.
Identity is concentrated here on purpose. Every other service verifies a signature and reads claims, which is cheap and needs no shared state. Only this service hashes a password, only this service holds the hash, and only this service signs. If a team spreads that responsibility across two services, both of them now need the credential store and both of them are now a place a password can leak.
Registration returns no tokens. The client logs in afterwards, so that an unauthenticated route cannot mint a session. Refresh consumes the presented token and issues a new pair, and presenting a consumed token is treated as theft: every refresh token for that user is revoked.
src/. The token pair is drawn as an output
because it is the thing the rest of the platform depends on, even though it leaves in an HTTP response.| Route | Guard | Returns | Notes |
|---|---|---|---|
POST /auth/register | none | 201 with the user | Links a user to an existing trading account. It does not create the account, and it issues no tokens. |
POST /auth/login | ThrottlerGuard | 200 with a token pair | Every failure returns the same AUTH-401 body. Repeated failures from one address return 429. |
POST /auth/refresh | none | 200 with a new pair | The presented token stops working immediately. Presenting a consumed token revokes every refresh token for that user. |
GET /auth/me | JwtAuthGuard | 200 with the user | Identity comes from the verified token, never from a query parameter or a client-controlled header. |
Five attempts a minute per address does not inconvenience someone who mistyped a password, and it does stop an unattended script working through a password list. It is a blunt control: an attacker with many addresses is unaffected, which is why it sits alongside argon2 rather than instead of it.
The guard is applied to login only. A global throttler would also cap /auth/me, which
the frontend calls on every page load, and the first symptom would be a dashboard that intermittently
logs the user out.
The Trade REST API verifies the signature with the same JWT_SECRET and reads the claims.
It does not call this service to check a token, because a network call per request would make identity an
availability dependency for trading. Configure the issuer check to accept any issuer, so that swapping
the Node auth stub for this service in Sprint 8 is a configuration change and not a code change.