Nexus Login Integration & URL Mismatch UX
Version: 3.1.0 Date: 2026-06-12
Goal: Replace Tower's own email/password login with a redirect to Nexus (OAuth 2.0 code flow), centralize Tower URL management via the activation token, and provide a clear UX when the Tower URL has drifted.
Solution:
nexus-authmodule handles the OAuth callback (/auth/callback) and code exchange with the gateway.bcyip-syncmodule (exmagic-auth) handles the URL heartbeat with BCYIP.InitializationServicereadstowerUrlfrom the activation token on startup and injects it intoConfigurationService.getTowerUrl()— the single source of truth for the Tower's public URL.PUT /config/settingsremoved;POST /reactivate-urladded for operator-driven URL drift recovery.- When the gateway detects a URL mismatch, it redirects to
/activate?reason=url_mismatchwhere the user can either navigate to the registered URL or re-activate with the current one. - Login page passes
company_id(from activation token) to Nexus so Nexus can fetch and display the company logo. /login/passwordroute added for legacy password-based auth as a discrete fallback link.
Features:
- Nexus OAuth code flow login (Tower → Nexus → Gateway → Tower callback)
- Tower URL sourced from
activation.token.towerUrl(deprecatesself.urlin YAML config) - URL mismatch detection + dedicated re-activation screen in
/activate - Dynamic branding:
company_idpassed to Nexus for company logo display - Logout button in Sidenav
BrandingLogouses localStorage cache + opacity fade to prevent logo flash/login/passwordfallback route for legacy auth- Nexus web URL derived automatically from
BCYIP_TARGET_ENV— noNEXUS_WEB_URLenv var needed on deployed envs
Files Modified:
Backend:
src/protocol/bcyip/api.ts— hardcoded Nexus web URLs per env (PROD/PREPROD/DEV);getNexusWebUrl()derives URL fromBCYIP_TARGET_ENV,NEXUS_WEB_URLenv var kept as overridesrc/core/auth/nexus/nexus-auth.controller.ts—GET /auth/configusesgetNexusWebUrl()instead of readingNEXUS_WEB_URLdirectlysrc/app.controller.ts— removedPUT /config/settings, addedPOST /reactivate-urlsrc/app.module.ts— replacedMagicAuthModulewithNexusAuthModule+BcyipSyncModulesrc/config.dto.ts— removedUpdateSettingsDto, addedReactivateUrlDtosrc/core/auth/nexus/— new:nexus-auth.controller.ts,nexus-auth.dto.ts,nexus-auth.module.tssrc/core/bcyip-sync/— new:bcyip-sync.service.ts,bcyip-sync.module.tssrc/core/configuration/configuration.service.ts— addedsetTowerUrl()/getTowerUrl()src/core/configuration/config.types.ts—self.urlmarked@deprecatedsrc/core/initialization/initialization.service.ts— readstowerUrlfrom activation tokensrc/core/data/data.service.ts— addedgetActivationToken().companyIdexposuresrc/protocol/source/{gitlab,github,bitbucket}/*.service.ts— usegetTowerUrl()instead ofself.url
Frontend:
ui/src/pages/login/login.tsx— Nexus redirect flow,company_idparam, password fallback linkui/src/pages/login/LoginPassword.tsx— new: legacy password login pageui/src/pages/login/NexusCallback.tsx— new: OAuth callback handler (renamed fromMagicCallback)ui/src/pages/login/activate.tsx— URL mismatch screen with re-activation optionsui/src/pages/settings/Settings.tsx— URL field read-onlyui/src/components/layout/Sidenav.tsx— added Logout buttonui/src/components/atoms/BrandingLogo.tsx— localStorage cache + opacity fadeui/src/protocol/tower.api.ts— removededitSettings, addedreactivateUrl, renamedlogoutMagic→logout; exportedtowerApiui/src/protocol/branding.api.ts— uses sharedtowerApiinstance (no separate axios client)ui/src/protocol/type.ts— addedcompanyIdtoTowerStateui/src/routes.tsx— added/login/passwordand/auth/callbackroutesui/src/main.tsx— prefetch branding query on startupui/src/pages/login/login.test.tsx— rewritten for Nexus-based login