Appearance
Auth Route Browser Smoke
Scope
This opt-in Playwright smoke proves the login page can observe the non-default auth-native gateway route table from a real browser context. It opens /auth/login, clicks the Google Auth entry, and verifies the browser response for GET /api/auth/google/config came through iam-service.
It also fetches unauthenticated /api/auth/me from the page context and checks the IAM AUTH_UNAUTHORIZED envelope. The legacy sibling route guard remains enabled by default so /api/auth/me/profile must stay legacy-proxied. Because the login page sends X-Request-Id, live browser mode also proves the gateway can answer CORS preflight locally before the browser sends the real auth request.
Required Runtime
Start these processes before live browser mode:
- Web app, default
WEB_BASE_URL=http://localhost:3000 - API gateway, default
GATEWAY_BASE_URL=http://localhost:8085 iam-service, default target fromdeploy/gateway/routes.auth-native-localhost-example.json- gateway route table:
GATEWAY_ROUTE_TABLE=deploy/gateway/routes.auth-native-localhost-example.json
The web app must be started with VITE_API_BASE_URL pointing at the gateway base URL. If the frontend points at a legacy API process, the smoke fails because browser responses will not include gateway route headers.
Run
Static self-test:
bash
cd go-platform
AUTH_ROUTE_BROWSER_SELF_TEST=1 make test-auth-routes-browserConfirmed browser run:
bash
cd go-platform
AUTH_ROUTE_BROWSER_CONFIRM=auth-native \
WEB_BASE_URL=http://localhost:3000 \
GATEWAY_BASE_URL=http://localhost:8085 \
make test-auth-routes-browserSession lifecycle browser run:
bash
cd go-platform
AUTH_SESSION_BROWSER_CONFIRM=auth-session \
WEB_BASE_URL=http://localhost:3000 \
GATEWAY_BASE_URL=http://localhost:8085 \
make test-auth-routes-session-browserUseful optional variables:
AUTH_ROUTE_BROWSER_HEADLESS=0to see Chromium.AUTH_ROUTE_BROWSER_PATH=/auth/loginto override the page path.AUTH_ROUTE_BROWSER_GOOGLE_BUTTON_SELECTOR=.htz-google-buttonto override the Google entry selector.AUTH_ROUTE_BROWSER_EXPECT_ROUTE_SET=candidate-readonlywithAUTH_ROUTE_BROWSER_CONFIRM=auth-candidatefor the IAM/Profile default candidate proof. This narrower mode only allowsauth-google-configandauth-meto be native while blocked auth/profile/KYC routes remain inactive.AUTH_ROUTE_BROWSER_SKIP_LEGACY_GUARD=1only when the isolated smoke environment cannot reach the broad legacy sibling route.AUTH_ROUTE_BROWSER_ARTIFACT_DIR=output/playwright/<name>to control artifact output.
Assertions
The smoke fails unless:
- the route table has exact native auth carve-outs before the broad
/api/authroute auth-google-config,auth-me,auth-login, andauth-registertargetiam-service- broad
/api/auth, broad/api/admin, andfallbackstaylegacy_proxy - the browser observes
X-Hoctapaz-Gateway-Route: auth-google-configforGET /api/auth/google/config - the browser observes
X-Hoctapaz-Gateway-Route-State: native_read - browser-originated auth requests receive gateway CORS headers for
X-Request-Idand credentialed requests - unauthenticated
/api/auth/mereturns401withAUTH_UNAUTHORIZED - the sibling profile route remains on the broad legacy auth route unless explicitly skipped
Live artifacts are written under output/playwright/ and include response headers, response bodies, a summary file, and a login page screenshot. The session lifecycle smoke writes redacted response artifacts only; raw passwords, access tokens, and refresh tokens are not persisted.
Latest Local Proof
On 2026-07-09, browser mode passed against local public-web, iam-service, and api-gateway:
bash
VITE_API_BASE_URL=http://localhost:18087 pnpm --filter @hoctapaz/public-web devbash
HTTP_ADDR=:8081 \
AUTH_JWT_SECRET=local-hoctapaz-development-secret-change-before-deploy \
GOTOOLCHAIN=go1.25.11 \
go run ./services/iam-service/cmd/serverbash
HTTP_ADDR=:18087 \
LEGACY_API_BASE_URL=http://127.0.0.1:9 \
GATEWAY_ROUTE_TABLE=deploy/gateway/routes.auth-native-localhost-example.json \
AUTH_JWT_SECRET=local-hoctapaz-development-secret-change-before-deploy \
GOTOOLCHAIN=go1.25.11 \
go run ./services/api-gateway/cmd/serverThen:
bash
AUTH_ROUTE_BROWSER_CONFIRM=auth-native \
AUTH_ROUTE_BROWSER_ARTIFACT_DIR=output/playwright/auth-route-browser-smoke-20260709-115940 \
AUTH_ROUTE_BROWSER_HEADLESS=1 \
WEB_BASE_URL=http://localhost:5205 \
GATEWAY_BASE_URL=http://localhost:18087 \
make test-auth-routes-browserArtifacts were written under:
txt
output/playwright/auth-route-browser-smoke-20260709-115940/Observed route evidence:
/api/auth/google/config: status200, routeauth-google-config, statenative_read,Access-Control-Allow-Origin: http://localhost:5205, andAccess-Control-Allow-Credentials: true./api/auth/me: status401, routeauth-me, statenative_read, and IAM error codeAUTH_UNAUTHORIZED./api/auth/me/profile?source=auth-browser-smoke: status502, routeauth, statelegacy_proxy. The502is expected because the legacy target was a deliberate dummy endpoint.
The local processes were stopped after the run.
Latest Session Lifecycle Proof
On 2026-07-09, session lifecycle browser mode passed against local public-web, iam-service, and api-gateway:
bash
VITE_API_BASE_URL=http://localhost:18095 pnpm --filter @hoctapaz/public-web dev --host 127.0.0.1 --port 5207bash
HTTP_ADDR=:8081 \
AUTH_JWT_SECRET=local-hoctapaz-development-secret-change-before-deploy \
GOTOOLCHAIN=go1.25.11 \
go run ./services/iam-service/cmd/serverbash
HTTP_ADDR=:18095 \
LEGACY_API_BASE_URL=http://127.0.0.1:9 \
GATEWAY_ROUTE_TABLE=deploy/gateway/routes.auth-native-localhost-example.json \
AUTH_JWT_SECRET=local-hoctapaz-development-secret-change-before-deploy \
GOTOOLCHAIN=go1.25.11 \
go run ./services/api-gateway/cmd/serverThen:
bash
AUTH_SESSION_BROWSER_CONFIRM=auth-session \
AUTH_SESSION_BROWSER_ARTIFACT_DIR=output/playwright/auth-route-session-browser-smoke-20260709-100744 \
AUTH_SESSION_BROWSER_HEADLESS=1 \
WEB_BASE_URL=http://127.0.0.1:5207 \
GATEWAY_BASE_URL=http://localhost:18095 \
make test-auth-routes-session-browserArtifacts were written under:
txt
output/playwright/auth-route-session-browser-smoke-20260709-100744/Observed route evidence:
POST /api/auth/register: status201, routeauth-register, statenative_write.POST /api/auth/login: status200, routeauth-login, statenative_write.- authenticated
GET /api/auth/me: status200, routeauth-me, statenative_read. POST /api/auth/refresh: status200, routeauth-refresh, statenative_write, and the refresh token rotated.- old refresh token reuse: status
401, routeauth-refresh, statenative_write. - refreshed-session
GET /api/auth/me: status200, routeauth-me, statenative_read. POST /api/auth/logout: status200, routeauth-logout, statenative_write.- refresh after logout: status
401, routeauth-refresh, statenative_write.
The response artifacts redact access tokens, refresh tokens, token type fields, and passwords. The local processes were stopped after the run.
Rollback
Switch GATEWAY_ROUTE_TABLE back to deploy/gateway/routes.json, remove the native auth route-table entries, or set the exact auth carve-outs back to legacy_proxy. This smoke writes no IAM/Profile data.