Notechondria

Version: 0.1.64 Build Date: 2026-04-23T00:00

What's Changed

Bug (root-cause fix) — session probe no longer 401s on stale tokens

The 0.1.63 "Fresh token rejected by backend" SnackBar was me papering over the symptom. The real bug:

  • SessionApiView at backend/creators/api.py was declared permission_classes = [permissions.AllowAny], so the view was correctly willing to handle anonymous callers and return {"authenticated": false}. But DRF's default authentication chain runs before any view: DEFAULT_AUTHENTICATION_CLASSES = [TokenAuthentication, ApiKeyAuthentication], and TokenAuthentication.authenticate_credentials raises AuthenticationFailed("Invalid token.") on any unknown token — even against an AllowAny view. So every request carrying a stale saved token 401'd the session probe itself, making it impossible for the probe to tell the frontend "your token is stale" without simultaneously looking like a backend failure.
  • Fix: SessionApiView now declares authentication_classes = [] explicitly, and does a manual Token.objects.get(key=...) lookup inside the view. Response is always 200; body is {"authenticated": true, token, user, ...} when the token matches a row, {"authenticated": false} otherwise (missing header, malformed header, unknown token, or deactivated user).
  • Frontend revert: the 0.1.63 checkSession-based panic added to frontend/editor_app/lib/app_shell.dart applyAuthPayload is removed. It was diagnosing a symptom the backend now prevents. The boot-time _restoreSession in frontend/editor_app/lib/core/auth_flows.dart already handled {authenticated: false} correctly (it clears the persisted session silently) — no frontend change needed there.

Why tokens go stale in the first place

For the record (this is NOT a bug — just documentation of the legitimate reasons a saved token stops working):

  1. Backend swap. You point the frontend at a different backend than the one that issued the token (Render ↔ Northflank, local ↔ cloud). The authtoken_token table is per-DB; tokens are not portable. Every session probe against a new backend will return {authenticated: false} — that's correct behavior now.
  2. Password change. ChangePasswordApiView at backend/creators/api.py:714 deletes and recreates the token explicitly. The response body includes the new token; the frontend saves it. Any OTHER device with the pre-change token becomes stale — by design.
  3. Explicit logout. LogoutApiView at backend/creators/api.py:836 wipes the user's tokens. Same deal.
  4. Database reset. If you re-provision the Postgres addon (new Render DB, reset Northflank addon), the authtoken_token table is empty on the new DB. All prior tokens are orphaned. This is a platform/ops action, not a code bug.

We do NOT need to "refresh tokens on each redeploy" — the DB is persistent across redeploys on both Render and Northflank, so tokens survive. If the user sees 401s after a code redeploy (not a DB rebuild), the likely cause is #1 or #2 above, not a platform issue. With the SessionApiView fix, the frontend can correctly detect and recover from all four cases silently.

Files Changed

  • VERSION — bumped 0.1.63 → 0.1.64.
  • backend/creators/api.pySessionApiView gets authentication_classes = [] and an explicit Token.objects.get lookup so invalid/stale tokens yield 200 with {"authenticated": false} instead of 401.
  • frontend/editor_app/lib/app_shell.dart — reverted the 0.1.63 checkSession-on-applyAuthPayload block. The saved-token probe path in _restoreSession now handles stale tokens without surfacing an error.

Notes

  • Only SessionApiView gets the authentication_classes = [] treatment. Other AllowAny views (FrontPageApiView, NoteListCreateApiView with scope='all', …) keep DRF's default auth chain — a stale token sent to those still 401s. That's correct: for those endpoints, if you sent an Authorization header, you meant to authenticate, and a hard 401 is the right response. The session probe is the ONE endpoint where the whole point is "is my saved credential valid?" and for that endpoint the probe itself must not be gated on the credential being valid.
  • Planner and portal apps still have their own applyAuthPayload inlined. They never had the 0.1.63 checkSession panic so they need no revert — tracked in docs/TODO.md as the broader "replicate auth fixes across all three apps" item.