diff --git a/ar-aging-app/README.md b/ar-aging-app/README.md index 990da79..1563305 100644 --- a/ar-aging-app/README.md +++ b/ar-aging-app/README.md @@ -48,10 +48,10 @@ Key production behaviors: under another name is skipped. A month can never count a file twice. - **Login** (`AR_AUTH`) — per-user accounts via `manage.py add-user`; journal review/approval and FX confirmations record the signed-in user's verified name. -- **Password self-service** — users change or recover passwords with a 6-digit code emailed - to their account address (Settings, or "Forgot password?" on the login screen). Sent via - the company Mail API (`AR_MAIL_API_*`), SMTP fallback; `manage.py set-password` remains - the admin override. +- **Password self-service** — Settings changes the password with the current one; a + forgotten password is recovered from the login screen ("Forgot password?") via a 6-digit + code emailed to the account address (company Mail API `AR_MAIL_API_*`, SMTP fallback); + `manage.py set-password` remains the admin override. - **Exchange rates** — "Fetch month-end rates" pulls central-bank rates (Frankfurter, free, keyless; `AR_FX_PROVIDER`); fetched rates still require human confirmation (Control C5). - **Completed closings are locked** read-only; corrections need an explicit Reopen. diff --git a/ar-aging-app/backend/app/api/auth.py b/ar-aging-app/backend/app/api/auth.py index 9362b27..17eed93 100644 --- a/ar-aging-app/backend/app/api/auth.py +++ b/ar-aging-app/backend/app/api/auth.py @@ -173,15 +173,14 @@ def _user_from_request(request: Request) -> AuthUser | None: async def auth_middleware(request: Request, call_next): """Guards every /api/* route except OPEN_PATHS. Registered in api/main.py.""" path = request.url.path.rstrip("/") or "/" - if path.startswith("/api") and path not in OPEN_PATHS: + if path.startswith("/api"): + # Identity is attached whenever a valid token is present — including on open + # paths, so e.g. a signed-in password-code request knows who is asking. user = _user_from_request(request) - if user is not None: - request.state.user = user - elif auth_required(): + request.state.user = user + if user is None and path not in OPEN_PATHS and auth_required(): return JSONResponse({"detail": "Not signed in (or the session expired). " "Sign in to continue."}, status_code=401) - else: - request.state.user = None return await call_next(request) diff --git a/ar-aging-app/frontend/src/pages/Settings.tsx b/ar-aging-app/frontend/src/pages/Settings.tsx index ed65a54..08b6827 100644 --- a/ar-aging-app/frontend/src/pages/Settings.tsx +++ b/ar-aging-app/frontend/src/pages/Settings.tsx @@ -7,7 +7,7 @@ import { useAuth } from "../auth"; export default function Settings() { const { data: health } = useQuery({ queryKey: ["health"], queryFn: api.health }); - const { user, emailEnabled } = useAuth(); + const { user } = useAuth(); return (
Application defaults and security posture.
- {user && (emailEnabled - ?