Files
clinicpro/docs/api/auth.md
T

15 KiB
Raw Blame History

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

{
  "mobile": "09123456789"
}
Field Type Required Validation
mobile string Format: 09XXXXXXXXX (11 digits)

Response 200

{
  "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

{
  "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

{
  "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

{
  "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

{
  "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

{
  "mobile_number": "09123456789",
  "password": "mypassword"
}
Field Type Required
mobile_number string
password string

Response 200

{
  "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

{
  "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

{
  "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

{
  "refresh_token": "def50200..."
}

Response 200

{
  "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 <token>

Response 200

{
  "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...",
    "doctor_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966",
    "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": "clinic",
        "doctor_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966"
      }
    ]
  }
}
فیلد نوع توضیح
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) برای اعتبارسنجی
doctor_uuid string|null UUID دکتر — ثابت است حتی در context کلینیک که db_uuid برابر UUID کلینیک است. برای کاربران غیر دکتر: null
context object|null context فعال انتخاب‌شده
available_contexts array همه محیط‌های کاری قابل انتخاب

قانون primary_role (اولویت‌بندی):

  • ROLE_ADMIN"admin"
  • ROLE_CLINIC"clinic"
  • ROLE_DOCTOR"doctor"
  • ROLE_SECRETARY"secretary"
  • بقیه → "user"

قانون context.role — نقشی که در آن محیط کاری فعال است:

  • context مطب شخصی دکتر: "doctor"
  • context کلینیک که دکتر عضو آن است: "clinic" (read-only — دسترسی به اطلاعات کلینیک)
  • context کلینیک که دکتر صاحب آن است: "clinic"
  • context منشی: "secretary"

نکته frontend: پس از switchContext، primaryRole در store باید از context.role آپدیت شود.

قانون 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

{
  "db_uuid": "clinic-uuid-..."
}
فیلد نوع Required توضیح
db_uuid string (UUID) UUID محیط کاری از لیست available_contexts

Response 200

{
  "success": true,
  "data": {
    "db_uuid": "clinic-uuid-...",
    "db_key": "new-hmac-hash...",
    "context": {
      "type": "clinic",
      "db_uuid": "clinic-uuid-...",
      "name": "کلینیک سلامت",
      "role": "clinic",
      "doctor_uuid": "a6ef5d29-38b8-4e69-b1ef-27a304696966"
    }
  }
}

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

{
  "target": "doctor",
  "new_mobile": "09123456789"
}
Field Type Required Validation
target string doctor or clinic
new_mobile string Format: 09XXXXXXXXX

Response 200

{
  "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

{
  "target": "doctor",
  "otp_code": "123456"
}
Field Type Required
target string doctor or clinic
otp_code string 6-digit code

Response 200

{
  "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: targetdoctor or clinic

Response 200

{
  "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: targetdoctor or clinic

Response 200

{
  "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

{
  "refresh_token": "def50200..."
}
Field Type Required
refresh_token string

Response 200

{
  "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

{
  "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 1015 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

{
  "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

{ "uuid": "550e8400-e29b-41d4-a716-446655440000" }
Field Type Required Description
uuid string UUID از verify-code (باید قبلاً verify شده باشد)

Response 200

{
  "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

{
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "new_password": "newpass123"
}
Field Type Required Validation
uuid string UUID از verify-code (باید قبلاً verify شده باشد)
new_password string حداقل ۶ کاراکتر

Response 200

{
  "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 کاربری با این شماره یافت نشد