fix(auth): preserve raw BetterAuth cookie token for session lookup
All checks were successful
ci/woodpecker/push/api Pipeline was successful

This commit is contained in:
2026-02-18 23:06:37 -06:00
parent 9ac971e857
commit d2cec04cba
3 changed files with 57 additions and 5 deletions

View File

@@ -426,6 +426,21 @@ describe("AuthService", () => {
}); });
}); });
it("should preserve raw cookie token value without URL re-encoding", async () => {
const auth = service.getAuth();
const mockGetSession = vi.fn().mockResolvedValue(mockSessionData);
auth.api = { getSession: mockGetSession } as any;
const result = await service.verifySession("tok/with+=chars=");
expect(result).toEqual(mockSessionData);
expect(mockGetSession).toHaveBeenCalledWith({
headers: {
cookie: "__Secure-better-auth.session_token=tok/with+=chars=",
},
});
});
it("should fall back to Authorization header when cookie-based lookups miss", async () => { it("should fall back to Authorization header when cookie-based lookups miss", async () => {
const auth = service.getAuth(); const auth = service.getAuth();
const mockGetSession = vi const mockGetSession = vi

View File

@@ -150,22 +150,20 @@ export class AuthService {
} }
private buildSessionHeaderCandidates(token: string): SessionHeaderCandidate[] { private buildSessionHeaderCandidates(token: string): SessionHeaderCandidate[] {
const encodedToken = encodeURIComponent(token);
return [ return [
{ {
headers: { headers: {
cookie: `__Secure-better-auth.session_token=${encodedToken}`, cookie: `__Secure-better-auth.session_token=${token}`,
}, },
}, },
{ {
headers: { headers: {
cookie: `better-auth.session_token=${encodedToken}`, cookie: `better-auth.session_token=${token}`,
}, },
}, },
{ {
headers: { headers: {
cookie: `__Host-better-auth.session_token=${encodedToken}`, cookie: `__Host-better-auth.session_token=${token}`,
}, },
}, },
{ {

View File

@@ -112,3 +112,42 @@ Completed items:
Remaining step (requires deploy): Remaining step (requires deploy):
- Redeploy API with this patch and rerun live Playwright flow on `app.mosaicstack.dev` to confirm `/auth/session` returns `200` after callback. - Redeploy API with this patch and rerun live Playwright flow on `app.mosaicstack.dev` to confirm `/auth/session` returns `200` after callback.
## Playwright Re-Check (2026-02-19, later run)
Live flow evidence after previous deploy attempt:
1. OAuth callback succeeds:
- `GET https://api.mosaicstack.dev/auth/oauth2/callback/authentik?code=...&state=...` -> `302`
- Redirect target observed: `https://app.mosaicstack.dev/`
- Browser cookie jar includes:
- `__Secure-better-auth.session_token` on `api.mosaicstack.dev` (HttpOnly, Secure, SameSite=Lax)
2. Session bootstrap still fails immediately:
- `GET https://api.mosaicstack.dev/auth/session` -> `500`
- Response body shape:
- `{"success":false,"message":"An unexpected error occurred","errorId":"...","path":"/auth/session","statusCode":500}`
- Web app returns to login because session fetch fails.
3. Frontend version mismatch observed:
- Live `POST /auth/sign-in/oauth2` response from login flow still shows callback URL pointing to `/dashboard`.
- Current repository login page uses callback URL `/`.
- This indicates deployed web image is older than current `develop` code (or stale image tag in runtime).
## Additional Code Fix Applied Locally (pending push/deploy)
Refined cookie candidate construction in API session verification:
- File: `apps/api/src/auth/auth.service.ts`
- Removed URL-encoding of session token when constructing cookie headers.
- Cookie candidates now pass raw token value exactly as extracted from incoming cookie.
Why:
- BetterAuth cookie tokens can contain characters like `/`, `+`, and `=`.
- Re-encoding these values can mutate token bytes and cause lookup/parse failures.
Regression test added:
- File: `apps/api/src/auth/auth.service.spec.ts`
- `should preserve raw cookie token value without URL re-encoding`