Skip to main content
Version: 3.1.0

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-auth module handles the OAuth callback (/auth/callback) and code exchange with the gateway.
  • bcyip-sync module (ex magic-auth) handles the URL heartbeat with BCYIP.
  • InitializationService reads towerUrl from the activation token on startup and injects it into ConfigurationService.getTowerUrl() — the single source of truth for the Tower's public URL.
  • PUT /config/settings removed; POST /reactivate-url added for operator-driven URL drift recovery.
  • When the gateway detects a URL mismatch, it redirects to /activate?reason=url_mismatch where 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/password route 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 (deprecates self.url in YAML config)
  • URL mismatch detection + dedicated re-activation screen in /activate
  • Dynamic branding: company_id passed to Nexus for company logo display
  • Logout button in Sidenav
  • BrandingLogo uses localStorage cache + opacity fade to prevent logo flash
  • /login/password fallback route for legacy auth
  • Nexus web URL derived automatically from BCYIP_TARGET_ENV — no NEXUS_WEB_URL env 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 from BCYIP_TARGET_ENV, NEXUS_WEB_URL env var kept as override
  • src/core/auth/nexus/nexus-auth.controller.tsGET /auth/config uses getNexusWebUrl() instead of reading NEXUS_WEB_URL directly
  • src/app.controller.ts — removed PUT /config/settings, added POST /reactivate-url
  • src/app.module.ts — replaced MagicAuthModule with NexusAuthModule + BcyipSyncModule
  • src/config.dto.ts — removed UpdateSettingsDto, added ReactivateUrlDto
  • src/core/auth/nexus/ — new: nexus-auth.controller.ts, nexus-auth.dto.ts, nexus-auth.module.ts
  • src/core/bcyip-sync/ — new: bcyip-sync.service.ts, bcyip-sync.module.ts
  • src/core/configuration/configuration.service.ts — added setTowerUrl() / getTowerUrl()
  • src/core/configuration/config.types.tsself.url marked @deprecated
  • src/core/initialization/initialization.service.ts — reads towerUrl from activation token
  • src/core/data/data.service.ts — added getActivationToken().companyId exposure
  • src/protocol/source/{gitlab,github,bitbucket}/*.service.ts — use getTowerUrl() instead of self.url

Frontend:

  • ui/src/pages/login/login.tsx — Nexus redirect flow, company_id param, password fallback link
  • ui/src/pages/login/LoginPassword.tsx — new: legacy password login page
  • ui/src/pages/login/NexusCallback.tsx — new: OAuth callback handler (renamed from MagicCallback)
  • ui/src/pages/login/activate.tsx — URL mismatch screen with re-activation options
  • ui/src/pages/settings/Settings.tsx — URL field read-only
  • ui/src/components/layout/Sidenav.tsx — added Logout button
  • ui/src/components/atoms/BrandingLogo.tsx — localStorage cache + opacity fade
  • ui/src/protocol/tower.api.ts — removed editSettings, added reactivateUrl, renamed logoutMagiclogout; exported towerApi
  • ui/src/protocol/branding.api.ts — uses shared towerApi instance (no separate axios client)
  • ui/src/protocol/type.ts — added companyId to TowerState
  • ui/src/routes.tsx — added /login/password and /auth/callback routes
  • ui/src/main.tsx — prefetch branding query on startup
  • ui/src/pages/login/login.test.tsx — rewritten for Nexus-based login