A transparent look at the authentication, authorisation, data isolation and security model behind the Pain Points platform.
Back to Pain PointsEvery organisation is isolated at the data layer. Authentication is delegated entirely to Microsoft Entra ID — we never see or store passwords.
Pain Points delegates all authentication to Microsoft Entra ID. We never store, transmit or handle passwords.
The React SPA detects no active session and redirects to Microsoft Entra ID via the MSAL library.
The employee authenticates with their company's M365 credentials, including any MFA policies their IT admin enforces.
Entra ID returns a signed JWT (ID token) to the browser. The token is cached in sessionStorage — it's gone when the tab closes.
Each API call includes the token as a Bearer header. The server verifies the RS256 signature against Microsoft's published JWKS keys, checks the audience and issuer, then resolves the user.
First-time users are automatically created from the Entra ID claims (name, email, tenant ID). No manual setup required.
Pain Points has zero knowledge of any passwords. Your existing M365 password and MFA policies apply automatically.
Whatever MFA and conditional access policies your IT team has configured in Entra ID are enforced by Microsoft before Pain Points ever sees the token.
Disable a user in Entra ID and they immediately lose access to Pain Points. No separate account to deactivate.
Tokens live in sessionStorage only. Close the browser tab and the session ends. No persistent cookies.
How your data is stored, protected and kept separate from every other organisation.
All connections to Azure SQL Server use TLS encryption. Data is encrypted in transit between the application and the database at all times.
Every query is scoped to the user's Azure tenant ID at the ORM level. There is no way for a user's request to return data belonging to another organisation.
Production secrets (database credentials, client secrets, tenant IDs) are stored in Azure Key Vault and retrieved via Managed Identity — no secrets in code or environment files.
Employees can submit pain points anonymously. The submitter's identity is masked as “Anonymous” for all users except organisation admins, who need visibility for follow-up.
Every authentication event, authorisation decision, and data change is logged with structured audit entries including user ID, IP address, request ID and timestamp.
All API responses include Cache-Control: no-store headers. Authenticated responses are never cached by browsers or intermediary proxies.
Every request to the Pain Points API passes through multiple layers of protection before reaching your data.
Every request passes through multiple layers of security middleware before reaching your data:
Every API endpoint validates its inputs with schema-based validation before processing. This includes string length limits, enum validation, format checks, and pagination bounds. Invalid requests are rejected before they reach any business logic.
Tiered rate limiting protects the platform: a global limit per IP address on all requests, with stricter limits on write operations per authenticated user.
The server enforces a strict CSP via Helmet:
default-src 'self' — only load resources from the same originscript-src 'self' — no inline or third-party scriptsconnect-src limited to self + Microsoft login endpointsProduction runs on Azure Container Apps with automated CI/CD, no stored credentials, and non-root containers.
Multi-stage Docker build. Production image runs as a non-root user with minimal attack surface.
GitHub Actions builds, tests, and deploys on every merge to main. Azure OIDC federation — no stored deployment credentials.
Container health probes run every 30 seconds. Unhealthy instances are automatically replaced by Azure Container Apps.
SIGTERM triggers a 10-second graceful drain, ensuring in-flight requests complete before the container stops.
We're happy to walk you through the architecture in detail. Book a call or drop us a line.