Resolve current-user identity from the token, not the cached config username#242
Conversation
The cached config-file username goes stale when a user is renamed on the
backend (the token keeps authenticating as the renamed user), and using it
to build requests caused two problems:
- `novem -p/-g/-m/-d/-j` (without --for-user) listed via
`<type>(author: <config-username>)`. After a rename this returned the
wrong/empty set, and with a dead token it silently fell back to that
name's *public* view instead of the user's own visualizations.
- `is_me` highlighting, `@user~group` paths and comment-thread paths all
compared against / interpolated the stale name.
Fixes:
- List one's own VDEs via `me { plots { ... } }` etc. (new `list_my_*_gql`),
which resolves identity server-side from the token. A bad token yields
`me: null` -> empty, closing the public-view leak. `--for-user X` keeps
the author-filtered query (an intentional other-user/public lookup).
- Add cached `NovemGQL.current_username`, resolved via the lightweight REST
`/whoami` endpoint, and use it for `is_me` and own-group path building.
- Comment/topic "me" highlight reuses the `me { username }` the topics
query already returns (no extra round-trip).
- `Context._threads_base` prefers the token-resolved `_me` when loaded,
falling back to the config name offline (stays a pure path-builder).
- Share the VDE field selection between the author- and me-scoped queries.
A friendly "you are not authenticated" error on a null/empty identity is
left as a TODO seam in `_list_me_vis` for a follow-up change.
A configured token that the backend rejects (e.g. expired, or revoked after a rename) otherwise let the data commands fall through to empty or public-only results with no explanation. The CLI now checks /whoami before dispatching a data command: a definitive 401/403 prints a short re-auth message and exits 1. Transient/network errors fall through so the real command still surfaces its own error, and a missing token is left alone (anonymous access is valid). The whoami response does double duty: a successful one seeds a per-token identity cache on the ConfigManager singleton, which NovemGQL.current_username reuses — so the startup check is the only /whoami request per invocation. Library usage is unaffected; the guard lives in the CLI entry path only.
The CLI surfaced uncaught API errors as "Novem404: Resource not found: <url> (Are you authenticated?)" — leaking the internal exception class, and nagging about auth even though the CLI now validates the token at startup. - NovemException grows a `cli_message` hook (defaults to str(self)); Novem404 overrides it to drop the "(Are you authenticated?)" hint. The hint still shows for library callers via str(), since they have no up-front auth check. - _cli_excepthook prints `cli_message` for our own exceptions (no class-name prefix) and keeps `Type: message` for unexpected errors to aid debugging.
|
Does this veer into the space of #170 by any chance? |
It do! |
|
You can ask for multiple things in a gql query, so you can get both me and the comments in the same query, saving a roundtrio |
I do not think we should use an extra roundtrip request for each usage when one is enough with just adding a top level { me } to the gql query. If we need to tell it a user is auth or not then it's better to fire a second request on the 404, or if using gql, again use them me. Though 404 is mostly useful for the rest requests |
sondove
left a comment
There was a problem hiding this comment.
Would love to avoid the extra request, but will not hold up progress
I don't completely disagree. More than happy to review the pull request |

Why
The config-file
usernameis a cached label that goes stale when a user is renamed on the backend — the token keeps authenticating as the renamed user, but the stored name no longer matches. Using that cached name to build requests caused two bugs:novem -p/-g/-m/-d/-j(without--for-user) listed via<type>(author: <config-username>). After a rename this returned the wrong/empty set, and with a dead token it silently fell back to that name's public view instead of the user's own visualizations.is_mehighlighting,@user~grouppaths and comment-thread paths all compared against / interpolated the stale name.What
Commit 1 — token-resolved identity
me { plots { … } }etc. (newlist_my_*_gql), resolving identity server-side from the token. A bad token yieldsme: null→ empty, closing the public-view leak.--for-user Xkeeps the author-filtered query (an intentional other-user/public lookup).NovemGQL.current_username, resolved via the lightweight REST/whoamiendpoint, used foris_meand own-group path building.me { username }the topics query already returns (no extra round-trip).Context._threads_baseprefers the token-resolved_mewhen loaded, falling back to the config name offline (stays a pure path-builder — no forced network call).Commit 2 — polite CLI crash on a dead token
ConfigManagersingleton, whichcurrent_usernamereuses — so there's exactly one/whoamiper invocation.Commit 3 — clean CLI error messages
Novem404: Resource not found: <url> (Are you authenticated?)— leaking the internal exception class, and nagging about auth even though the token is already validated at startup.NovemExceptiongrows acli_messagehook;Novem404overrides it to drop the(Are you authenticated?)hint. The hint still shows for library callers viastr(exc), since they have no up-front auth check._cli_excepthookprintscli_messagefor our exceptions (no class-name prefix) and keepsType: messagefor unexpected errors, to aid debugging.Resource not found: <url>.Compatibility
No public SDK signatures change. The identity cache is token-keyed, CLI-only, cleared by
config.reset(), andSessioninstances get their own isolated cache — library identity resolution (Context/comments via the topics query) is untouched.Tests
690 passed, 4 skipped; flake8 + mypy clean. New coverage intests/test_cli_dead_token.py(dead-token abort, no-leak-before-listing, single-whoami double-duty, missing-token-not-treated-as-dead) andtests/test_cli_error_output.py(no class-name/hint leak in CLI output; hint kept for the library). Verified live against prod: own listings show only the user's VDEs,--for-usershows the other user's, dead token exits 1, andis_mehighlighting flows through the cached identity.Screenies