# Authentication API > **Prefix:** `/api/v1/user` and `/oauth` > **Permission:** All endpoints in this module are **PUBLIC** (no JWT required) except `userinfo` and `logout` --- ## POST `/api/v1/user/send-code` Send OTP code to mobile number. **Permission:** `PUBLIC` ### Request Body ```json { "mobile": "09123456789" } ``` | Field | Type | Required | Validation | |-------|------|----------|------------| | `mobile` | string | ✅ | Format: `09XXXXXXXXX` (11 digits) | ### Response `200` ```json { "success": true, "data": { "uuid": "550e8400-e29b-41d4-a716-446655440000", "message": "کد تأیید ارسال شد" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_001` | 422 | Invalid mobile format | | `ERR_AUTH_004` | 429 | OTP rate limit exceeded | --- ## POST `/api/v1/user/verify-code` Verify OTP code. Returns whether this is a new or existing user. **Permission:** `PUBLIC` ### Request Body ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000", "code": "123456" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `uuid` | string | ✅ | UUID returned from `send-code` | | `code` | string | ✅ | 6-digit OTP | ### Response `200` ```json { "success": true, "data": { "message": "کد تأیید شد", "is_new_user": false, "uuid": "550e8400-e29b-41d4-a716-446655440000" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_002` | 401 | Invalid OTP code | | `ERR_AUTH_003` | 401 | OTP expired | | `ERR_VALIDATION_002` | 422 | Missing required field | --- ## POST `/api/v1/user/register` Complete registration for new users (called only when `is_new_user: true`). **Permission:** `PUBLIC` ### Request Body ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000", "real_name": "علی احمدی" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `uuid` | string | ✅ | Verified UUID from `verify-code` | | `real_name` | string | ❌ | User's full name | ### Response `201` ```json { "success": true, "data": { "message": "ثبت‌نام با موفقیت انجام شد", "uuid": "550e8400-e29b-41d4-a716-446655440000" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_002` | 422 | Missing uuid | | `ERR_CONFLICT_001` | 409 | User already registered | --- ## POST `/api/v1/user/login` Login with mobile number and password (for users who set a password). **Permission:** `PUBLIC` ### Request Body ```json { "mobile_number": "09123456789", "password": "mypassword" } ``` | Field | Type | Required | |-------|------|----------| | `mobile_number` | string | ✅ | | `password` | string | ✅ | ### Response `200` ```json { "success": true, "data": { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", "refresh_token": "def50200..." } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_005` | 401 | Wrong credentials | | `ERR_AUTH_006` | 403 | Account suspended | | `ERR_AUTH_004` | 429 | Too many attempts | --- ## POST `/oauth/token` Exchange verified UUID for JWT access token. **Permission:** `PUBLIC` ### Request Body ```json { "grant_type": "mobile", "uuid": "550e8400-e29b-41d4-a716-446655440000" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `grant_type` | string | ✅ | Must be `"mobile"` | | `uuid` | string | ✅ | UUID from verified OTP flow | ### Response `200` ```json { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", "refresh_token": "def50200..." } ``` > JWT payload: `{ username: mobile_number, roles: [...], iat, exp }` > Access token TTL: **1 hour** | Refresh token TTL: **30 days** (stored in Redis) ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_002` | 400 | Invalid or expired UUID | --- ## POST `/oauth/token/refresh` Refresh expired JWT using refresh token. **Permission:** `PUBLIC` ### Request Body ```json { "refresh_token": "def50200..." } ``` ### Response `200` ```json { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", "refresh_token": "def50200..." } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Invalid or expired refresh token | --- ## GET `/oauth/userinfo` Get authenticated user info — extended with multi-context support. **Permission:** `AUTH` — requires valid JWT ### Headers ``` Authorization: Bearer ``` ### Response `200` ```json { "success": true, "data": { "id": 4766, "uuid": "550e8400-e29b-41d4-a716-446655440000", "mobile_number": "09123456789", "realName": "دکتر وحید درویشی", "status": 1, "roles": ["ROLE_USER", "ROLE_DOCTOR"], "primary_role": "doctor", "db_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966", "db_key": "hmac-sha256-hash...", "context": { "type": "doctor", "db_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966", "name": "مطب شخصی دکتر وحید درویشی", "role": "doctor" }, "available_contexts": [ { "type": "doctor", "db_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966", "name": "مطب شخصی دکتر وحید درویشی", "role": "doctor" }, { "type": "clinic", "db_uuid": "clinic-uuid-...", "name": "کلینیک سلامت", "role": "doctor" } ] } } ``` | فیلد | نوع | توضیح | |------|-----|-------| | `primary_role` | string | نقش اصلی: `admin` \| `clinic` \| `doctor` \| `secretary` \| `user` | | `db_uuid` | string\|null | UUID موجودیت فعال (null = هنوز context انتخاب نشده) | | `db_key` | string\|null | `HMAC-SHA256(db_uuid, APP_SECRET)` برای اعتبارسنجی | | `context` | object\|null | context فعال انتخاب‌شده | | `available_contexts` | array | همه محیط‌های کاری قابل انتخاب | **قانون `primary_role`** (اولویت‌بندی): - `ROLE_ADMIN` → `"admin"` - `ROLE_CLINIC` → `"clinic"` - `ROLE_DOCTOR` → `"doctor"` - `ROLE_SECRETARY` → `"secretary"` - بقیه → `"user"` **قانون `db_uuid`**: - اگر یک context وجود دارد: خودکار فعال می‌شود - اگر چند context وجود دارد و کاربر هنوز انتخاب نکرده: `null` — frontend باید صفحه انتخاب نشان دهد - پس از `POST /api/v1/auth/switch-context`: برابر context انتخاب‌شده ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Invalid or missing token | --- ## POST `/api/v1/auth/switch-context` تغییر محیط کاری فعال — باید از لیست `available_contexts` انتخاب شود. **Permission:** `AUTH` ### Request Body ```json { "db_uuid": "clinic-uuid-..." } ``` | فیلد | نوع | Required | توضیح | |------|-----|----------|-------| | `db_uuid` | string (UUID) | ✅ | UUID محیط کاری از لیست `available_contexts` | ### Response `200` ```json { "success": true, "data": { "db_uuid": "clinic-uuid-...", "db_key": "new-hmac-hash...", "context": { "type": "clinic", "db_uuid": "clinic-uuid-...", "name": "کلینیک سلامت", "role": "doctor" } } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_AUTH_001` | 401 | Missing or invalid token | | `ERR_AUTH_006` | 403 | `db_uuid` در لیست context های این کاربر نیست | | `ERR_VALIDATION_001` | 422 | `db_uuid` ارسال نشده | --- ## Notification Mobile (OTP) Endpoints for setting/verifying a separate SMS notification number for doctors and clinics. This number receives appointment SMS notifications instead of the account login mobile. > **Permission:** All 4 endpoints require `IS_AUTHENTICATED_FULLY` (JWT). --- ## POST `/api/v1/notification-mobile/request-otp` Request an OTP code to verify a new notification mobile number. **Permission:** `AUTH` ### Request Body ```json { "target": "doctor", "new_mobile": "09123456789" } ``` | Field | Type | Required | Validation | |-------|------|----------|------------| | `target` | string | ✅ | `doctor` or `clinic` | | `new_mobile` | string | ✅ | Format: `09XXXXXXXXX` | ### Response `200` ```json { "success": true, "data": { "message": "کد تأیید ارسال شد", "expires_in": 300 } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_001` | 422 | Invalid `target` or mobile format | | `ERR_NOT_FOUND_001` | 404 | No doctor/clinic profile found for this user | ### Notes - Previous unused OTPs for the entity are deleted before creating a new one - OTP is 6-digit, expires in 5 minutes - Sends via SMS asynchronously --- ## POST `/api/v1/notification-mobile/verify` Verify the OTP and save the notification mobile number. **Permission:** `AUTH` ### Request Body ```json { "target": "doctor", "otp_code": "123456" } ``` | Field | Type | Required | |-------|------|----------| | `target` | string | ✅ `doctor` or `clinic` | | `otp_code` | string | ✅ 6-digit code | ### Response `200` ```json { "success": true, "data": { "notification_mobile": "09123456789", "message": "شماره اعلان با موفقیت ذخیره شد" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_001` | 422 | Invalid target / OTP expired / wrong code | | `ERR_NOT_FOUND_001` | 404 | No OTP request found or profile not found | --- ## GET `/api/v1/notification-mobile/{target}` Get the current notification mobile for the authenticated user. **Permission:** `AUTH` **Path param:** `target` — `doctor` or `clinic` ### Response `200` ```json { "success": true, "data": { "notification_mobile": "09123456789" } } ``` - Returns `null` for `notification_mobile` if not set ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_NOT_FOUND_001` | 404 | No profile found for this user | --- ## DELETE `/api/v1/notification-mobile/{target}` Remove the notification mobile number. **Permission:** `AUTH` **Path param:** `target` — `doctor` or `clinic` ### Response `200` ```json { "success": true, "data": { "message": "شماره اعلان حذف شد" } } ``` ### Errors | Code | HTTP | Description | |------|------|-------------| | `ERR_NOT_FOUND_001` | 404 | No profile found for this user | --- ## POST `/oauth/logout` Invalidate the refresh token (stored in Redis). **Permission:** `AUTH` ### Request Body ```json { "refresh_token": "def50200..." } ``` | Field | Type | Required | |-------|------|----------| | `refresh_token` | string | ❌ | ### Response `200` ```json { "success": true, "data": { "message": "با موفقیت خارج شدید" } } ``` --- ## POST `/api/v1/pre-registration` Submit a pre-registration request (doctor or clinic). Public endpoint — no auth required. **Permission:** Public ### Request Body ```json { "type": "independent_doctor", "name": "دکتر علی احمدی", "mobile": "09121234567", "info": "متخصص داخلی، ۱۰ سال سابقه" } ``` | Field | Type | Required | Notes | |-------|------|----------|-------| | `type` | string | ✅ | `independent_doctor` \| `doctor_with_clinic` \| `clinic_manager` | | `name` | string | ✅ | min 2 chars | | `mobile` | string | ✅ | 10–15 chars | | `info` | string | ❌ | specialty, address, etc. | **Type meanings:** | Value | Roles granted on approval | Entities created | |-------|--------------------------|-----------------| | `independent_doctor` | `ROLE_DOCTOR` | Doctor | | `doctor_with_clinic` | `ROLE_DOCTOR` + `ROLE_CLINIC` | Doctor + Clinic | | `clinic_manager` | `ROLE_CLINIC` | Clinic | ### Response `200` ```json { "success": true, "data": { "uuid": "...", "status": "pending" } } ``` ### Error Codes | Code | HTTP | Meaning | |------|------|---------| | `VALIDATION_ERROR` | 422 | Invalid type / mobile / name | | `DUPLICATE_REQUEST` | 409 | Pending request already exists for this mobile | --- ## POST `/api/v1/user/otp-login` ورود با کد OTP تأییدشده — **فقط کاربران موجود**، کاربر جدید ایجاد نمی‌شود. **Permission:** `PUBLIC` ### Request Body ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `uuid` | string | ✅ | UUID از `verify-code` (باید قبلاً verify شده باشد) | ### Response `200` ```json { "access_token": "eyJ...", "refresh_token": "...", "token_type": "Bearer", "expires_in": 3600, "refresh_token_expires_in": 2592000 } ``` ### Error Codes | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_002` | 422 | uuid ارسال نشده | | `ERR_AUTH_002` | 400 | uuid نامعتبر یا تأیید نشده | | `ERR_AUTH_003` | 400 | OTP منقضی شده | | `ERR_AUTH_005` | 401 | کاربری با این شماره یافت نشد | --- ## POST `/api/v1/user/reset-password` تغییر رمز عبور با تأیید هویت از طریق OTP. **Permission:** `PUBLIC` ### Request Body ```json { "uuid": "550e8400-e29b-41d4-a716-446655440000", "new_password": "newpass123" } ``` | Field | Type | Required | Validation | |-------|------|----------|------------| | `uuid` | string | ✅ | UUID از `verify-code` (باید قبلاً verify شده باشد) | | `new_password` | string | ✅ | حداقل ۶ کاراکتر | ### Response `200` ```json { "success": true, "data": { "message": "رمز عبور با موفقیت تغییر یافت" } } ``` ### Error Codes | Code | HTTP | Description | |------|------|-------------| | `ERR_VALIDATION_001` | 422 | uuid یا new_password نادرست/ناقص | | `ERR_AUTH_002` | 400 | uuid نامعتبر یا تأیید نشده | | `ERR_NOT_FOUND_001` | 404 | کاربری با این شماره یافت نشد |