Azure Entra
Microsoft Entra ID (Azure AD) integration. Two responsibilities:
- SSO — every
@renewa.deuser authenticates via Microsoft; the login UI offers no password path for internal users. - Workforce sync — internal employees, departments, locations, and Team groups are Entra-owned and synced on a schedule. Neither mocks nor migrations may re-seed them (spec:
docs/superpowers/specs/2026-06-09-mock-people-defer-to-entra-design.md).
Architecture
| Component | Path |
|---|---|
| Auth routes | backend/src/routes/entra-auth.ts (GET /api/auth/entra/login, GET /api/auth/entra/callback) |
| Auth service | backend/src/services/entra/entra-auth-service.ts |
| User sync service | backend/src/services/entra/entra-sync-service.ts |
| Group sync service | backend/src/services/entra/entra-group-sync-service.ts |
| Group read service | backend/src/services/entra/entra-group-service.ts |
| Graph API client | backend/src/services/entra/graph-client.ts |
| Scheduled job | backend/src/lib/jobs/processors/entra-sync.ts (BullMQ, hourly at :30 — lib/jobs/schedulers.ts) |
| CLI | backend/src/db/entra-sync-cli.ts (db:entra-sync) |
| Frontend callback | frontend/src/pages/AuthCallback.tsx |
SSO Flow
- User enters their email on the login page
- Frontend calls
POST /api/auth/check-domain; for@renewa.dethe backend answersauthMethod: 'entra' - Frontend redirects to
GET /api/auth/entra/login(returnUrl preserved viasessionStorage) - User authenticates with Microsoft; Azure redirects back to
/api/auth/entra/callback - Backend exchanges the code for tokens, validates claims, links/creates the Users account, issues the session
- Frontend
AuthCallbackpage restores the return URL and redirects into the app
Because the UI routes every @renewa.de email to SSO, browser-automation sessions (Playwright / e2e bots) authenticate via the API login endpoint instead — see the e2e-bot pattern in the repo CLAUDE.md.
Scheduled Workforce Sync
EntraSyncService runs hourly (BullMQ scheduler, cron 30 * * * *) and also via db:entra-sync, which runs before db:mock on local/pr-preview/development:
- Syncs members of the security group
ENTRA_SYNC_GROUP_IDfrom Microsoft Graph: creates new users, updates existing ones, deactivates users removed from the group - “Entra always wins”: Entra data overwrites RENEWA contact fields on every sync; email conflicts are logged and skipped
- Auto-creates departments and locations from Entra attributes (spec:
docs/superpowers/specs/2026-04-20-entra-department-auto-create-design.md) - An in-memory mutex prevents concurrent sync runs
EntraGroupSyncService syncs Entra “Team*” groups and memberships into entra_groups / entra_group_members:
- Full sync hourly alongside the user sync
- JIT sync of a single user’s groups at login
- Owners who are also members get the
ownerrole; unknown Entra OIDs are skipped
Role Mapping
Members of the optional ENTRA_ADMIN_GROUP_ID group get internalRole: 'admin'; everyone else synced from the group is employee. Checked both at login (entra-auth-service.ts) and during scheduled sync (entra-sync-service.ts) via Graph checkMemberGroups.
Redirect URI Management
Each environment needs its redirect URI (/api/auth/entra/callback) registered on the Azure app registration:
| Environment | URI |
|---|---|
| Local | http://localhost:<nginx-port>/api/auth/entra/callback |
| Development | https://renewa-app-development.fly.dev/api/auth/entra/callback |
| Staging / Production | same pattern on renewa-app-staging / renewa-app-production |
| PR Preview | https://renewa-app-pr-<N>.fly.dev/api/auth/entra/callback |
Managed via make sso-register → scripts/manage-entra-redirect-uri.sh (runs automatically as part of make dev-init). See PR Preview Deployments and Makefile Commands.
Configuration
| Secret | Purpose |
|---|---|
ENTRA_TENANT_ID | Organization directory |
ENTRA_CLIENT_ID | App registration identifier |
ENTRA_CLIENT_SECRET | App authentication (Graph + OAuth) |
ENTRA_SYNC_GROUP_ID | Security group whose members are synced as employees |
ENTRA_ADMIN_GROUP_ID | Optional — members get the admin role |
Secrets live in Infisical (EU) and sync natively to Fly.io (not GitHub Environment Secrets). See Deployment Pipeline.
User Provisioning
On first SSO login the system links the Azure identity (Entra OID) to an existing Users account with the Microsoft email, or creates a new user from the Azure profile. The scheduled sync provisions employees ahead of their first login, so most internal users already exist.
Related
- Authentication — Overall auth architecture (JWT, 2FA, password)
- Users — User account creation and linking
- External Integrations — All third-party integrations
- Makefile Commands —
make sso-registercommand - PR Preview Deployments — Dynamic redirect URI registration
- Background Jobs — Hourly sync scheduler