feat(idaa): server-side Novi member verification endpoint
Proxies GET /customers/{uuid} to Novi AMS server-to-server so members'
browser IPs are no longer in the call path, eliminating false "Access
Denied" for users on hotel/conference WiFi, VPNs, and CDN-filtered nets.
- New router: GET /v3/action/idaa/novi_member/{uuid}
- Business logic in app/methods/idaa_novi_verify_methods.py
- Redis cache (4h TTL, key: idaa:novi_member:{uuid})
- 404 never cached (recently-joined member anti-pattern)
- Email space→+ normalization (Novi quirk)
- Display name: "FirstName L." format with Name field fallback
- Registered in registry.py under /v3/action/idaa tag
- 9 unit tests covering all response paths (200/404/429/503/unreachable,
cache hit, email normalization, display name format)
- Frontend guide (Section 12) and tests/README updated with full spec
and migration table for frontend hand-off
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,42 @@
|
||||
- [ ] **Step 2:** Coordination (Verify Frontend uses `x-account-id` instead of token).
|
||||
- [ ] **Step 3:** Frontend V3 WebSocket integration test — queued after IDAA-specific work. Backend is ready (auth wired, heartbeat presence refresh confirmed, unit tests passing). Frontend guide updated at `GUIDE__AE_API_V3_for_Frontend_websockets.md`.
|
||||
|
||||
## 🔌 IDAA: Server-Side Novi Verification (Mini Project)
|
||||
> **Status: P1–P4 Complete (May 2026).** Endpoint live at `GET /v3/action/idaa/novi_member/{uuid}`. P5 (frontend migration) is the remaining step.
|
||||
> Rationale and frontend integration notes: `aether_app_sveltekit/documentation/CLIENT__IDAA_and_customized_mods.md` → "Planned: Server-Side Novi Verification"
|
||||
|
||||
**Goal:** Proxy the Novi member-verification call server-to-server (FastAPI → Novi) so members' browser IPs are no longer in the call path.
|
||||
|
||||
- [x] **[P1] New router:** `app/routers/api_v3_actions_idaa.py`
|
||||
- Route: `GET /v3/action/idaa/novi_member/{uuid}`
|
||||
- Required auth: `Depends(get_account_context)` — valid API key + any account context (x-account-id, JWT, or bypass). This is the standard V3 gate.
|
||||
- Reads `novi_idaa_api_key` / `novi_api_root_url` from site `cfg_json` via `_load_idaa_cfg()` (same as Mailman bridge)
|
||||
- Calls Novi: `GET {novi_api_root_url}/customers/{uuid}` with `Authorization: Basic {api_key}`
|
||||
- Normalize email: `.replace(' ', '+')` (Novi quirk — see Novi-Mailman bridge notes)
|
||||
- Build display name: `"{FirstName} {LastName[0]}."` format, fall back to `Name` field
|
||||
- Returns `{ "verified": true, "full_name": "...", "email": "..." }` on success
|
||||
- Returns `404` if Novi 200 with no identity data (empty-member anti-pattern)
|
||||
- Returns `429` if Novi rate limits; `503` if Novi unreachable or 5xx
|
||||
- Business logic in `app/methods/idaa_novi_verify_methods.py`
|
||||
|
||||
- [x] **[P2] Redis cache:**
|
||||
- Key: `idaa:novi_member:{uuid}` — TTL 4 hours
|
||||
- Note: `account_id` dropped from key — Novi credentials are hardcoded to the IDAA site; same UUID always returns the same data regardless of caller, so per-caller scoping wastes Redis space and halves hit rate.
|
||||
- Cache only verified (200) results — do NOT cache 404 (member may have just joined)
|
||||
- Uses `redis_client` from `lib_redis_helpers.py` directly
|
||||
|
||||
- [x] **[P3] Register in registry:** Added to `routers/registry.py` at `/v3/action/idaa` tag `IDAA Actions (V3)`. Confirmed live — endpoint appears in `/openapi.json`.
|
||||
|
||||
- [x] **[P4] Tests:** `tests/unit/test_unit_idaa_novi_verify.py` — 9 tests, all passing.
|
||||
- Mock Novi responses (200/empty-200/404/429/503/unreachable)
|
||||
- Verify Redis cache is set on 200, hit bypasses Novi call
|
||||
- Verify email normalization (space → +)
|
||||
- Verify display name format (5 cases)
|
||||
|
||||
- [ ] **[P5] Coordinate with Frontend Agent** once P1–P3 are done:
|
||||
- Frontend replaces direct `fetch()` to Novi in `+layout.svelte:verify_novi_uuid()`
|
||||
- Map response codes: 200 → verified, 404 → denied, 429 → `'rate_limited'`, 503 → `'api_error'`
|
||||
|
||||
## 🛡️ Security & Privacy Baseline (IDAA)
|
||||
- **Status:** **ENFORCED**.
|
||||
- **Maintenance:** Run `tests/e2e/test_e2e_v3_security_audit.py` after ANY router or registry change.
|
||||
|
||||
Reference in New Issue
Block a user