Skip to content

Ensemble Manager calls send no Authorization header — localStorage key mismatch #85

Closed

Description

@mosoriob

Question

Fix the localStorage key mismatch that stops the Ensemble Manager calls from being authenticated.

Found by the #76 smoke test, after a real login. The token store writes one key. The two
Ensemble Manager callers read a different key that nothing ever writes.

Key
Written by src/lib/auth/token-store.ts:7 mint.access_token
Read by src/pages/modeling/MintThread.tsx:153 access-token
Read by src/pages/modeling/thread/MintRuns.tsx:218 access-token

Verified live: after signing in on localhost:3000, localStorage holds exactly
mint.access_token and mint.access_expires_at. There is no access-token. A repo-wide grep
finds 'access-token' in two places only, both reads — nothing writes it.

It fails silently. Both call sites spread the header conditionally:

const token = localStorage.getItem('access-token');   // always null
...(token ? { Authorization: `Bearer ${token}` } : {})

So the request goes out with no Authorization header instead of raising an error. Affected:

  • POST {ENSEMBLE_MANAGER_API}/executionEngines/{engine} — submit runs (MintThread.tsx)
  • GET {ENSEMBLE_MANAGER_API}/executions/{id}/logs — run logs (MintRuns.tsx)

The fix is to use the store's own accessor rather than a string literal, so the key cannot
drift again:

import { getAccessToken } from '@/lib/auth/token-store';
const token = getAccessToken();

Still to confirm: the user-visible symptom. Unauthenticated GET /v1/executions/<id>/logs
returns 404 for an unknown id rather than 401, so it is not yet known whether TACC's Ensemble
Manager rejects these calls or accepts them anonymously. Check that during the authenticated
walkthrough on #76. Either way the token should be attached.

Add a regression test that asserts the outgoing Authorization header, not just the storage key
— the current tests pass ensembleManagerApi="http://ensemble" and never assert the header.

Activity

  1. added
    wayfinder:taskWayfinder ticket: manual work that unblocks a decision
    on Aug 8, 2026
  2. self-assigned this
    on Aug 8, 2026
  3. mosoriob commented on Aug 8, 2026

    @mosoriob
    ContributorAuthor

    Fixed in PR #87, open against develop. The diagnosis in the body held exactly: the key was wrong, nothing wrote access-token, and the conditional spread turned it into silence.

    The symptom is now known — it was never silent on the server side. I read the Ensemble Manager source rather than guessing from the 404 probe, which could not distinguish the cases. Both routes forward req.headers.authorization into their service, and both services demand a token when the execution engine is tapis, which is what TACC runs (window.REACT_APP_EXECUTION_ENGINE = "tapis" at mint.tacc.utexas.edu/config.js):

    Call Server behaviour with no token What the user saw
    POST /executionEngines/tapis executionsTapisService throws a plain Error("Unauthorized"), which is not an HttpError, so the route falls to the 500 branch "Ensemble manager returned 500"
    GET /executions/{id}/logs logsService throws UnauthorizedError → 401 "Error loading log: HTTP 401" in the dialog

    So both looked like server faults, not a missing credential. The 404 my probe returned is the unknown id path — fetchLog looks the execution up before it reads the header, so an unauthenticated call on a nonexistent id 404s whether or not a token is attached. That is why the ticket could not settle the symptom by probing.

    The fix went one step past the ticket's prescription. Using getAccessToken() at both call sites stops the key drifting, but it leaves the submit path untestable: MintThread builds its execution state locally and starts it at null, so no test can drive the wizard to Runs and click Send. Both calls now live in src/lib/ensemble-manager.ts (submitRuns, fetchExecutionLog, ensembleManagerHeaders), so the header is built once and both endpoints are directly assertable. MintRuns keeps its abort controller and ANSI cleanup; MintThread keeps its state updates.

    Tests assert the outgoing header, as asked — a test on the storage key would have passed while every call went out anonymous.

    • src/lib/__tests__/ensemble-manager.test.ts — both endpoints, logged in and logged out, plus abort-signal forwarding and error mapping.
    • MintRuns.test.tsx — one guard through the component, clicking View Log. I checked it fails against the old code, so it is not vacuous.

    Full suite: 750 passed, tsc -b clean, no new lint.


    Fixing this exposed a second defect, filed as #88: run submission still cannot work at TACC. ui-react hardcodes localex as the execution engine, and the Ensemble Manager exposes exactly one execution-engine route, /executionEngines/tapis — confirmed in server.ts:93 and in the deployed spec at ensemble-manager.mint.tacc.utexas.edu/v1/ui/. POST /v1/executionEngines/localex is an unmatched route and 404s. This fix was necessary but not sufficient; #88 carries the rest. The log call, which uses a real route, is fully fixed by this ticket.

  4. added a commit that references this issue on Aug 8, 2026
    4f7debf
  5. mosoriob commented on Aug 8, 2026

    @mosoriob
    ContributorAuthor

    PR #87 merged to develop as 4f7debf. The log call is fixed on develop; run submission still 404s until #88.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

wayfinder:taskWayfinder ticket: manual work that unblocks a decision

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions