Skip to main content

Configuration & Security

Two things you'll touch often: constants.js (app-wide config and feature flags) and the security headers / CSP that wrap every response.


constants.js

Location: libs/shared/src/constants/constants.js. It exports a single constants object of build-time configuration and feature flags, imported anywhere via @aquagen-mf-webapp/shared/constants/constants.

Environment & mode

ConstantDefaultPurpose
envmapThe known environments: prod, dev, local, ngrok, demo
currentEnvprodWhich environment the app runs as (drives isDemo, base URLs, etc.)
azureAdEnvprodWhich Azure AD (MSAL) config to use
aiAgentEnvprodWhich AI-agent backend to use
isDemoderivedtrue when currentEnv === 'demo'
isPreProdfalseSet true for pre-prod/dev deploys (flipped by deploy.sh)
isMobilederivedCapacitor.isNativePlatform() — true inside the native iOS/Android shell

const development = process.env.NODE_ENV !== 'production' is the one value derived from the build itself; several flags below default to it.

Analytics & logging flags

ConstantDefaultPurpose
analyticsEnvdevelopmentDisable-analytics flag — see the caution below
logAnalyticsEventfalseConsole-log analytics events (debugging)
logRequest / logResponsefalseConsole-log API requests / responses
logCURLfalsePrint each request as a copy-pasteable cURL
analyticsSecretGA measurement ID + Mixpanel project token (client-side keys)
analyticsEnv is a "disable analytics" flag — it reads backwards

The analytics service does if (constants.analyticsEnv) return;, so truthy = analytics OFF. It defaults to development (→ false in a production build, so production records analytics), and deploy.sh sets it true for every dev deploy (dev/pre-prod do not record analytics). Full detail in Deployment → Environment flags.

Auth & timing

ConstantDefaultPurpose
microsoftSignInEnabledtrueShow the Microsoft (Azure AD) sign-in option
googleSignInEnabledtrueShow the Google sign-in option
refreshDuration5 * 60 * 10005-minute duration constant used by the auth/session flow
resendOtpTime30Seconds before "resend OTP" is offered

Requests

constants.request centralises HTTP config used by the API client:

  • defaultTimeout50 000 ms (50 s) on every Axios call.
  • contentTypeapplication/json and multipart/form-data.
  • error — the canonical status-code → message map the app shows users, covering 400, 401, 402, 404, 440, and 500.

Misc

sidebarDisabledPages (routes that hide the sidebar), demoVideoUrl, and feedbackForm (a ClickUp form URL).

These are build-time constants, edited per deploy

deploy.sh rewrites analyticsEnv, currentEnv, and isPreProd in this file before building, then reverts them — which is why the build scripts use --skip-nx-cache. See Deployment & Environments.


Auth & backend URLs

The API base URL and the OAuth redirect URI are computed at runtime from constants.currentEnv and the current domain, in libs/shared/src/services/api/urls.js and libs/shared/src/constants/authConfig.js.

API base URL — Urls.baseUrl

Chosen from constants.currentEnv:

EnvBase URL
prodhttps://prod-aquagen.azurewebsites.net/api/user/
devhttps://dev2-aquagenapi.azurewebsites.net/api/user/
localhttp://localhost:5001/api/user/
demohttps://aqua-demo-api-….azurewebsites.net/api/user/

There are also separate AI-agent backends (getAIAgentBaseUrl(), uwiAIBaseUrl), each keyed off constants.aiAgentEnv / constants.currentEnv.

OAuth redirect URI — redirectUri / Urls.azureADRedirectUrl

authConfig.js sets MSAL's redirectUri to Urls.azureADRedirectUrl. In the browser that is simply the current origin:

// libs/shared/src/services/api/urls.js
get azureADRedirectUrl() {
if (typeof window !== 'undefined') return `${window.location.origin}/`;
// per-env fallback for non-browser contexts:
// prod → https://web.aquagen.co.in/ , dev → https://dev-aquagen.web.app/ , local → http://localhost:4200/
}

So the redirect URI auto-adapts to whatever domain the app is served fromhttps://web.aquagen.co.in/ in production, https://dev-aquagen.web.app/ on the dev site, http://localhost:4200/ locally, and so on. This is what makes one build work across all the product/dev URLs.

Every domain must be registered in Azure AD (and Google)

Because the redirect URI is just the current origin, each domain the app runs on has to be added to the Azure AD app registration's Redirect URIs (and to the Google OAuth authorized origins). Add a new hosting URL there or Microsoft/Google sign-in will fail on that domain.

MSAL & Google config — authConfig.js

ConfigKey valuesToggle
msalConfigauthority: https://login.microsoftonline.com/organizations, clientId, redirectUri (above)constants.microsoftSignInEnabled
googleConfigGoogle OAuth clientIdconstants.googleSignInEnabled

Both providers are wired at startup in each app's bootstrap.jsx (<MsalProvider> and <GoogleOAuthProvider>).


Security headers & CSP

Every response ships a strict set of security headers, including a Content-Security-Policy. They are defined in three places so the app is protected in dev, in production, and even before JavaScript runs:

WhereFileApplies to
Production hostingfirebase.jsonhosting[].headers (source: "**")every response served by Firebase
Dev serverapps/production/rspack.config.jsdevServer.headershttp://localhost:4200
Clickjacking guardapps/production/src/index.htmlthe HTML shell, before the app loads

The Firebase header block is essentially the same across all hosting targets (with minor per-target variation), and the dev-server block mirrors it (slightly more permissive — it also allows localhost:8000 and Google Maps frames, and sets Cross-Origin-Embedder-Policy: unsafe-none).

The headers

HeaderValue (summary)What it does
Content-Security-Policysee belowRestricts where scripts, styles, images, connections, and frames may come from
X-Frame-OptionsDENYPage cannot be put in an <iframe>
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preloadForces HTTPS for a year (HSTS)
X-Content-Type-OptionsnosniffNo MIME-type sniffing
X-XSS-Protection1; mode=blockLegacy browser XSS filter
Referrer-Policystrict-origin-when-cross-originLimits referrer leakage
Permissions-Policyaccelerometer=(), camera=(), geolocation=(), microphone=(), payment=(), usb=() …Disables sensitive browser APIs
Cross-Origin-Opener-Policysame-origin-allow-popupsIsolates the browsing context (allows OAuth popups)
Cross-Origin-Resource-Policysame-originBlocks cross-origin resource embedding
X-Permitted-Cross-Domain-PoliciesnoneBlocks Adobe/Flash cross-domain policies
Cache-Controlno-store, no-cache, must-revalidate, proxy-revalidatePrevents caching of authenticated pages

Content-Security-Policy (directive by directive)

DirectiveAllowsNotes
default-src'self'Everything not listed below defaults to same-origin
script-src'self' 'unsafe-inline' 'unsafe-eval' + Google APIs, ClickUp CDNThe two 'unsafe-*' are required by current dependencies
style-src'self' 'unsafe-inline' blob: + Google Fonts/Accounts
img-src'self' data: https: blob:Images from any HTTPS host
font-src'self' data: + Google Fonts
connect-srcAzure Web Apps + Web PubSub (wss:), Firebase, Google, Microsoft login, Mixpanel, ClickUp, Iconify, localhost:5001The API + realtime + analytics allowlist
frame-src'self' blob: + Azure, ClickUp formsEmbeddable iframes
frame-ancestors'none'This app may not be iframed by anyone
base-uri / form-action'self'Locks <base> and form targets to same-origin

Clickjacking protection (belt & braces)

index.html ships a small script that hides <body> by default and only reveals it once JavaScript confirms the page is the top-level window (self === top); otherwise it breaks out of the frame (top.location = self.location). This backs up frame-ancestors 'none' and X-Frame-Options: DENY for browsers that miss either.

Adding a new external service? Update the CSP

If you integrate a new API, CDN, font host, analytics provider, or embeddable widget, the browser will block it until you add its origin to the matching CSP directive in both firebase.json and apps/production/rspack.config.js (dev). A request that works locally but fails in production is almost always a missing connect-src/frame-src entry in firebase.json.