# Authentication & Authorization Negareh API supports two actor types — **users** (customers) and **admins** (back-office). Both authenticate via **phone OTP** and receive **JWT access tokens** with **refresh tokens**. ## Overview ``` ┌─────────────┐ OTP request ┌─────────────┐ │ Client │ ──────────────────► │ Auth API │ │ │ ◄────────────────── │ (SMS/Redis)│ └─────────────┘ OTP code └─────────────┘ │ │ │ OTP verify │ └──────────────────────────────────► │ ▼ ┌─────────────────────────┐ │ Access JWT + Refresh │ │ token (hashed in DB) │ └─────────────────────────┘ ``` ## OTP flow ### User (public) | Endpoint | Method | Description | |----------|--------|-------------| | `/public/auth/otp/request` | POST | Send OTP to phone (creates user on verify) | | `/public/auth/otp/verify` | POST | Verify OTP → returns tokens + user profile | | `/public/auth/refresh` | POST | Exchange refresh token for new token pair | ### Admin | Endpoint | Method | Description | |----------|--------|-------------| | `/admin/auth/otp/request` | POST | Send OTP (phone must exist in `admins` table) | | `/admin/auth/otp/verify` | POST | Verify OTP → returns tokens + admin profile | | `/admin/auth/refresh` | POST | Exchange refresh token for new token pair | ### OTP details - Phone numbers are normalized to local format via `normalizePhoneToLocal()` (`09xxxxxxxxx`). - OTP codes are 5-digit numeric values stored in **Redis** with TTL (`OTP_EXPIRATION_TIME`, default 240 seconds). - OTP request endpoints are rate-limited (`@Throttle`: 3 requests per 180 seconds). - SMS delivery uses SMS.ir patterns (configured via `SMS_*` env vars). ### Request / verify DTOs ```typescript // Request OTP { "phone": "09362532122" } // Verify OTP { "phone": "09362532122", "otp": "12345" } ``` ## JWT tokens ### Access token - Signed with `JWT_SECRET`. - Expiration: `JWT_EXPIRATION_TIME` (seconds). - Sent as `Authorization: Bearer `. **User payload:** ```typescript { userId: string } ``` **Admin payload:** ```typescript { adminId: string } ``` ### Refresh token - Opaque 64-character hex string (not a JWT). - Stored **hashed** (SHA-256) in the `refresh_tokens` table. - Expiration: `REFRESH_TOKEN_EXPIRE` (days). - Rotated on each refresh — old token is deleted in the same transaction as the new pair. - Type field distinguishes `user` vs `admin` tokens. ### Refresh response shape ```json { "accessToken": { "token": "...", "expire": 1710000000000 }, "refreshToken": { "token": "...", "expire": 1711000000000 } } ``` `expire` values are Unix timestamps in milliseconds. ## Guards ### `AuthGuard` (users) - Applied with `@UseGuards(AuthGuard)` on public routes that require a logged-in user. - Verifies JWT from `Authorization` header. - Sets `request.userId` from the token payload. - Extract identity in controllers with `@UserId()`. ### `AdminAuthGuard` (admins) - Applied with `@UseGuards(AdminAuthGuard)` on admin routes. - Verifies JWT and sets `request.adminId`. - Extract identity with `@AdminId()`. - Reads `@Permissions(...)` metadata from the handler/class for RBAC checks. ## Role-based access control (RBAC) Admins have a **role** with attached **permissions**. Permission names are defined in `PermissionEnum` (e.g. `view_orders`, `create_invoice`, `manage_admins`). ### Declaring required permissions ```typescript @Get('admin/orders') @UseGuards(AdminAuthGuard) @Permissions(PermissionEnum.VIEW_ORDERS) findAllAsAdmin(@Query() dto: FindOrdersDto) { return this.orderService.findOrdersAsAdmin(dto); } ``` The `@Permissions()` decorator stores required permission keys in metadata. `AdminAuthGuard` reads them via `Reflector`. > **Note:** Permission enforcement against the database/cache is partially implemented. The guard structure and decorators are in place; ensure permission checks are active before relying on them in production. ## Route prefixes Routes are namespaced by audience in the controller path (not a global prefix): | Prefix | Audience | Guard | |--------|----------|-------| | `public/` | End users | `AuthGuard` (when protected) | | `admin/` | Back-office | `AdminAuthGuard` | Unauthenticated routes use `public/auth/...` or `admin/auth/...` without guards. ## WebSocket auth Admin WebSocket connections use `WsAdminAuthGuard` in the notifications module. Follow the same JWT verification pattern as HTTP admin auth. ## Environment variables | Variable | Purpose | |----------|---------| | `JWT_SECRET` | Signing key for access tokens | | `JWT_EXPIRATION_TIME` | Access token TTL (seconds) | | `REFRESH_TOKEN_EXPIRE` | Refresh token TTL (days) | | `OTP_EXPIRATION_TIME` | OTP cache TTL (seconds) | | `REDIS_URI` / `REDIS_HOST` | OTP and cache storage | | `SMS_BASE_URL`, `SMS_API_KEY`, `SMS_PATTERN_OTP` | SMS delivery | ## Security practices - Refresh tokens are hashed at rest; raw tokens are only returned once to the client. - Token refresh runs inside a database transaction to prevent race conditions. - Rate limiting on OTP and refresh endpoints reduces brute-force risk. - Never log tokens, OTP codes, or secrets. - Use HTTPS in production; CORS is enabled with `credentials: true` for cookie support if needed. ## Client integration checklist 1. Request OTP with normalized Iranian mobile number. 2. Verify OTP and store both `accessToken.token` and `refreshToken.token`. 3. Send `Authorization: Bearer ` on protected requests. 4. On 401, call the refresh endpoint with the refresh token. 5. Replace stored tokens with the new pair from the refresh response.