feat(website): replace auth-astro with better-auth#1191
Open
fhennig wants to merge 24 commits into
Open
Conversation
Replaces auth-astro + @auth/core with better-auth, running in stateless mode (no database, JWT-based sessions). Removes the custom patch that was required for auth-astro TypeScript compatibility. - Add better-auth config (src/auth.ts) with GitHub OAuth and trustedProxyHeaders - Add catch-all API route at /api/auth/[...all] to handle auth requests - Replace getSession() calls across all pages and backendProxy with auth.api.getSession() - Replace signIn() in LoginButton with authClient.signIn.social() - Replace custom logout implementation with auth.api.signOut() - Update E2E test helper to use better-auth cookie name and JWT format - Remove auth-astro Astro integration from astro.config.mjs - Remove patches/auth-astro+4.2.0.patch and patch-package NOTE: session.user.id must be verified to contain the GitHub numeric user ID after a real login — see TODO in src/auth.ts. The E2E test token format is also a best-guess pending verification — see TODO in tests/helpers/auth.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
auth.api.signOut() returns typed data, not a Response object, so calling .headers.getSetCookie() on it threw a 500. The asResponse option makes it return a proper Response with Set-Cookie headers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
better-auth's session.user.id is a randomly generated internal ID, not the GitHub numeric ID the backend uses for ownership checks. Adds a getGitHubUserId() helper that retrieves the correct ID via auth.api.listUserAccounts() and updates backendProxy and the two collection pages that do ownership comparisons. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s user field Replaces per-page auth calls with a single middleware that runs once per request and populates Astro.locals. The GitHub numeric user ID is now stored as a dedicated `githubId` additionalField on the user object via `mapProfileToUser`, so it is available directly on `session.user` without any secondary lookups. - Add `authMiddleware` that calls `getSession` once and sets `context.locals.user` and `context.locals.session` - Configure `user.additionalFields.githubId` + `mapProfileToUser` in `auth.ts` to populate it from the GitHub profile at login time - Remove `getGitHubUserId` helper (no longer needed) - Update `backendProxy` to read `context.locals.user?.githubId` via `APIContext` instead of re-deriving the ID from headers - Update all pages/components to read from `Astro.locals` instead of calling `auth.api.getSession` directly - Update `App.Locals` types to use the inferred auth user type so `githubId` is typed correctly everywhere - Remove unused `jose` devDependency and E2E test auth helper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds tests/helpers/auth.ts with createAuthCookies() and setupAuthCookie() that craft valid better-auth session cookies for Playwright tests without needing a real OAuth flow. Also sets cookiePrefix to 'gen-spectrum' in auth config (required for the helper to match what the server sets). Note: E2E cookie injection is untested — cookieCache also needs to be enabled in auth.ts before getSession will accept the crafted session_data cookie without a store lookup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The patches/ directory was deleted when auth-astro was replaced with better-auth, causing the Docker build to fail. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Enables better-auth cookieCache (JWE, 1hr) so getSession can validate crafted session cookies without an in-memory store lookup. This makes the E2E auth helper work across the separate test/server processes. Also aligns the E2E test GitHub ID with the seeded collection's userId so ownership checks pass on the edit page. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
profile.id from GitHub is a number but githubId is declared as string. Without String(), the stored value is a number and ownership checks (currentUserId === collection.ownedBy) fail due to type mismatch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The type says string but GitHub returns a number at runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fengelniederhammer
requested changes
May 11, 2026
Contributor
fengelniederhammer
left a comment
There was a problem hiding this comment.
Found a few things, but looks good otherwise 👍
- Set 7-day session expiry explicitly - Add refreshCache: true so stateless JWE cookie stays fresh - Enable storeAccountCookie for fully stateless operation - Fix cookie prefix from 'gen-spectrum' to 'genspectrum' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Also fix cookie prefix to match auth.ts ('genspectrum').
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… to user Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion YAML Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
resolves #1189, #682, #1188
Summary
auth-astro+@auth/corewithbetter-auth, running in stateless mode (no database required, JWT-based sessions)patches/auth-astro+4.2.0.patch) that was needed to fix TypeScript issues in auth-astroChanges
src/auth.ts(new) — better-auth config with GitHub OAuth providersrc/pages/api/auth/[...all].ts(new) — catch-all route handling all auth requestssrc/middleware/authMiddleware.ts(new) — runs on every request, callsgetSessiononce and populatesAstro.localssrc/backendApi/backendProxy.ts— readscontext.locals.user?.githubIdviaAPIContextsrc/components/auth/LoginButton.tsx—signIn('github', ...)→authClient.signIn.social({ provider: 'github', ... })src/auth/logout.ts— custom@auth/corelogout →auth.api.signOut({ headers, asResponse: true })Astro.localsinstead of callingauth.api.getSessiondirectlyastro.config.mjs— removedauth()Astro integrationGitHub user ID handling
better-auth's
session.user.idis a randomly generated internal ID, not the GitHub numeric user ID. The backend uses the GitHub numeric ID for ownership checks (e.g. who can edit a collection).The solution:
user.additionalFields+mapProfileToUserinauth.tsstore the GitHub numeric ID directly on the user object asgithubIdat login time:String()is needed becauseprofile.idfrom GitHub is a number, but the backend stores and compares user IDs as strings. Without the coercion, ownership checks fail due to type mismatch.This means
session.user.githubIdis the GitHub numeric ID (as a string) everywhere — no secondary lookups needed. The auth middleware sets it onAstro.locals.useronce per request, and all pages and the backend proxy read it from there.E2E test cookie helper
tests/helpers/auth.tsprovidescreateAuthCookies(accountPayload, sessionPayload)andsetupAuthCookie(page, name)to craft valid better-auth session cookies for Playwright tests without a real OAuth flow. The cookies are signed/encrypted using the sameAUTH_SECRETthe server uses.cookieCache(JWE, 1hr) is enabled sogetSessioncan validate the craftedsession_datacookie without an in-memory store lookup.New Cookies
better-authsets 3 cookies. Asession_token,session_dataandaccount_data. Session data and account data are JWE, encrypted data.Test plan
/collections,/subscriptions) show the login prompt when logged outnpm run e2e