Recover from a dead session, and report a node problem once
An expired or orphaned session left the app on a half-rendered page instead of the login screen: a properly signed token naming a user who no longer exists answered 404, which the client does not treat as an authentication failure. All failures in get_current_user are 401 now, and the client stops retrying them, so a dead session goes straight back to the login screen. - Clicking an edge names the two nodes it runs between, not just the message. - A node with a problem shows one badge carrying the whole explanation, rather than a corner badge and a status dot saying the same thing twice. The dot is back to what it is good at: whether the node ran. - The sidebar's collapse control sits in the sidebar, where a phone still finds one in the chrome because there is no sidebar on screen to hold it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WzrvW7rjQbynnhF6pxh6i
This commit is contained in:
co-authored by
Claude Fable 5
parent
6fb42bb3ef
commit
c254d487ba
@@ -0,0 +1,45 @@
|
||||
"""A session the server will not accept must say so as an auth failure.
|
||||
|
||||
The client clears the stored token and returns to the login screen on 401. Any
|
||||
other status leaves the browser holding a token that can never work, stuck on a
|
||||
page that will not load.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.core import security
|
||||
from app.core.config import settings
|
||||
|
||||
PROTECTED = [
|
||||
f"{settings.API_V1_STR}/users/me",
|
||||
f"{settings.API_V1_STR}/flows/",
|
||||
]
|
||||
|
||||
|
||||
def token_for(subject: str) -> str:
|
||||
return security.create_access_token(subject, timedelta(days=1))
|
||||
|
||||
|
||||
def test_no_token_is_unauthorised(client: TestClient) -> None:
|
||||
for path in PROTECTED:
|
||||
assert client.get(path).status_code == 401
|
||||
|
||||
|
||||
def test_a_token_we_did_not_sign_is_unauthorised(client: TestClient) -> None:
|
||||
headers = {"Authorization": "Bearer not.a.real.token"}
|
||||
for path in PROTECTED:
|
||||
assert client.get(path, headers=headers).status_code == 401
|
||||
|
||||
|
||||
def test_a_token_for_a_user_who_is_gone_is_unauthorised(
|
||||
client: TestClient,
|
||||
) -> None:
|
||||
# Properly signed, but the user it names no longer exists — which is what a
|
||||
# browser holds after the database is reset.
|
||||
headers = {"Authorization": f"Bearer {token_for(str(uuid.uuid4()))}"}
|
||||
for path in PROTECTED:
|
||||
response = client.get(path, headers=headers)
|
||||
assert response.status_code == 401, path
|
||||
Reference in New Issue
Block a user