Skip to content

web: sign out with server-side token revocation #229

Description

@rghvgrv

Parent

#196 — PRD: Angular web client for SubVora (parity minus reminders)

What to build

Signing out for real: revoke the refresh token server-side, clear local state, and land on the login screen — including in any other open tab. A sign-out that only clears the browser leaves a 30-day refresh token alive on the server, which is not what the user was promised.

Implementation Steps

  1. Store method — logout() in core/auth/auth.store.ts: POST /auth/logout with the stored refresh token, then clear the in-memory token and the storage key regardless of the response — a failed revoke must not trap the user in a signed-in-looking app.
  2. UI entry point — a sign-out item in the shell's account menu and on the settings screen, both calling the same store method, then navigating to /login.
  3. Cross-tab — clearing the storage key triggers the storage listener from slice 10, so other tabs sign out too; verify that path rather than assuming it.
  4. Spec — auth.store.spec.ts additions: logout posts the refresh token then clears state; a failing logout call still clears local state and navigates; after signing out, a guarded route redirects to /login.

Agent Routing

agent_routing:
  complexity_hint: quite-easy
  required_capability: fast
  parallel_safe: true
  cost_preference: low
  speed_preference: high
  ownership_scope:
    - src/SubVora.Web/src/app/core/auth/auth.store.ts
    - src/SubVora.Web/src/app/layout/shell.component.ts
    - src/SubVora.Web/src/app/features/settings/**
  verification:
    - cd src/SubVora.Web && npx ng test --no-watch

Technical Context Snapshot

Current stack in scope

  • UI: Angular (20+) standalone components with signals and built-in control flow, Angular Material (Material 3) as the only component library, SCSS. Static SPA — no SSR, no service worker.
  • State: signal-backed injectable stores, one per domain area, mirroring the MAUI ViewModel split in src/SubVora.Mobile/ViewModels one-to-one. No NgRx.
  • API access: hand-written models plus one service per API controller under src/SubVora.Web/src/app/core/api. Enums travel as JSON strings (JsonStringEnumConverter in Program.cs), so TypeScript string-literal unions are exact.
  • Backend consumed unchanged: ASP.NET Core net10.0, /api/v1/, JWT bearer in the Authorization header, tokens in JSON bodies (no cookies).
  • Tests: Angular CLI unit-test builder (Vitest runner; Karma is deprecated) with HttpTestingController. Stores, interceptors, mappers and utils only — no component-DOM or browser automation.

Dependencies in scope

  • Reuse: @angular/*, @angular/material, rxjs, and the utilities already added under src/SubVora.Web/src/app/core. No chart library, no date library, no HTTP wrapper library.
  • New dependency additions allowed for this slice: no. If a dependency looks unavoidable, stop and raise it on the issue rather than adding it.

Architecture alignment

  • Preserve the repo's load-bearing rules (CLAUDE.md): burn-rate maths is server-side and counts cycles, never days; currency conversion is a read-time projection and stored amounts are never overwritten; nothing advances next_billing_date on a timer; provider matching stays one SQL query; the mobile SQLite cache stays a read-only mirror.
  • There is deliberately no shared DTO project. Web models mirror the API's JSON contract by convention — a contract change means editing both sides.
  • create-git-issue provides routing hints only and assigns no concrete agent or model.
  • run-with-it remains the final runtime routing authority.

Integration touchpoints

  • Consumes POST /api/v1/auth/logout with the refresh token in the body plus a bearer header.
  • Server-side this revokes that refresh token; skipping the call would leave a 30-day token live.
  • Relies on the cross-tab storage listener so a sign-out propagates to other tabs.

Acceptance criteria

  • Signing out revokes the refresh token server-side and clears all local token state.
  • A failed logout request still signs the user out locally and navigates to /login.
  • Other open tabs are signed out too.

Blocked by

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestready-for-agentReady for autonomous agent execution

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions