Authentication & Authorization Middleware¶
Prism has eight auth middlewares (grouped into six logical layers — see Security Overview). Each guards a route family, checks a specific credential, validates against a specific datastore, and fails closed. This page documents each one against its source file, then gives a decision table and a selection flowchart.
Validation legend:
verified against source ·
representative, verify ·
security finding
All of these run after the pre-auth pipeline (bodyParser → CORS → traceIdMiddleware → requestLogger). See Request Lifecycle.
1. authMiddleware — staff / web + mobile¶
File: middlewares/authMiddleware.js
Credential: token header (primary) or password in body (bcrypt fallback). Branches on the source header (web default, else "mobile").
Datastore: MySQL webPool (config/mysqlPool.js). One join across users → roles → addo_account_mapping → addo_business → business_category, filtered by u.userId=? AND b.id=? and all status=1.
Validation order (web branch, authMiddleware.js:44-78):
1. Load the user+business row (returns full business profile used downstream).
2. isValid if token header === users.apiToken (current device token).
3. Else, if a token was supplied, check auth_tokens for a live multi-session token bound to that userId (older devices keep working after apiToken rotates). A token cannot be replayed against a different account.
4. Only if token auth fails entirely, fall back to bcrypt: inputs.password === db_password || bcrypt.compare(...). The expensive bcrypt never runs on normal token calls (deliberate perf fix — see the in-file comment).
Sets on req: req.body.created_by = userName (used by controllers for audit fields).
Failure: 403 invalid credentials on bad creds / no row; 500 fail-closed on any thrown error (DB down, pool exhausted) — authMiddleware.js:134-143.
Route families: /crm_api, /crm_api/v2 (selected), /stats (all), and most prism_routes (campaign_tag, digital_bill, loyalty_rewards staff routes).
![]()
passwordis read from the body (and, perrequestLogger, may arrive in the query string). See findings.
2. adminMiddleware — admin only¶
File: middlewares/adminMiddleware.js
Credential: token header or password body.
Datastore: MySQL via a fresh single connection per request (config/mysqlConnection.js), not the pool — the connection is explicitly .end()ed on every exit path.
Check: Gates on inputs.business_id == '1' (adminMiddleware.js:9) — the only thing that distinguishes "admin". Then the same user+business join as
authMiddleware; isValid = token === apiToken || password === db_password || bcrypt.compare(...).
Sets on req: req.body.created_by = userName.
Failure: 403 invalid credentials (bad creds) or 403 not authorized (business_id ≠ 1).
Route families: admin endpoints within /crm_api.
Authorization is a hardcoded numeric check, not role-based. See Remediation.
3. appMiddleware — customer loyalty app¶
File: middlewares/appMiddleware.js
Credential: contactMappingId + token + parent_id, all in the body.
Datastore: MySQL webPool. SELECT id FROM addo_contacts_mapping WHERE id=? AND token=? AND businessId=? AND status=1 (parent_id maps to businessId).
Sets on req: nothing — presence of a matching row is the gate.
Failure: 403 invalid credentials; 500 fail-closed on error.
Route families: customer-facing loyalty routes in prism_routes/loyalty_rewards_routes.js — e.g. getCustomerLoyaltyDetails, viewAllRewards, viewExpRedeemedRewards.
4. crm_offer_middleware — POS / 3rd-party integration¶
File: middlewares/crm_offer_middleware.js
Credential: Authorization header (raw token, not Bearer-prefixed).
Datastore: MongoDB authorization_token collection — findOne({ token: String(headers.authorization) }).
Sets on req: req.parent_business_id = auth_token.parent_business_id — this is the multi-tenancy anchor for integrator calls (see tenancy).
Failure: 401 with the canonical integration error shape { status: 0, error_code: "uE_101", error_message: "Authentication Failed" }.
Route families: all of /integration/v1 (prismIntegration.js), the POS/loyalty routes in injestion.js (/injestion/rista, /api/ingestion/orderPush, /api/checkLoyaltyRewards, /api/redeemLoyalty, /api/generateOTP, /api/revokeLoyalty), and getWalletDetails in loyalty_rewards.
5. jwtMiddleware / jwtWhatsappMiddleware — internal JWT¶
Files: middlewares/jwtMiddleware.js, middlewares/jwtWhatsappMiddleware.js
Credential: Authorization: Bearer <jwt> — split on space, take [1].
Validation: jwt.verify(token, secret, { algorithms: ["HS256"] }). The secret is a base64-decoded hardcoded string (jwtMiddleware.js:5, jwtWhatsappMiddleware.js:5) — the two middlewares use different secrets.
Sets on req: req.user = decoded (CRM JWT payload: user_id, customer_number, parent_id; WhatsApp JWT payload: parent_id).
Failure: 401 Unauthorized (no token) or 403 Invalid token (verify fails).
Route families: prism_routes/whatsapp_login_routes.js (/check/whatsapp/login uses jwtWhatsappMiddleware).
Hardcoded HS256 secrets → token forgery. Rotate + env-inject. See findings.
6. injestion_middleware / thirdPartyMiddleware — static token¶
Files: middlewares/injestion_middleware.js, middlewares/thirdPartyMiddleware.js
Credential: token header, compared against a single hardcoded string (injestion_middleware.js:8, thirdPartyMiddleware.js:6) .
Datastore: none — pure string compare.
Failure: 401 token in required (missing) or 401 invalid token (mismatch).
Route families: /injestion/ls_center, /sync/crm/token (injestion_middleware); occasional custom integrations (thirdPartyMiddleware).
7. Which middleware protects which route family¶
| Route family | Base path | Middleware | Credential → datastore |
|---|---|---|---|
| CRM staff (web/mobile) | /crm_api, /crm_api/v2 |
authMiddleware (many routes open |
token/password → MySQL users+auth_tokens |
| Admin | within /crm_api |
adminMiddleware |
token/password + business_id==1 → MySQL (single conn) |
| Dashboard metrics | /stats |
authMiddleware (all 12 routes) |
token/password → MySQL |
| Customer loyalty app | prism_routes/loyalty_rewards |
appMiddleware |
contactMappingId+token+parent_id → MySQL addo_contacts_mapping |
| POS / 3rd-party integration | /integration/v1, parts of /injestion |
crm_offer_middleware |
Authorization → MongoDB authorization_token |
| Order ingestion (protected) | /injestion/ls_center, /sync/crm/token |
injestion_middleware |
static token header |
| Order ingestion (open) | /injestion/uengage, /injestion/petpooja, /injestion/posist, etc. |
(none) |
— |
| Posist loyalty | /posist/v1 |
(none) |
— |
| WhatsApp login | prism_routes/whatsapp_login |
jwtWhatsappMiddleware |
Bearer JWT (HS256) |
Several high-value routes have no middleware — notably open ingestion endpoints (
/injestion/uengage,/injestion/petpooja,/injestion/posist,/injestion/salon/order), the entire/posist/v1family, andcustomerController.getCustomerDetails. Ingestion controllers rely on payload-embedded tokens (crm_token) rather than middleware. Treat these as unauthenticated at the edge and verify tenant scoping inside the controller.
8. Auth-selection flowchart¶
flowchart TD
R[Route matched] --> BP[["Pre-auth pipeline:<br/>bodyParser · CORS(*) ·<br/>traceIdMiddleware · requestLogger"]]
BP --> Q{Which route family?}
Q -->|/integration/v1 · /injestion/rista ·<br/>/api/* loyalty| CO[crm_offer_middleware]
CO --> COc{Authorization header in<br/>Mongo authorization_token?}
COc -->|no| CO401[401 uE_101]
COc -->|yes| COok[set req.parent_business_id → controller]
Q -->|/crm_api · /crm_api/v2 · /stats ·<br/>staff prism_routes| AM[authMiddleware]
AM --> AMc{token=apiToken OR<br/>token in auth_tokens OR<br/>bcrypt password?}
AMc -->|no| AM403[403 invalid credentials]
AMc -->|error| AM500[500 fail-closed]
AMc -->|yes| AMok[set req.body.created_by → controller]
Q -->|admin within /crm_api| AD[adminMiddleware]
AD --> ADc{business_id == 1<br/>AND creds valid?}
ADc -->|no| AD403[403 not authorized /<br/>invalid credentials]
ADc -->|yes| ADok[set req.body.created_by → controller]
Q -->|customer loyalty app| AP[appMiddleware]
AP --> APc{contactMappingId+token+parent_id<br/>in addo_contacts_mapping status=1?}
APc -->|no| AP403[403 invalid credentials]
APc -->|yes| APok[controller]
Q -->|/injestion/ls_center ·<br/>/sync/crm/token| IM[injestion_middleware]
IM --> IMc{token header == static token?}
IMc -->|no| IM401[401 invalid token]
IMc -->|yes| IMok[controller]
Q -->|whatsapp login| JW[jwtWhatsappMiddleware]
JW --> JWc{Bearer JWT verifies HS256?}
JWc -->|no| JW403[401/403]
JWc -->|yes| JWok[set req.user → controller]
Q -->|open ingestion · /posist/v1 ·<br/>some /crm_api| NONE[no middleware ⚠️]
NONE --> NC[controller relies on<br/>payload token / tenant filter]
Related¶
- Security Overview — findings, remediation priority, multi-tenancy
- Request Lifecycle — pre-auth pipeline
- API Overview · Endpoint Reference
- Order Ingestion · Loyalty