Skip to main content

Authentication

Nova Print uses three separate auth schemes against the same Django /api/ surface. Do not mix tokens across clients.

SchemeWhoObtainSend on requests
Staff / ops JWTAdmin dashboard, warehouse, design catalog managePOST /api/token/Authorization: Bearer <access>
POS access keyPOS terminalPOST /api/pos-auth/X-POS-Access-Key: <key>
Ecommerce customer JWTStorefront account APIsPOST /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)​

EndpointMethodNotes
/api/token/POSTCustomTokenObtainPairView (backend-main/users/views.py) — username/password; only users who pass can_access_admin_panel
/api/token/refresh/POSTStandard SimpleJWT refresh
/api/users/forgot-password/POSTPublic; throttled
/api/users/reset-password/POSTPublic
/api/users/me/GETAuthenticated self
/api/users/change_password/POSTAuthenticated self
/api/users/CRUDAdmin (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)).

PermissionWho passes
IsAdminUseris_staff or is_superuser
IsStockUseradmin or role stock
IsDesignUseradmin or role design
IsPrintUseradmin or role print
IsAccountsUseradmin or role accounts
IsAdminOrAccountsadmin or role accounts
can_access_admin_panelstaff/superuser or any non-empty profile role

POST /api/token/ rejects users who cannot access the admin panel (AuthenticationFailed).

POS access key​

  1. Optional public lookup: GET /api/pos-info/<slug>/ (POSPublicInfoView (backend-main/pos/views.py)).
  2. Login: POST /api/pos-auth/ with { "slug", "password" } → { access_key, expires_at, pos_name, shop_name, … }.
  3. Key is stored on PointOfSale.access_key / access_key_expires_at (12 hours from issue).
  4. Subsequent POS APIs: header X-POS-Access-Key. Helpers: get_pos_from_access_key in pos/views.py (backend-main/pos/views.py) and customerlink/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.

EndpointAuth
/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 verifyMostly 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).