Skip to content

Accounts, Identity & Authentication — Technical Reference

Part of the Aashray Technical Reference. Pairs with Business Logic — Accounts, Identity & Auth, which describes these rules in plain terms with no code detail. Related technical detail also touches: Booking Lifecycle, Raj Pravas / Travel, Payments, Admin Panel Map, API Reference.

This file is derived from reading the backend, admin, and app source on main. It covers the identity backbone (CardDb), the three distinct authentication realms (member, admin+RBAC, coordinator-OTP) and exactly how each is enforced, the member sign-in / forgot-password / logout / change-password flows, profile read/update/photo-upload, the onboarding gating fields, admin card issuance & management, admin account (staff) management, the financial "accounts management" reporting surface, guest identity records and guest relationships, and the country→state→city→centre location reference data.

Verification status: audited & anchored per docs/DOCS-METHODOLOGY.md on 2026-07-07 against aashray-backend main (with aashray-admin cross-checked for the panel claims). Every rule carries a [src: …] anchor. This domain was found fully accurate — all 15 discrepancies confirmed real against code, no false claims (one trivial nuance corrected: AdminUsers.status is nullable with a default, not strictly not-null).

Data-model backbone: the hub table is CardDb (card_db), a pre-issued member card whose cardno (string, unique) is the foreign key used everywhere. AdminUsers optionally links to a cardno; every booking, transaction, guest record and relationship hangs off cardno. See models/associations.js and config/constants.js.


0. The three authentication realms (and how each is enforced)

There is no single auth system — three separate realms coexist, enforced very differently.

Realm Who Credential Token issued How it is enforced Where
Member App users (mumukshus / residents / seva kutir / guests) 10-digit phone (mobno) + password None — no session/JWT is issued to members middleware/validate.js#validateCard: reads cardno from req.params/req.body/req.query, looks it up in CardDb, and attaches the row as req.user. Any caller who knows a valid cardno passes. client routes
Admin Staff via the admin web panel username + password JWT, SECRET, 24h expiry middleware/AdminAuth.js#auth verifies the bearer JWT, re-loads the AdminUsers row, rejects inactive accounts, and loads active AdminRoles. authorizeRoles(...roles) then gates by role. admin routes
Coordinator Bus/travel coordinators (a subset of members) mobno + WhatsApp OTP JWT, SECRET, 7d expiry, payload {cardno, mobno} Each coordinator controller verifies the bearer JWT inline (there is no shared middleware). Eligibility = the card owns a TravelDb booking that is set as coordinator_bookingid on an upcoming TravelBusGroup. /api/v1/coordinator/*

Key consequences: - Members are not "logged in" server-side. The token column on CardDb stores the device's Expo push-notification token, not a session token; verifyAndLogin sets it and logout nulls it. Authorization for member endpoints is purely "does this cardno exist" — see Discrepancies. - Admin RBAC is server-enforced (per-route authorizeRoles) and mirrored client-side in the panel (style/js/roleCheck.js + adminhome.html role→button map). The server list is authoritative. - Coordinator auth reuses member cards but issues its own JWT; full implementation of the coordinator dashboard/boarding flow lives in the Travel technical reference.


1. CardDb — the identity record

What it is. The master member/person record. Pre-issued by staff (there is no self-service sign-up anywhere in the product). One row per person; cardno is the universal key.

Data & relationships (models/card.model.js, table card_db, timestamps on):

Field Type Notes
id INT PK autoincrement internal only
cardno STRING, unique, not null the real key used across all tables; also exposed as virtual username (getter returns cardno)
issuedto STRING, not null full name
gender ENUM M/F, not null only two values
dob DATEONLY, nullable
mobno BIGINT, unique, not null login identifier & WhatsApp target
email STRING, nullable recovery + receipts
idType, idNo STRING, nullable govt ID (PAN / Passport)
address, country, state, city, pin STRING, nullable address (free strings, not FKs to the location tables)
center STRING, nullable home centre
pfp TEXT, nullable S3 URL of profile photo
token TEXT, nullable Expo push token (not a session token)
active BOOL, not null, default true
status ENUM onprem/offprem, not null on-campus presence
res_status ENUM MUMUKSHU/PR/SEVA KUTIR/GUEST, not null residency class (PR = permanent resident)
updatedBy STRING, not null audit (admin username or USER)
password STRING, not null bcrypt hash; shared default $2b$10$0rv/aoTVqmqruYcStSJ31.hund78qaqGE8CylUak1248ymHC7r/xy [src: models/card.model.js:111-116]
credits JSON, nullable store-credit buckets {room, food, travel, utsav} (1 credit = ₹1)
showDevelopmentDashboard BOOL, default false unlocks in-app dev tools

Associations off CardDb (from associations.js): hasOne AdminUsers (FK cardno); hasMany on GateRecord, FoodDb (both cardno and bookedBy), BulkFoodBooking, ShibirBookingDb (cardno + alias shibirBookedByCard on bookedBy), FlatBooking/RoomBooking/TravelDb/UtsavBooking (each with a parallel bookedBy alias), FlatDb (owner), MaintenanceDb (requested_by), Transactions (cardno), GuestDb (cardno), GuestRelationship (both cardno and guest), SupportTickets (issued_by), AdhyayanFeedback, UtsavFeedback, ShibirAttendance*, PermanentWifiCodes, WifiDb. This is why cardno is the join key everywhere and why bookings distinguish whose (cardno) vs who created it (bookedBy).

Helpers (helpers/card.helper.js): validateCard(cardno) / validateCards([...]) throw 404 User not found if absent; userIsPR(cardno) returns res_status == 'PR'; findCardByMobno(mobno).


2. Member authentication

What it is. Phone+password sign-in for pre-issued cards, plus temporary-password recovery, logout, and in-app password change. Controller controllers/client/auth.controller.js; routes routes/client/auth.routes.js mounted at /api/v1/client.

Backend rules & logic: - verifyAndLogin — looks up the card by mobno; 404 user not found if none. bcrypt.compareSync(password, hash); 404 Incorrect Password on mismatch. On success it stores the request's token (Expo push token) into CardDb.token, computes isFlatOwner (any FlatDb where owner = cardno), blanks password in the response, returns {message:'logged in', data: <card + isFlatOwner>}. Excludes id/token/active/status/... from the returned object. [src: controllers/client/auth.controller.js:97-151] - forgotPassword — looks up by mobno (404 if none), generates a 5-character temporary password via generateTemporaryPassword() (charset = A–Z a–z 0–9 only, no special symbols despite the code comment), bcrypt-hashes & saves it, then emails it (forgotPasswordEmail template) and sends WhatsApp template password_reset_app. Returns the email in the response. WhatsApp failures are swallowed (logged, not fatal). [src: controllers/client/auth.controller.js:153-231 (length 5 :157, charset :155-156)] - updatePassword — behind validateCard; requires current_password + new_password; verifies current against stored hash (404 incorrect password provided), saves new bcrypt hash, sends WhatsApp password_update_app. No length/complexity rule server-side. [src: controllers/client/auth.controller.js:11-71] - logout — sets CardDb.token = null for the cardno in query. Returns {message:'logged out'}. [src: controllers/client/auth.controller.js:74-95]

Every route (base /api/v1/client):

Method Path Auth realm Purpose Key params/body
POST /verifyAndLogin public Sign in; store push token body {mobno, password, token} (token = Expo push token)
POST /forgotPassword public Issue temp password via email + WhatsApp body {mobno}
POST /updatePassword member (validateCard) Change password (needs current) body {cardno, current_password, new_password}
GET /logout public Clear push token query ?cardno=

3. Member profile

What it is. Read/update the member's own CardDb, upload the avatar to S3, list the member's transaction ledger, and send push notifications. Controller controllers/client/profile.controller.js; routes routes/client/profile.routes.js mounted at /api/v1/profile with router.use(validateCard) (every route requires a valid cardno).

Backend rules & logic: - fetchProfile (GET /) — returns the card (excluding id/token/active/status/createdAt/updatedAt/updatedBy) plus computed isFlatOwner. - updateProfile (PUT /) — updates the 13 editable fields (issuedto, gender, dob, address, mobno, idType, idNo, email, country, state, city, pin, center). Diffs old vs new (normalized/trimmed) to build a human-readable changed list ("Name", "Email", …); if anything changed, sends WhatsApp template profile_updated (name, cardno, IST timestamp, changed fields). Does not touch pfp or password. - upload (POST /upload) — multer memory storage, image/* only, 3 MB cap, single field image. Uploads to S3 (AWS_S3_BUCKET_NAME) with a unique key, sets CardDb.pfp to the public S3 URL, and deletes the previous object if one existed. Returns the URL. - transactions (GET /transactions) — paginated raw SQL over transactions LEFT-JOINed to room_booking/flat_booking/travel_db/shibir_booking_db(+shibir_db)/utsav_booking(+utsav_packages_db/utsav_db) and card_db, filtered to transactions.cardno = :cardno. Supports status (comma list or repeated; all dropped) and category filters; returns {data, pagination:{page,pageSize,hasMore}}. - sendNotification (POST /notification) — takes tokenData[] and calls sendPushNotifications; 400 if not a non-empty array.

Every route (base /api/v1/profile, all behind validateCard):

Method Path Auth realm Purpose Key params/body
GET / member Fetch own profile (+isFlatOwner) cardno (query)
PUT / member Update editable profile fields body {cardno, issuedto, gender, dob, address, mobno, idType, idNo, email, country, state, city, pin, center}
POST /upload member Upload/replace avatar to S3 multipart image; cardno
GET /transactions member Paged transaction ledger query cardno, page, page_size, status, category
POST /notification member Send Expo push notifications body {cardno, tokenData:[…]}

Edge cases: avatar upload errors return 400 with error; profile update WhatsApp is best-effort; the ledger's status accepts a comma-joined string and normalizes it, and all is treated as "no filter."


4. Onboarding gating fields

What it is. The exact record-state the app uses to route a signed-in user to one of four shells. Enforced client-side from the persisted user object (the backend has no onboarding gate).

State Condition Shell
Signed out no stored user Auth → Sign In
Needs photo user exists and pfp empty Onboarding Step 1 (upload photo)
Needs profile user exists and has pfp but profile incomplete Onboarding Step 2 (13-field form)
Fully onboarded user exists and profile complete Main app (tabs)

"Profile complete" = all 13 non-empty: issuedto, email, mobno, address, dob, gender, idType, idNo, country, state, city, pin, center. Photo (pfp) is mandatory before the profile step. Step 2 submits via PUT /profile; the photo step uses POST /profile/upload; both onboarding steps expose Logout (GET /client/logout).


5. Admin authentication & RBAC

What it is. Staff login and role model for the web panel. Controller controllers/admin/auth.controller.js; routes routes/admin/auth.routes.js at /api/v1/admin/auth. Middleware middleware/AdminAuth.js.

Backend rules & logic: - login — looks up AdminUsers by username (404 Invalid Username); rejects status = inactive (401 Account Deactivated); loads active AdminRoles; bcrypt.compare(password); on success signs a JWT {user:{id, username, status}} with SECRET, 24h, and returns {token, roles, username}. Wrong password → 401 Incorrect password. [src: controllers/admin/auth.controller.js:11-54] - createAdmin — creates an AdminUsers (bcrypt hash, optional cardno link, updatedBy = req.user.username) + bulk AdminRoles, in a transaction; sends WhatsApp admin_account_created if the linked card has a phone. - resetPassword — resets an admin's password by username + newPassword (min 8 chars); rejects inactive; sends WhatsApp admin_password_reset_notice if linked to a card. ⚠️ This endpoint has no auth middleware — see Discrepancy #7. [src: controllers/admin/auth.controller.js:123-160; routes/admin/auth.routes.js:15] - auth middleware — verifies bearer JWT, re-loads the AdminUsers row (401 if missing), rejects inactive, loads active role names into req.roles, sets req.user = decoded.user. - authorizeRoles(...roles) — passes if req.roles intersects the allowed set, else 401 Unauthorized.

Data & relationships: - AdminUsers (admin_users): id PK, username unique not null, password, status ENUM active/inactive (nullable, default active), cardnocard_db.cardno (nullable). belongsTo CardDb as 'card'; hasMany AdminRoles. [src: models/admin_users.model.js:13-35] - Roles (roles): name STRING PK, status ENUM active/inactive, updatedBy. - AdminRoles (admin_roles): composite PK (user_idadmin_users.id, role_nameroles.name), status ENUM active/inactive default active, updatedBy. belongsTo AdminUsers + belongsTo Roles.

Every route (base /api/v1/admin/auth):

Method Path Auth realm Purpose Key params/body
POST /login public Admin login → JWT + roles body {username, password}
POST /create admin + superAdmin Create admin user + roles body {username, password, roles:[…], cardno?}
POST /reset-password public (no auth) Reset an admin's password body {username, newPassword}

Admin operations (panel): login form at admin/index.htmlPOST /admin/auth/login, stores token+roles in sessionStorage, redirects to adminhome.html. admin/adminhome.html reads roles from sessionStorage and renders a role→button menu (map below). style/js/roleCheck.js#checkRoleAccess(allowedRoles) guards each page client-side (redirects to login if no token, to home if wrong role, logs out if no valid role). Password reset is a "Forgot Password?" modal on the login page.

RBAC role catalog & capability gating

Roles are defined in config/constants.js and gated per route group. The capability mapping (server-enforced via authorizeRoles):

Route group (base path) Roles allowed (server)
/admin/sudo (admin & role management) superAdmin only [src: routes/admin/adminControls.routes.js:16-17]
/admin/auth/create superAdmin only [src: routes/admin/auth.routes.js:9-14]
/admin/card (card issuance/management) officeAdmin, superAdmin, cardAdmin, utsavAdmin, wifiAdmin, foodAdmin [src: routes/admin/cardManagement.routes.js:17-18]
/admin/accounts (financial reports) superAdmin, accountsAdmin, accountsAdminPra [src: routes/admin/accountsManagement.routes.js:26-27]

Full role constant set (24): superAdmin, roomAdmin, cardAdmin, officeAdmin, adhyayanAdmin, adhyayanAdminKol/adhyayanAdminRaj/adhyayanAdminDhu, utsavAdmin, utsavAdminReadOnly, utsavAdminRaj, foodAdmin, foodPlateAdmin, travelAdmin, travelAdminDri, accountsAdmin, accountsAdminPra, gateAdmin, maintenanceAdmin, housekeepingAdmin, electricalAdmin, avtAdmin, wifiAdmin, smilesAdmin, adhyayanAdminReadOnly. The panel's adminhome.html maps each role to menu buttons (e.g. cardAdmin → Card Management; accountsAdmin → Account Management; the location-scoped adhyayanAdminKol/Raj/Dhu and utsavAdminRaj map to pre-filtered report pages; smilesAdmin → Bulk Food; foodPlateAdmin → K1 Kitchen Count). superAdmin sees the full menu.


6. Admin account & role management ("sudo")

What it is. Manage staff accounts and the role catalog. Controller controllers/admin/adminControls.controller.js; routes routes/admin/adminControls.routes.js at /api/v1/admin/sudo, entirely behind auth + authorizeRoles(superAdmin).

Backend rules & logic: - fetchAllAdmins — all AdminUsers, active first then alphabetical. - updateAdminRoles — in a transaction, sets all of a user's existing AdminRoles to inactive, then bulk-inserts the new role list as active (roles are soft-toggled, not deleted). - deactivateAdmin / activateAdmin — flip AdminUsers.status; WhatsApp admin_status_updated if the account is card-linked. - createRole — add a Roles row (409-style 400 name already taken if it exists). - fetchRoles — active role names only. - deleteRole — hard destroy by name (500 if nothing deleted).

Every route (base /api/v1/admin/sudo, all superAdmin):

Method Path Auth realm Purpose Key params/body
GET /fetch_all_admins admin+superAdmin List all admins
PUT /update_roles admin+superAdmin Replace a user's roles body {userid, roles:[…]}
PUT /deactivate/:username admin+superAdmin Deactivate admin param username
PUT /activate/:username admin+superAdmin Activate admin param username
POST /role/:name admin+superAdmin Create role param name
GET /role admin+superAdmin List active roles
DELETE /role/:name admin+superAdmin Delete role param name

Admin operations (panel): admin/sudo/*fetchAllAdmins.html, createAdmin.html (→ POST /admin/auth/create), activateAdmin/deactivateAdmin, updateAdminRoles, createRole/fetchRoles/deleteRole. All gated checkRoleAccess(['superAdmin']).

Edge cases: updateAdminRoles requires the new roles array to be non-empty (bulkCreate of empty → 500). Deleting a role does not cascade-clean the admin_roles rows in this call (association declares CASCADE, but hard-delete order matters).


7. Card issuance & management

What it is. Staff CRUD over CardDb (issue a card, edit, transfer a card number, reset a member's password, look up cards). Controller controllers/admin/cardManagement.controller.js; routes routes/admin/cardManagement.routes.js at /api/v1/admin/card, behind auth + authorizeRoles(officeAdmin, superAdmin, cardAdmin, utsavAdmin, wifiAdmin, foodAdmin).

Backend rules & logic: - createCard — 400 if cardno exists. Creates the card with status = offprem, updatedBy = admin username (password defaults to the shared model default). If res_status === 'GUEST', requires referenceCardno + guestType, validates the parent card exists, and creates a GuestRelationship {cardno: referenceCardno, guest: cardno, type: guestType} — all in one transaction. WhatsApp card_account_created on success. SequelizeUniqueConstraintError → "Card number must be unique." - updateCard — 400 if card missing; if res_status === 'GUEST' requires referenceCardno+guestType. Diffs & updates the fields, then either upserts or destroys the guest relationship (see Discrepancies). WhatsApp profile_updated on any change. - transferCard — reassigns cardnonew_cardno (renames the key; associations cascade on update). - fetchAllCards / searchCardsByName (LIKE over issuedto/mobno/cardno) / getCardByMobile (returns cardno, issuedto, center, mobno, res_status, gender). - resetPasswordDefault — resets a member's password to the model's shared default hash (CardDb.rawAttributes.password.defaultValue); WhatsApp password_reset_admin. - fetchTotalTransactions — per-category expense/refund/net for a cardno.

Every route (base /api/v1/admin/card, all admin-realm with the role set above):

Method Path Auth realm Purpose Key params/body
POST /create admin (card roles) Issue a card (+guest link) body: full card + res_status, optional referenceCardno,guestType,centre
GET /getAll admin (card roles) All cards
GET /search/:name admin (card roles) Search by name/mobno/cardno param name
GET /by-mobile/:mobno admin (card roles) Lookup by phone param mobno
PUT /update admin (card roles) Edit a card body: card fields + cardno
PUT /transfer admin (card roles) Rename card number body {cardno, new_cardno}
GET /transactions/:cardno admin (card roles) Card expense/refund totals param cardno
POST /reset-pwd admin (card roles) Reset member password to default body {cardno}

Admin operations (panel): admin/card/*index.html (search + list, "Add New Card", "Mumukshu Credit Report"), createCard.html/.js (cascading country/state/city via /admin/location/*, guest fields when res_status=GUEST), updateCard.html/.js, cardDetails.html/.js, creditsReport. All gated checkRoleAccess(['officeAdmin','cardAdmin','superAdmin']) (narrower than the server's route guard).

Data & relationships: writes card_db; guest branch writes guest_relationship (member cardno ↔ guest's card guest).

Edge cases: createCard forces status=offprem; guest creation validates the parent card. transferCard relies on FK onUpdate: CASCADE to move all child rows to the new cardno.


8. Accounts management (financial reporting)

What it is. Read-only financial/reconciliation reporting over Transactions, Razorpay settlements, and member credits. Controller controllers/admin/accountsManagement.controller.js; routes routes/admin/accountsManagement.routes.js at /api/v1/admin/accounts, behind auth + authorizeRoles(superAdmin, accountsAdmin, accountsAdminPra). (Included here because it is the "accounts" surface for staff identities; the money/status semantics live in the Booking Lifecycle.)

Backend rules & logic (summary): all endpoints join transactions to the relevant booking table and to card_db twice (bookedBy vs bookedFor). fetchCompletedTransactions (status in completed/cash completed/credited), fetchPendingTransactions (cash pending/pending), fetchAllCreditTransactions (credited), fetchAllDebitTransactions (credited-excluded + "credits used: N" descriptions). Settlement upload/recon: uploadRazorpaySettlementExcel + updateSettlementFieldsFromExcel ingest XLSX; fetchAllSettlements, fetchTransactionsBySettlementId, fetchTransactionsByPaymentId reconcile app txns against Razorpay webhook/"Satshrut" payments. fetchCredits lists cardholders with non-empty credits JSON (parsed into room/food/travel/utsav); fetchCreditTransactions reconstructs a per-card credit earn/spend ledger.

Every route (base /api/v1/admin/accounts, all admin-realm accounts roles):

Method Path Auth realm Purpose Key params/body
GET /fetchcompleted admin (accounts) Completed txn report query startDate,endDate,category,adhyayanId,utsavId
GET /fetchpending admin (accounts) Pending txn report query startDate,endDate
GET /fetchcredits admin (accounts) Credited txns query startDate,endDate
GET /fetchdebits admin (accounts) Credits-used txns query startDate,endDate
POST /setrep admin (accounts) Upload Razorpay settlement XLSX multipart file
POST /updateset admin (accounts) Upsert settlement recon XLSX multipart file
GET /fetchset admin (accounts) All settlements (+fees/tax) query startDate,endDate
GET /fetchTransactions/:settlementId admin (accounts) Txns for a settlement param settlementId
GET /fetchTransactions/payment/:razorpay_order_id admin (accounts) Txns for an order param razorpay_order_id
GET /credits admin (accounts) Cardholders with credit balances
GET /fetchcreditstransactions admin (accounts) Per-card credit ledger query cardno, category

Admin operations (panel): admin/account/*index.html (checkRoleAccess(['accountsAdmin','superAdmin'])) links to Completed / Pending / Credited / Credits-Used reports, Settlement Report / Reconciliation / Breakdown, and Mumukshu Credits Report.


9. Coordinator OTP realm

What it is. A phone-OTP login for travel/bus coordinators (a subset of member cards) with a small dashboard. Controller controllers/admin/coordinatorAuth.controller.js; helper helpers/sendCoordinatorOtp.js; routes routes/admin/coordinatorAuth.routes.js at /api/v1/coordinator. Model coordinatorOtp.model.js (coordinator_login_otp: id UUID PK, mobno, otp, expires_at, verified bool, attempts int).

Backend rules & logic: - sendOtp — requires mobno (400); card must exist (404 Coordinator not found); card must own a TravelDb booking (403); the card must be the coordinator_bookingid of an upcoming TravelBusGroup (403 You are not assigned as coordinator). Rate-limit: ≥5 OTPs in 10 min → 429. Generates a 6-digit OTP, stores it with a 5-min expiry, sends WhatsApp coordinator_login_otp. - verifyOtp — finds the newest unverified {mobno, otp}; on miss, increments attempts on the latest OTP and blocks after 5 invalid attempts (429); checks max attempts and expiry; on success marks verified and signs a JWT {cardno, mobno} (7d), returning {token, user:{cardno,mobno,issuedto,center}}. - fetchCoordinatorDashboard / updateBoardingStatus — each verifies the bearer JWT inline (401 on missing/invalid), then re-derives the coordinator's assigned upcoming buses and passengers. updateBoardingStatus additionally authorizes that the caller is the assigned coordinator of the passenger's bus before flipping boarded/boarded_at. Full bus/passenger/dashboard mechanics are documented in the Travel technical reference — this section covers only the auth realm itself.

Every route (base /api/v1/coordinator):

Method Path Auth realm Purpose Key params/body
POST /send-otp public (eligibility-checked) Send login OTP via WhatsApp body {mobno}
POST /verify-otp public Verify OTP → 7d JWT body {mobno, otp}
GET /dashboard coordinator JWT (inline) Assigned buses + passengers header Authorization: Bearer
PUT /boarding-status coordinator JWT (inline) Mark a passenger boarded/unboarded header bearer; body {passenger_id, boarded}

Edge cases: OTP is single-use (verified flips true), 5-min TTL, 5-request/10-min throttle, 5-attempt lockout (which marks the OTP verified to burn it). Eligibility is recomputed on every call (event_date ≥ today).


10. Guest identity records & guest relationships

Two distinct concepts, both keyed on card_db:

GuestDb (guest_db) — a guest person attached to a member card, created during guest bookings. Fields: id PK, cardnocard_db.cardno (the host member), name not null, type not null, mobno nullable, gender ENUM M/F, updatedBy default 'USER'. CardDb hasMany GuestDb (cardno). These are lightweight profiles for people who don't have their own card.

GuestRelationship (guest_relationship) — links a member card to a guest's own card. Fields: id PK, cardnocard_db.cardno (host/reference), guestcard_db.cardno (the guest's card), type not null, updatedBy not null. CardDb hasMany GuestRelationship on both cardno and guest. Written by createCard when res_status='GUEST' ({cardno: referenceCardno, guest: newCardno, type: guestType}).

Note a card can be both a member and a guest depending on res_status, and a guest card is still a full CardDb row (can log in like any member).


11. Location & centre reference data

What it is. Read-only country→state→city hierarchy plus centres, used to populate address dropdowns in both the app profile form and the admin card forms. Controller controllers/client/location.controller.js; routes routes/client/location.routes.js.

Data & relationships: Countries (countries: id PK, name) hasMany States; States (states: id PK, country_id FK, name) hasMany Cities; Cities (cities: id PK, state_id FK, name). Centres come from CentreDb. All list endpoints return [{key:id, value:name}] sorted by name. addData bulk-imports the full geo tree from a local data.json in 1000-row chunks (seed utility).

Every route (mounted at both /api/v1/location and /api/v1/admin/location — same client router):

Method Path Auth realm Purpose Key params/body
GET /countries public List countries
GET /states/:country public States in a country (by name) param country
GET /cities/:country/:state public Cities in a state (by name) params country,state
GET /centres public List centres
POST / public Seed geo data from data.json

Discrepancies

  1. No member session token. The product has no member JWT despite "login" language; endpoints trust cardno. Any party who learns a cardno can call member endpoints for that card (fetch profile, transactions, change/replace push token, etc.). This is the single biggest identity gap. [src: middleware/validate.js:10-20]
  2. Temp password is only 5 chars from a 62-char alphanumeric set; the Gujarati code comment claims special symbols but none are included. [src: controllers/client/auth.controller.js:155-157]
  3. Shared default password. Every new CardDb row is created with the same bcrypt default hash, so a freshly issued card is guessable until the member changes it. resetPasswordDefault (§7) resets back to this same publicly-known shared hash. [src: models/card.model.js:111-116; controllers/admin/cardManagement.controller.js:463]
  4. POST /profile/notification is only cardno-gated yet can push arbitrary notification payloads to arbitrary Expo tokens supplied in the body — abusable given there is no member token (discrepancy #1). [src: routes/client/profile.routes.js:13,18; controllers/client/profile.controller.js:331-357]
  5. updateProfile lets a member change their own mobno (the login identifier) with no re-verification. [src: controllers/client/profile.controller.js:21-35,64-85]
  6. Onboarding gate is purely client-side against the same 13 fields, so a staff edit that clears any one field (via card management) bounces the member back into onboarding on next launch. [src: no backend onboarding gate; only validateCard on profile routes]
  7. POST /admin/auth/reset-password has no auth middleware — anyone who knows (or guesses) an admin username can reset that admin's password to a value of their choosing. This is a privilege-escalation hole. [src: routes/admin/auth.routes.js:15]
  8. The valid-role list is duplicated in three places (index.js, roleCheck.js, adminhome.html) and must be kept in sync manually. [src: aashray-admin/style/js/roleCheck.js; aashray-admin/admin/adminhome.html]
  9. updateCard's guest-relationship write is broken. It does GuestRelationship.findOrCreate({ where:{cardno} , defaults:{ cardno, referenceCardno, guestType, createdBy }}) — but the GuestRelationship model has columns cardno, guest, type, updatedBy (no referenceCardno/guestType/createdBy), and it keys where:{cardno} on the member card rather than the guest. createCard writes the correct columns; updateCard does not, so editing a guest card corrupts/fails the relationship. [src: controllers/admin/cardManagement.controller.js:335-343; models/guest_relationship.model.js]
  10. fetchTotalTransactions is SQL-injectablecardno is string-interpolated directly into the query (WHERE cardno = ${cardno}, marked // TODO: FIX this). [src: controllers/admin/cardManagement.controller.js:415,434]
  11. Panel path/body mismatch. cardDetails.js calls PUT /admin/card/update/${cardno} (cardno in path) but the route is PUT /admin/card/update (cardno in body) — the path variant would 404. updateCard.js uses the correct body form. [src: aashray-admin/admin/card/cardDetails.js:61; aashray-admin/admin/card/updateCard.js:48; aashray-backend/routes/admin/cardManagement.routes.js:24]
  12. Coordinator JWT verification is copy-pasted inline in each handler (fetchCoordinatorDashboard, updateBoardingStatus) rather than sharing AdminAuth — divergent from both other realms and easy to drift. [src: controllers/admin/coordinatorAuth.controller.js]
  13. Location routes are unauthenticated on both mounts, including /admin/location (the admin card form calls it with a bearer token, but no token is required). [src: routes/client/location.routes.js; app.js:175,191]
  14. A parallel controllers/admin/location.controller.js + routes/admin/location.routes.js exist but are not wired into app.js/api/v1/admin/location is served by the client router. The admin copies are dead code. [src: app.js:191 (client locationRoutes); controllers/admin/location.controller.js (unimported)]
  15. CardDb.country/state/city are stored as free strings, not FKs to the Countries/States/Cities tables, so location integrity isn't enforced on the card record. [src: models/card.model.js:59-70]

How this connects to other domains (technical)

  • Booking Lifecycle & Engine (business-logic file 03) — every booking stores cardno (whose stay) + bookedBy (who created it: self/guest/mumukshu), both FKs into CardDb; Transactions.cardno/bookingid and the credits JSON bucket originate here. The status/type constants used across bookings are the same config/constants.js this doc references.
  • Raj Pravas / Travel (business-logic file 06; technical 06) — the coordinator realm (§9) is the identity gate in front of the bus-coordinator dashboard; eligibility is derived from TravelDb + TravelBusGroup; full dashboard/boarding mechanics live in that technical file.
  • Admin Panel Map (11) — the RBAC role→screen mapping (§5, adminhome.html role button map, roleCheck.js) is the authorization backbone for every admin management screen; card/account/sudo screens documented here are entries in that map.
  • API Reference (12) — the route tables here (/client, /profile, /location, /admin/auth, /admin/sudo, /admin/card, /admin/accounts, /coordinator) feed the canonical endpoint list; the three auth realms define the Auth realm column semantics used throughout.
  • Payments & Credits (business-logic file 08) — the credits JSON on CardDb and the Transactions joins in §3/§8 are the identity side of the money system (earning credits on paid-then-cancelled bookings, auto-applied at checkout).