Skip to content

Commit b20394a

Browse files
Merge branch 'main' into bobaivigitalpoint-ui/userapi-1347-1350
2 parents 9444147 + 6415e92 commit b20394a

61 files changed

Lines changed: 1866 additions & 236 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/api/auth-caching.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Auth API Redis caching
2+
3+
`GET /auth/me/dashboard`-style dashboard data (`AuthService.getDashboard`) is cached per user for 30 seconds through `AuthCacheService`, which uses the shared `CacheService` (Redis when configured).
4+
5+
- Key: `auth:dashboard:<userId>`; TTL 30s, so data can be up to 30 seconds stale.
6+
- Read-through only: cache errors, or no cache being available, fall back to computing the response, so caching can never fail a request.
7+
- Token, session and API-key checks are deliberately **not** cached, because revocation has to take effect immediately.

‎docs/api/auth-db-optimization.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Auth API database query optimisation
2+
3+
Changes in `AuthService`:
4+
5+
- **Refresh-token reuse detection** counted tokens in a family by fetching every row just to log the number; it now uses `count()`.
6+
- **Logout everywhere** fetched all of a user's blacklisted refresh tokens only for `.length`; it now uses `count()`.
7+
- **Dashboard** ran a query for the user's ten latest properties whose result was never used (removed), and fetched up to three API key rows to report their number. It now uses `apiKey.count()`, so `apiKeysCount` is the real number of keys rather than being capped at 3.

‎docs/api/auth-error-handling.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Auth API error handling
2+
3+
`POST /auth/password-reset/request` no longer fails when the reset email cannot be sent. Previously a mail-provider error propagated as a `500`, which let a caller tell registered addresses (500) from unknown ones (silent success). The failure is now caught and logged (`auth.password_reset_email_failed`, with the user id and a hash of the email, never the address or token), and the response is the same as for an unknown address.
4+
5+
The reset token is still created, so a retry from the user produces a fresh token and email.

‎docs/api/auth-health.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Auth API health check
2+
3+
`GET /auth/health` (no authentication) reports whether the Auth API can do its job.
4+
5+
- `200` with `{ status: "up", checkedAt, checks: { database: { status, latencyMs } } }`
6+
- `503` with the same shape and `status: "down"` when the database check fails or takes longer than 2 seconds.
7+
8+
Only the database is checked because sessions and refresh tokens depend on it. Error details are never returned. This is separate from the platform-wide `/healthz` and `/readyz` probes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Auth API injection hardening
2+
3+
`CredentialInjectionPipe` runs on `POST /auth/register`, `/auth/login` and `/auth/refresh` and answers `400` for:
4+
5+
- object-valued fields (e.g. `{"email": {"$ne": null}}`) and nested objects in arrays,
6+
- keys starting with `$` or containing `.`,
7+
- NUL bytes,
8+
- SQL comment or stacked-statement markers (`--`, `/*`, `; drop ...`) in free-text fields. Password, token and code fields are exempt because they may legitimately contain those characters.
9+
10+
Database access already goes through Prisma's parameterised queries; the pipe rejects hostile input before it reaches the service.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Auth API input sanitization
2+
3+
`RegisterDto` and `LoginDto` normalise input through class-transformer (the global `ValidationPipe` already runs with `transform: true`):
4+
5+
- `email`: Unicode NFKC normalisation, control characters removed, surrounding whitespace trimmed. The case is **not** changed, so existing accounts keep matching.
6+
- `firstName` / `lastName`: additionally strip HTML tags and collapse whitespace.
7+
- Passwords and tokens are never altered.
8+
9+
Sanitising happens before validation, so `" user@example.com "` is accepted as `user@example.com` and a name consisting only of tags becomes empty and fails `@IsNotEmpty`. Helpers live in `src/auth/sanitize.util.ts`.

‎docs/api/auth-metrics.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Auth API Prometheus metrics
2+
3+
Exposed on the existing `/metrics` endpoint:
4+
5+
| Metric | Type | Labels |
6+
| --- | --- | --- |
7+
| `auth_requests_total` | counter | `operation` (controller handler name), `outcome` (`success`, `client_error`, `server_error`) |
8+
| `auth_request_duration_seconds` | histogram | `operation` |
9+
10+
`AuthMetricsInterceptor` records both for every `AuthController` handler. Labels are bounded (a fixed set of handler names and three outcomes), so cardinality does not grow with user input. Example alert: rate of `auth_requests_total{outcome="server_error"}` above a threshold.

‎docs/api/auth-openapi.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Auth API OpenAPI documentation
2+
3+
The main `AuthController` operations now carry `@ApiOperation` summaries and `@ApiResponse` entries (success plus the documented 400/401/404/429 cases) so they appear described in Swagger: register, login, refresh, logout, logout-all, me, change-password, password-reset request/reset and verify-email.
4+
5+
Open the Swagger UI exposed by the app and look under the **Authentication** tag. Protected operations show the `access-token` bearer scheme through the existing `@ApiBearerAuth` decorators.

‎docs/api/auth-pagination.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Auth API pagination
2+
3+
`GET /auth/api-keys` accepts `page` (>= 1) and `limit` (1-100, default 20).
4+
5+
- With neither parameter the response is the plain array it has always been, so existing clients are unaffected.
6+
- With either, the response is `{ data, meta: { page, limit, total, totalPages, hasNext } }`, newest first, with `id` as a tie-breaker so pages are stable.
7+
- Invalid values (page 0, limit above 100, non-numbers) return `400`.

‎docs/api/auth-rate-limiting.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Auth API rate limiting
2+
3+
`AuthRouteRateLimitGuard` + `@AuthRouteLimit({ max, windowMs })` limit sensitive unauthenticated routes per client address (hashed before storage). Each client has its own counter per route, so one abusive client cannot lock out everyone else.
4+
5+
| Route | Limit |
6+
| --- | --- |
7+
| `POST /auth/register` | 5 per hour |
8+
| `POST /auth/refresh` | 30 per 15 min |
9+
| `POST /auth/password-reset/request` | 5 per hour |
10+
| `POST /auth/password-reset/reset` | 10 per hour |
11+
| `POST /auth/verify-email` | 10 per 15 min |
12+
| `POST /auth/email/resend` | 3 per hour |
13+
14+
Over the limit the response is `429` with a `Retry-After` header. Login keeps its existing account/IP lockout (`LoginRateLimitService`). Note: the `ENDPOINT_RATE_LIMITS` table is a single bucket shared by all clients, so it was not used here.

0 commit comments

Comments
 (0)