Skip to content

test: cover the Keycloak issuer migration and cross-app token handoff - #10

Merged
saqibmanan merged 3 commits into
mainfrom
test/keycloak-migration-regression
Sep 2, 2026
Merged

test: cover the Keycloak issuer migration and cross-app token handoff#10
saqibmanan merged 3 commits into
mainfrom
test/keycloak-migration-regression

Conversation

@saqibmanan

Copy link
Copy Markdown
Contributor

Repairs two guards the Keycloak migration silently broke, and adds the regression test that would have caught ParakhAI-Backend#107.

Broken guards

tests/e2e/test_add_model_flow.py detected a bounce to login with:

if "opub-kc" in page.url or "auth/realms" in page.url:

Both halves are now dead. opub-kc.civicdatalab.in is decommissioned, and Keycloak moved from /auth to the domain root, so the live redirect is auth.civicdatalab.in/realms/... and neither substring appears. A session expiry stopped being detected and the test failed with a confusing locator timeout instead of skipping cleanly.

Now matched host-agnostically on /realms/ plus login keywords, so it works against either server.

New coverage

  • sign-in redirects to the migrated issuer
  • sign-in never touches the decommissioned opub-kc host
  • a live ParakhAI token is accepted by CivicDataSpace

The third one is the point

ParakhAI's DataSpaceAuthMiddleware forwards user tokens to the CivicDataSpace backend. A cross-issuer mismatch degrades every authenticated request to anonymous rather than erroring — the exact silent failure in ParakhAI-Backend#107. The test logs in for real and POSTs the resulting token to the CDS token-exchange endpoint.

Deliberately not asserted via GraphQL. I verified on dev that { myAssignments { id } } returns an identical empty result authenticated and unauthenticated — audits and auditorAssignments likewise. Any assertion built on those would have passed whether or not the handoff worked, which is worse than no test.

Verification

All 3 pass against dev. Proven non-vacuous: tampering the token makes the handoff test fail with the production symptom, 401 {"error":"Invalid or expired token"}. ruff check clean.

The session-expiry guards in test_add_model_flow.py matched on
`"opub-kc" in page.url or "auth/realms" in page.url`. Both halves are
now dead: opub-kc.civicdatalab.in is decommissioned, and Keycloak moved
from /auth to the domain root, so the live redirect is
auth.civicdatalab.in/realms/... and neither substring appears.

A bounce to login therefore stopped being detected and the test failed
with a confusing locator timeout instead of skipping cleanly.

Match host-agnostically on /realms/ plus login keywords, so the guard
works against either Keycloak server.
Three regression tests for the auth.civicdatalab.in migration:

- sign-in redirects to the migrated issuer (api, regression)
- sign-in never touches the decommissioned opub-kc host (api, regression)
- a live ParakhAI token is accepted by CivicDataSpace (api, regression,
  auth)

The third is the one that matters. ParakhAI's DataSpaceAuthMiddleware
forwards user tokens to the CivicDataSpace backend, so a cross-issuer
mismatch degrades every authenticated request to anonymous rather than
erroring - the exact silent failure in ParakhAI-Backend#107. It logs in
for real and POSTs the resulting token to the CDS token-exchange
endpoint.

Deliberately not asserted via GraphQL: verified on dev that
`{ myAssignments { id } }` returns an identical empty result
authenticated and unauthenticated, so such an assertion would pass
whether or not the handoff works.

Proven non-vacuous: tampering the token makes the test fail with the
production symptom (401 "Invalid or expired token").
The handoff test asserted `status_code != 401`. A 5xx satisfies that
too, so it passed while the CivicDataSpace token-exchange endpoint was
timing out entirely - proving nothing about whether the token is
accepted. Found by running the CivicDataSpace API suite against dev,
where the same endpoint returned 504.

Now asserts == 200, and separates the two failure modes:

- 401/403 -> the token was rejected. That is the ParakhAI-Backend#107
  issuer mismatch and still fails the test.
- 502/503/504 or a client timeout -> the endpoint could not answer, so
  acceptance cannot be evaluated. Retried three times with a 90s
  timeout (longer than nginx's own 60s, so slowness arrives as a status
  code rather than a ReadTimeout), then skipped with a message naming
  the defect.

Skipping there is deliberate. Failing would report an issuer mismatch
that has not been shown, and retrying harder would dress a broken
endpoint up as a passing test.

The endpoint is genuinely degraded on dev: measured 504, 200 in 7s, 200
in 42s, 504 across four consecutive calls, while Keycloak itself answers
in 0.13s and the backend's own /health/ in 0.2s - so the latency is
inside the exchange handler, not the Keycloak migration.

Verified: 3 consecutive runs green, the skip path exercised live, and a
tampered token still fails with HTTP 401.
@saqibmanan
saqibmanan merged commit 1568c96 into main Sep 2, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant