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 renaming
the issuer or moving this service stays a configuration change and not a code change.