# Security

## Authentication

- Passwords are hashed with **bcrypt** (Laravel default)
- Login supports email, username, or contact number
- Session fixation is prevented by `Illuminate\Session\Middleware\AuthenticateSession`
- API tokens are hashed via Laravel Sanctum — raw token is only shown once on issue

---

## Authorization

- All actions checked via Spatie Permission (`@can`, `Gate::allows()`)
- Super-admin bypasses all checks via `Gate::before()`
- Tenant isolation: every query is scoped to `tenant_id` via global model scope
- Accessing another tenant's resources returns 403

---

## Encrypted Fields

The following fields are stored encrypted in the database:
- `payment_method_configurations.gateway_credentials` — EasyPaisa/JazzCash API keys
- `payment_gateway_transactions.request_payload` — raw gateway request
- `payment_gateway_transactions.response_payload` — raw gateway response
- `payment_gateway_transactions.webhook_payload` — raw webhook payload

---

## Webhook Security

All incoming webhook callbacks (EasyPaisa, JazzCash) are verified:
1. Extract the signature from the request header
2. Compute HMAC of the raw body using the configured secret
3. Compare — reject with HTTP 400 if mismatch
4. Only then update payment records

---

## Rate Limiting

- Login: 10 attempts per minute per IP (API)
- API: 60 requests per minute per token

---

## Input Validation

All form inputs are validated via Laravel Form Request classes before processing.
File uploads are type-checked and size-limited.

---

## ULID Primary Keys

All resource IDs in URLs are ULIDs — non-sequential, non-guessable. No integer
IDs are ever exposed in URLs or API responses.

---

## Sensitive Data Handling

- CNIC numbers are stored but never logged in activity logs
- Payment proof images are stored in private storage (not publicly accessible)
- Session logs record IP and user agent for audit purposes only
