Files
orion/docs/proposals/store-login-platform-detection.md
Samir Boulahtit 319900623a
Some checks failed
CI / ruff (push) Successful in 14s
CI / pytest (push) Failing after 50m12s
CI / validate (push) Successful in 25s
CI / dependency-scanning (push) Successful in 32s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped
feat: add SQL query tool, platform debug, loyalty settings, and multi-module improvements
- Add admin SQL query tool with saved queries, schema explorer presets,
  and collapsible category sections (dev_tools module)
- Add platform debug tool for admin diagnostics
- Add loyalty settings page with owner-only access control
- Fix loyalty settings owner check (use currentUser instead of window.__userData)
- Replace HTTPException with AuthorizationException in loyalty routes
- Expand loyalty module with PIN service, Apple Wallet, program management
- Improve store login with platform detection and multi-platform support
- Update billing feature gates and subscription services
- Add store platform sync improvements and remove is_primary column
- Add unit tests for loyalty (PIN, points, stamps, program services)
- Update i18n translations across dev_tools locales

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:08:07 +01:00

3.1 KiB

Store Login: JWT Token Gets Wrong Platform

Status: Resolved Date: 2026-02-24 Resolved: 2026-03-10

Problem

When a user logs in to a store via /platforms/loyalty/store/FASHIONHUB/login, the JWT token should encode platform_id=3 (loyalty). Instead, it gets platform_id=2 (main). This causes the store sidebar menu to show the wrong modules — e.g., analytics (from "main") appears while loyalty (from "loyalty") is missing.

Root Cause Chain

  1. User visits http://localhost:8000/platforms/loyalty/store/FASHIONHUB/login
  2. PlatformContextMiddleware detects loyalty and rewrites path to /store/FASHIONHUB/logincorrect
  3. Login page renders with loyalty platform context — correct
  4. JavaScript login.js POSTs to apiClient.post('/store/auth/login', ...) which resolves to /api/v1/store/auth/loginno platform prefix
  5. PlatformContextMiddleware sees /api/v1/store/auth/login, doesn't match /platforms/*, defaults to "main" platform
  6. Store auth endpoint calls get_current_platform(request) → gets "main" (id=2) instead of "loyalty" (id=3)
  7. Token encodes platform_id=2, all subsequent menu/API calls use the wrong platform

Solution (Implemented)

The login endpoint uses a 3-source priority chain to resolve the platform:

Source How When it fires
Source 1: Middleware request.state.platform from domain/subdomain/custom-domain Production always — domain carries platform context in every request
Source 2: Request body platform_code field in login JSON body Dev mode — JS sends window.STORE_PLATFORM_CODE || localStorage.store_platform
Source 3: Fallback get_first_active_platform_id_for_store() Only on fresh browser in dev mode (no URL context, no localStorage)

Files Changed

File Change
app/modules/tenancy/routes/api/store_auth.py Added platform_code to StoreLoginResponse and /me response
app/modules/tenancy/schemas/auth.py Added platform_code to StoreUserResponse
app/modules/tenancy/static/store/js/login.js Save platform_code to localStorage on login; use as fallback in login request

Why Source 3 Fallback Is Safe

Source 3 only fires when both Source 1 and Source 2 have nothing — meaning:

  • Not on a platform domain (localhost without /platforms/ prefix)
  • No platform_code in request body (no STORE_PLATFORM_CODE on page, no localStorage)

This only happens on a completely fresh browser session in dev mode. In production, Source 1 always resolves because the domain itself identifies the platform.

Platform Resolution by URL Pattern

See middleware.md § "Login Platform Resolution" for the complete matrix.

Diagnostic Tools

  • Backend trace: /admin/platform-debug — simulates the full middleware + login resolution pipeline for any host/path combo
  • JS overlay: Ctrl+Shift+P on any store page (localhost only) — shows window.STORE_PLATFORM_CODE, localStorage.store_platform, JWT decoded platform, /auth/me response, and consistency checks