Authorization: Bearer <token> issued by POST /api/v1/auth/login. Passwords are stored as bcrypt hashes; login attempts are rate-limited per IP to slow brute-force attempts. This is Secure Connect's own gate — Gallagher itself has no concept of JWT; it only ever sees the separate Gallagher API key described below.create_cardholder/update_cardholder/delete_cardholder accept an optional X-Gallagher-Api-Key header. When present, that request's Gallagher-side action is attributed to that specific Gallagher REST Client identity instead of the shared server-configured key — useful when multiple operators use the same Secure Connect deployment but should show up distinctly in Gallagher's own audit trail. Falls back to the server's GALLAGHER_API_KEY when omitted. This is a separate, independent credential from the JWT bearer token — do not confuse the two headers.services/CardholderService.js) is decoupled from the vendor-specific client (api/gallagher/GallagherAdapter.js), so new integrations can be added by implementing the same adapter interface./api/v1 to allow non-breaking evolution.Dockerfile for containerized deployment.api/<vendorName>/<VendorName>Adapter.js, implementing the same public methods as GallagherAdapter.js (createCardholder, updateCardholder, deleteCardholder, findCardholderHrefByFirstName, findDivisionHrefByName, findCardNumberHref).routes/v1/cardholderRoutes.js, selecting an adapter based on a header, query parameter, or body field.ipconfig/ifconfig) for GALLAGHER_API_URL, not 127.0.0.1/localhost — Command Centre's REST Client IP allowlist matches on the interface address, and loopback connections get rejected with a bare 401.DEFAULT_DIVISION_NAME, DEFAULT_ACCESS_GROUP_ID, GALLAGHER_ACCESS_CARD_TYPE_ID, and GALLAGHER_MSIC_CARD_TYPE_ID must exactly match resources that already exist in your Gallagher instance (Configure > Divisions / Access Groups / Card Types in the Gallagher Configuration Client) — every one of these is instance-specific and create_cardholder will fail with Gallagher's own validation message if any of them don't match. Fetch your instance's real IDs with:6-9 alphanumeric validation — some instances only accept numeric card numbers for Access cards. If create_cardholder returns Invalid card number '...', try a numeric-only value.certificates/ folder at the project root and place your Gallagher mTLS client certificate (.pfx) inside. This folder is gitignored — never commit certificate files.nodemon.json restricts the dev auto-reload watcher to the actual source directories (server.js, routes/, middlewares/, services/, utils/, api/, config/gallagher.js) so unrelated file activity elsewhere in the repo doesn't trigger restarts and silently reset the in-memory Gallagher cache./api/v1)POST /api/v1/auth/login — public, rate-limited (5 attempts / 15 min / IP)Authorization: Bearer <token> from this response.validatePersonBody in routes/v1/cardholderRoutes.js. The request body must be an object with a top-level person key.person)firstName (non-empty), cards (array, min 1)lastName, email (valid email), divisionName, employmentCategory, photo (base64 JPEG, raw or data URI)cards)cardType: "Access" or "MSIC"cardNumber: ^[A-Z0-9]{6,9}$ (case-insensitive input, normalized to uppercase), unique per requestactivationDate / expiryDate: ISO 8601 datetime, expiryDate strictly after activationDatePOST /api/v1/create_cardholderPATCH /api/v1/update_cardholdertype (optional) updates the Access card's status via a JSON Patch–style update to Gallagher.person.firstName.DELETE /api/v1/delete_cardholderfirstName and deletes the first match — if multiple cardholders share a first name, disambiguate upstream in Gallagher before deleting.GET /api/v1/cache_status — cache initialization state and cached hrefs. Self-warms the cache on a fresh process if it isn't initialized yet, so it doesn't just report an empty cache.POST /api/v1/clear_cache — clears the in-memory href cacheGET /api/v1/cached_hrefs — returns cached Gallagher endpoint hrefs (also self-warms)400:401. Exceeding the login rate limit returns 429. Unhandled server errors return 500.update_cardholder currently requires a non-empty cards array in every request, even if you only want to change lastName/description/etc. and aren't touching cards. This is a validation gap (the schema is shared with create_cardholder), not a Gallagher limitation.delete_cardholder matches by firstName only and deletes the first result — if more than one cardholder shares a first name, disambiguate in Gallagher first.update_cardholder's type field only changes an Access card's status (e.g. Lost/Active).config/secrets.env, which is gitignored — they are never sent from the browser.utils/logger.js).AUTH_USERNAME/AUTH_PASSWORD_HASH for a real user store before using this in production with multiple operators.Posted Aug 11, 2026
Secure Connect: Automating Physical Access Control for Enterprise Security Systems