Constant re-login on the NAS — sessions bounce to /auth/error?reason=profile-missing
Symptom¶
Since the app moved off Vercel onto the NAS (T-073 / T-074), the owner has to re-login constantly.
Screenshot shows p-eop.theestablishers.com/auth/error?reason=profile-missing → "Something went wrong
while verifying your access." Reported 2026-06-22 (verbatim):
"Ever since I started hosting the web app on my NAS, I constantly need to re-login to my web app because of reason unknown… It wasn't like this when the web app was hosted on Vercel"
Diagnosis (code-derived; confirmed against the auth flow)¶
It is not session expiry — it's a fail-closed RBAC re-check tripped by the NAS's flaky uplink.
- The NextAuth JWT callback (
pages/api/auth/[...nextauth].ts) re-readsaote-system/users/{uid}every 60s (REFRESH_INTERVAL_MS) viagetUserProfileStrict(uid). - That reader (
lib/rbac/claims.ts) throws on infra errors by design (so an infra failure isn't mistaken for a deleted profile). The callback's catch block then callsmarkProfileUnverified()→profileMissing = true(the comment: "Fail closed: stale RBAC data is not safe…"). A transient network blip and a genuinely-deleted profile were treated identically. - The next protected request hits
lib/rbac/serverAuthApp.ts(if (user.profileMissing) redirect('/auth/error?reason=profile-missing')) → the screenshot.
Why the NAS and not Vercel: per docs/eop-tasks/runbooks/claude-nas-channel.md, the NAS's WAN is a
4G/CGNAT mobile modem. The every-60s Firestore round-trip over that high-latency link times out
intermittently → throws → fail-closed → bounce. On Vercel the app ran in a GCP datacenter adjacent to
Firestore, where this read essentially never failed, so the fail-closed branch was never exercised.
Same code, very different network reliability. lib/firebaseAdmin.ts also sets preferRest: true
("optimized settings for serverless") — a Vercel-era tuning now running on a long-lived process.
Ruled out¶
- Session-secret rotation:
loadSecrets()falls back to a stable'dev-secret'whenNEXTAUTH_SECRETis unset, so restarts can't be silently invalidating JWTs. - Distinct from I-002 (client-side Firebase
sessionStoragesign-in error on p-eop) — that's the browser redirect layer; this is the server-side RBAC re-read.
Not confirmable this session¶
Live NAS-log confirmation wasn't possible — the DSM env var held only the URL
(https://nas.theestablishers.com), no account=/password=, so the DSM Web API couldn't be
authenticated. Decisive log lines if creds are restored: [auth] Failed to refresh access profile
(throw path) vs. a [firebaseAdmin] … applicationDefault() warning (creds/DB path).
Escalation — T-088 (the structural fix)¶
Escalated 2026-06-22 to T-088: make the periodic re-read tolerate a transient link failure by
holding last-known-good RBAC for a bounded grace window — gated so it is self-disabling on healthy
infrastructure (owner's explicit requirement). Fix specced + implemented there.
2026-07-01 — fixed (via T-088)¶
Resolved via T-088 (done, PR #782) — gated-resilient RBAC refresh fixed the NAS constant-relogin. [owner: close I-005] - Source (edit): EOP Local Assistance (fork) · session edb0abc7-6373-449b-8e31-379fd38d391b