MotorSignal-App/docs/qa-2026-10-01/website-auth-followup.md

9.0 KiB
Raw Permalink Blame History

Website authentication follow-up

Date: 2026-10-01. Scope: local website Firebase authentication and Group Ride Hub diagnosis. All MotorSignal API traffic in this follow-up used 127.0.0.1; no request was sent to api.motorsignal.app. The isolated services used demo project demo-motorsignal-web-qa, website port 3101, Auth Emulator port 9199, Firestore Emulator port 8188, and token-verifying local fixture API port 8189. No production credentials or accounts were used.

Source and isolated setup

The website client initializes Firebase in website/lib/firebase-client.ts. It now honors NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_PORT and NEXT_PUBLIC_FIREBASE_FIRESTORE_EMULATOR_PORT, defaulting to 9099 and 8088 for existing local workflows. The QA run set them to 9199 and 8188. Invalid port values fail before Firebase app initialization. The Auth and Firestore clients used the same demo-motorsignal-web-qa project. Firestore emulator rules were loaded from a local copy whose SHA-256 matched firestore.rules (A731D6319B56D2A13960FCBE621004D448AE611DC5C5657FAB25F0EFBE302EC6).

The local Next server ran on 127.0.0.1:3101 with MOTORSIGNAL_API_BASE_URL=http://127.0.0.1:8189; the explicit override prevented the code's default https://api.motorsignal.app destination from being used. Browser request capture showed auth/session and Firestore traffic on loopback. Google Identity Toolkit and static Google/Tailwind assets were separately contacted by the browser for the popup and page styling. No MotorSignal production API request occurred in this follow-up.

The local API fixture in website/test-support/qa-auth-backend.mjs uses Firebase Admin verifyIdToken, then checks token audience and issuer against the demo project before issuing opaque in-memory QA session values. Its session endpoint validates those values. It does not implement MotorSignal account/profile or ride APIs and is not evidence of a real MotorSignal server session. It has no production configuration path: startup requires a demo- project and loopback Auth Emulator host.

Authentication trace and 401s

For Google sign-in, website/components/auth-form.tsx calls Firebase signInWithPopup, obtains credential.user.getIdToken(), then POSTs {id_token} to same-origin /api/auth/firebase-login. website/app/api/auth/[action]/route.ts maps this to /api/v1/auth/firebase/login at MOTORSIGNAL_API_BASE_URL, forwards the upstream status and body, and, on success, sets motorsignal_access_token and refresh cookies with HttpOnly, Secure, SameSite=Lax, Path=/, and a 30-day max age. /api/account/sessions reads the access cookie and forwards it as a bearer token to the backend sessions endpoint. This local HTTP loopback test successfully returned the Secure, Lax, root-path cookie on protected requests; the browser received 200 for /api/account/sessions twice per account. Harness logs recorded verified:true, the demo project, cookieBearerPresent:true, and sessionMatched:true, with only UID prefixes LZvemU5k (organizer) and hB1r4vgD (member).

Browser evidence for both Google accounts showed Auth Emulator identity-provider and account-lookup responses 200, same-origin Firebase login 200, and protected session reads 200. The fixture logged /api/v1/auth/sessions status 200 with a present bearer and matched local session. The account page's separate /api/v1/me request returned 404 because that endpoint is intentionally absent from the fixture; therefore the account profile workflow was not verified. Email login returned 401 by design: the local fixture logged email-auth-not-configured-in-local-fixture and the UI displayed “Email authentication is not configured in this local QA fixture.”

The earlier 401s against the default website setup cannot be attributed to a specific server rejection from this isolated run. The Next route defaults to https://api.motorsignal.app when MOTORSIGNAL_API_BASE_URL is unset, and that production endpoint was deliberately not called. A code-level issue was confirmed: Google sign-in did not check response.ok before redirecting to /account, so a failed session exchange could look like successful navigation.

Hub discovery and multi-user behavior

The website's website/components/group-rides/group-rides-management.tsx:245-274 listens to two user-scoped queries: group_rides where organizerUid == currentUser.uid, and group_rides where accessUids array-contains the current UID. It applies no public visibility, status, or time filter. firestore.rules:267-296 defines canReadRide() (public visibility is allowed for point reads) and a separate allow list requiring organizer UID, access UID, or active club-admin association. Thus the member's zero-result Hub was a successful empty response from these scoped queries, not a query error or authorization failure. The “Explore Community Rides” label does not currently query public rides, so public discovery is not implemented by this listener.

As the organizer (LZvemU5k UID prefix), the website created one ride through the actual Firestore client SDK and emulator rules. Firestore WebChannel write and listen requests returned 200. Emulator inspection found one group_rides record with ID ride_1790852960205_LZvemU, title Website Followup QA Ride 20261001, visibility public, join policy requestToJoin, organizer UID matching the creator, and accessUids containing that creator. The create flow also wrote the organizer participant document. The organizer Hub then showed one squad, one “My Pack Run”, one “Explore Community Rides”, one “Organized by Me”, and zero “Joined as Wingman”. Screenshot: website-followup-owner-hub.png.

The second authenticated member (hB1r4vgD UID prefix) saw zero rides because neither query matched that account. Selecting “Join with Code” and entering APEX84 produced the success toast “Synchronized telemetry with ‘Bay Area Dawn Patrol’” and a “Telemetry Linked” state, but no Firestore write followed. Source at website/components/group-rides/join-ride-modal.tsx:48-80 confirms this modal uses a 400 ms timeout and canned code mapping, not a ride lookup or membership mutation. The handleRequestSlot handler at website/components/group-rides/group-rides-management.tsx:481-491 also only calls setRides and shows a toast. Emulator inspection afterward showed the created ride still had participant count 1 and only the organizer UID in accessUids; no member participant document existed. Screenshot: website-followup-join-simulated.png. This is a UI-only false success, not a successful join.

The Hub's request-slot handler only updates local React state and shows a toast; it performs no backend write. A join-requests API route exists but the website UI does not call it, and no web approval flow was found. Therefore multi-user create/join/approve is blocked after the verified organizer create: member discovery, durable join, and approval are not implemented in the exercised website path. No product change was made to expand this scope.

Validation, evidence, and cleanup

Focused client tests ran with npm exec vitest run tests/firebase-client-emulator.test.ts --reporter=verbose: 6 passed, 0 failed. They cover emulator selection, custom isolated ports, and invalid port rejection. The run emitted the repository's existing Vite config-loader warning. No full website suite or production build was run; no product behavior code changed.

Screenshots are docs/qa-2026-10-01/website-followup-owner-hub.png and docs/qa-2026-10-01/website-followup-join-simulated.png. Browser and local service logs were inspected with tokens and cookie contents excluded. Auth/Firestore emulator data was test-only and was cleared after evidence capture. The local Next server, fixture API, Firebase emulators, and three dedicated browser sessions were stopped. The isolated ports were checked for listeners after shutdown. Existing root-owned QA services on 9099/8088/4400/4500 were not stopped. No test user or ride remains in the isolated emulators.

The website-only source/test changes are website/lib/firebase-client.ts and website/tests/firebase-client-emulator.test.ts. Temporary support files under website/test-support/ document the isolated emulator rules, Firebase config, and token-verifying fixture server; they are test-only and do not change production auth. Existing modified files, including website/next-env.d.ts, were preserved.

Post-diagnosis auth error handling fix

website/components/auth-form.tsx now requires a successful HTTP response from /api/auth/firebase-login before redirecting. A rejected response surfaces server detail (or a useful fallback) in the form. The network-error legacy-link fallback must also return a successful response. New UI regression test website/tests/auth-form.test.tsx first failed because no alert appeared after a 401 and the handler attempted navigation; after the fix it passes and verifies one request plus visible rejection feedback. Focused test: 1/1 PASS. Production auth code is not weakened and no API request to production was made.