Repository navigation
Conversation
Adds pgx, goose and x/crypto. The user, session, account and verification tables keep Better Auth's names and columns; Migrate runs them under their own advisory lock so app migrations never contend with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign-up, sign-in, sign-out, get-session, email verification and password reset at Better Auth's paths and response shapes. argon2id hashing with a dummy hash for unknown emails, Better Auth scrypt hashes accepted and upgraded, signed better-call cookies and bearer tokens on the same session rows, the trusted-origin CSRF check and per-IP rate limits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each run creates and drops its own database. Covers every flow and its reverse state: sign-out and reset revoke sessions, expired and used tokens, wrong passwords, unverified sign-in, bearer, origin and rate limits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd stale reset links Fixes from a security review: - verification links carry a fingerprint of the password hash they were issued for; a duplicate sign-up of an unverified account replaces its password, so the address owner reclaims a squatted account - verifying revokes other sessions; a reset revokes every other reset link - client IPs are normalised (IPv6 /64, IPv4-mapped) before rate limiting - host-only wildcard origins only match http(s) URLs - Origin: null is accepted with Sec-Fetch-Site: same-origin, as in Better Auth - sign-out with bearer and cookie revokes both sessions - request-password-reset requires redirectTo - Shutdown no longer races sendMail on the WaitGroup - expired verification cleanup runs off the request path, indexed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- server migrate runs auth.Migrate (goose_auth) before app migrations; dbtest templates and sqlc read packages/auth/migrations - serve builds *auth.Auth from config: Resend when RESEND_API_KEY is set, otherwise the dev mailer; email verification from config.Features; PUBLIC_URL, TRUSTED_ORIGINS and the Expo origins (exp:// outside production) as trusted origins; CLIENT_IP_HEADER for rate limits - server.CallerFrom adapts auth.UserFrom to api.Caller - graceful shutdown waits for queued auth emails - devauth, authstub and Features.DevAuth are gone - sqlc omits unused structs so credential tables stay out of packages/database - depguard keeps packages/auth from importing packages/database Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under make -j its temporary probe packages showed up as untracked Go files in check-generated (and would break lint/test). Lint and tests still run in parallel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jayleaton
marked this pull request as ready for review
October 22, 2026 06:53
This was referenced Oct 22, 2026
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.
Problem
The foundation (#1) shipped with a dev-only auth fake (
devauth, which signed every request in as a seeded user) and a stub"user"table (authstub). The template had no real sign-up, sign-in, sessions, email verification or password reset. Nothing authenticated the mobile app either.How
packages/authis a Go port of the Better Auth 1.7 core. It keeps Better Auth's table names and columns, endpoint paths, JSON shapes, error codes, cookie name and signing format, so data from the original app carries over, the Expo client keeps working, and later features (2FA, passkeys, OAuth, organisations) can be ported as sub-packages. This PR also wires it into the server.send-verification-email.AUTH_SECRET.Authorization: Bearerworks on the same session rows for mobile.cmd/serverbuilds*auth.Authfrom config. It uses Resend whenRESEND_API_KEYis set, and email verification followsconfig.Features.PUBLIC_URL,TRUSTED_ORIGINSand the Expo origins (exp://only outside production).server.CallerFromadaptsauth.UserFromtoapi.Caller.packages/auth/migrations.devauth,authstubandFeatures.DevAuthare deleted.omit_unused_structskeeps credential tables out ofpackages/database.packages/authfrom importingpackages/database.make cinow runs the layering proof on its own. Undermake -j, its temporary probe packages had madecheck-generatedfail..env.exampleare updated.packages/auth/README.mdcovers the endpoints, how to port a Better Auth feature, and the security notes.Contract additions (vs BUILD-PLAN; no signature changed)
auth.Configgained optional fields:MinPasswordLength,MaxPasswordLength,LoginPath,RateLimiter,ClientIPHeader,LoggerandMailTimeout.SendVerificationEmail, used by the original's resend button, andShutdown.MobileTrustedOrigins(production)andMigrations;NewDevMailer/NewResendMailer,NewMemoryRateLimiter;Errortype andErr*values.POST /send-verification-email,GET /reset-password/{token}(the link in the email) andGET /ok./api/auth/*returns Better Auth response shapes ({"code","message"}on error), not the envelope, because the original did and the Expo client expects them.RequireUserand the API still use the envelope.CLIENT_IP_HEADER(setX-Real-IPon Railway).Features.DevAuthbecameFeatures.MobileDevOrigins.Evidence
Before (main @ a6562bd):
POST /api/auth/sign-in/emailanswers501 {"error":"auth is not wired yet (dev auth fake)"}. In development every request is the seededdev@example.com.After:
make ciis green locally:golangci-lint: 0 issues;go test -race ./...: all packages pass,packages/authcoverage 89.7% (measured on the last pre-rebase run; onlyMigrationsand a loop changed since);The auth tests run against real Postgres and cover every flow plus its reverse state: sign-out and reset revoke sessions, expired and used tokens are rejected, wrong password, unverified sign-in, bearer, origin check, rate limits, concurrent reset-token use, and a squatted sign-up reclaimed by the address owner. The new
server.TestAuthWiringdrives sign-up, cookie GET/PATCH/api/core/v1/users, bearer, sign-out and the 401 throughserver.New.Real server (
make db-up && make migrate && make dev, curl):Known residual risk (from packages/auth/README.md)
RateLimiterbefore running several instances. Behind a proxy withoutCLIENT_IP_HEADER, every client shares one bucket.rememberMe, cookie cache.Merge Danger
Door: two-way for code. The auth migration creates the
user,session,accountandverificationtables and thegoose_authtable. Nothing is deployed yet, so a revert costs nothing now. Once real users sign up, rolling back the schema would drop their accounts.Blast Radius: platform. Every authenticated route, the API caller, migrations, sqlc input, dbtest templates and
make cichange.apps/web/ui(feat/web-ui) should call the auth service methods andSetSessionCookie. Its HTML form routes need their own CSRF protection;http.CrossOriginProtectionalready covers them.🤖 Generated with Claude Code