Authentication
Nova Print uses three separate auth schemes against the same Django /api/ surface. Do not mix tokens across clients.
| Scheme | Who | Obtain | Send on requests |
|---|---|---|---|
| Staff / ops JWT | Admin dashboard, warehouse, design catalog manage | POST /api/token/ | Authorization: Bearer <access> |
| POS access key | POS terminal | POST /api/pos-auth/ | X-POS-Access-Key: <key> |
| Ecommerce customer JWT | Storefront account APIs | POST /api/ecommerce/auth/login/ or …/register/ | Authorization: Bearer <access> (customer token) |
Root mounts: backend-main/config/urls.py. DRF defaults: JWT auth + IsAuthenticated in backend-main/config/settings.py (REST_FRAMEWORK, SIMPLE_JWT). CORS allows x-pos-access-key in the same settings file.
Staff / ops JWT (SimpleJWT)
| Endpoint | Method | Notes |
|---|---|---|
/api/token/ | POST | CustomTokenObtainPairView (backend-main/users/views.py) — username/password; only users who pass can_access_admin_panel |
/api/token/refresh/ | POST | Standard SimpleJWT refresh |
/api/users/forgot-password/ | POST | Public; throttled |
/api/users/reset-password/ | POST | Public |
/api/users/me/ | GET | Authenticated self |
/api/users/change_password/ | POST | Authenticated self |
/api/users/ | CRUD | Admin (IsAdminUser) user management via UserViewSet |
Token claims added in CustomTokenObtainPairSerializer (backend-main/users/serializers.py): email, username, role, is_staff, is_superuser.
Defaults (settings): access 60 minutes, refresh 7 days, ROTATE_REFRESH_TOKENS=True.
Role gates (users.permissions)
Defined in backend-main/users/permissions.py. Profile roles: stock, design, print, accounts (UserProfile.ROLE_CHOICES (backend-main/users/models.py)).
| Permission | Who passes |
|---|---|
IsAdminUser | is_staff or is_superuser |
IsStockUser | admin or role stock |
IsDesignUser | admin or role design |
IsPrintUser | admin or role print |
IsAccountsUser | admin or role accounts |
IsAdminOrAccounts | admin or role accounts |
can_access_admin_panel | staff/superuser or any non-empty profile role |
POST /api/token/ rejects users who cannot access the admin panel (AuthenticationFailed).
POS access key
- Optional public lookup:
GET /api/pos-info/<slug>/(POSPublicInfoView(backend-main/pos/views.py)). - Login:
POST /api/pos-auth/with{ "slug", "password" }→{ access_key, expires_at, pos_name, shop_name, … }. - Key is stored on
PointOfSale.access_key/access_key_expires_at(12 hours from issue). - Subsequent POS APIs: header
X-POS-Access-Key. Helpers:get_pos_from_access_keyinpos/views.py(backend-main/pos/views.py) andcustomerlink/views.py(backend-main/customerlink/views.py).
Lockout: 5 failed password attempts → locked=True (403). Expired / invalid key → 401. Many POS views set permission_classes = [AllowAny] and enforce the key inside the view (DRF JWT is not used for the terminal session).
SSE / EventSource cannot set custom headers: use query ?access_key= (and for admin streams ?token=) — see SSE and realtime.
Ecommerce customer JWT
Separate from staff tokens. Issued by create_customer_tokens (backend-main/ecommerce/auth.py) with claims auth_type=ecommerce_customer, customer_id, email, name.
Validated by EcommerceCustomerAuthentication (backend-main/ecommerce/auth.py) (subclass of SimpleJWT JWTAuthentication). Staff tokens fail this check.
| Endpoint | Auth |
|---|---|
/api/ecommerce/auth/register/, /login/ | Public → returns access + refresh |
/api/ecommerce/auth/me/ | Customer JWT |
/api/ecommerce/addresses/…, /orders/ (account) | Customer JWT |
| Catalog / checkout / Razorpay verify | Mostly AllowAny (see Ecommerce API) |
Password reset for customers: /api/ecommerce/auth/forgot-password/, /reset-password/ (own token generator, not staff reset).
Quick client map
- Admin app → staff JWT → warehouse (
IsStockUser), admin orders, reports, ecommerce manage, design-catalog manage. - POS app → access key →
/api/pos-*,/api/pos-orders/,/api/customer-links/. - Storefront → public catalog + optional customer JWT for account/reviews.
- Checkout chat → public
pos_slug+link_code(no key).