SaaS Starter (Better Auth)
Authentication, organizations, roles, sessions, feature flags, and an admin app, powered by
Better Auth on your own Postgres. This is the starter-saas-betterauth pack, and the
default starter. Unlike WorkOS, Better Auth is a self-hosted library, so there's no separate account to create. You
bring your own Google OAuth app.
npx @hype-stack/cli compose --packs starter-saas-betterauth,layout-basicWhat you get
- Email/password and Google sign-in, password reset, email verification
- Organizations: create, switch, invite members by email, roles and CASL permissions enforced with
hasAccess - An onboarding flow for users with no organization yet
- Per-organization feature flags, with an admin tab to override them
- A settings page with profile and organization tabs that feature packs extend
- The full admin app: users, organizations, dashboard rows, admin management
- Mobile and browser-extension slots with the auth screens, the organization switcher, and settings
- The organization switcher and menu are contributed to the layout's shell action areas, so they render in every layout
Starting from the free auth starter instead? Swapping to this one later is a family swap with no migration.
For session flow, middleware, and route guards, see Authentication and Organizations.
What you need
- A Google OAuth web app you create in Google Cloud Console. Better Auth talks to Google directly.
- Resend for transactional email (password reset, email verification). Uses the base template's
RESEND_API_KEY. - Postgres and Valkey (base services).
Register a Google OAuth app
- Open Google Cloud Console > APIs & Services > Credentials.
- Create an OAuth 2.0 Client ID of type Web application.
- Under Authorized redirect URIs, add the Better Auth callback path:
http://localhost:3000/api/auth/callback/google- Copy the Client ID and Client secret into
apps/backend/.envasGOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET.
In production, add your deployed origin as well:
https://yourdomain.com/api/auth/callback/googleThe path is fixed: Better Auth owns the handler at /api/auth/*, and the Google callback is always
/api/auth/callback/google appended to your BETTER_AUTH_URL.
Environment variables
| Variable | Purpose |
|---|---|
BETTER_AUTH_URL | Origin Better Auth runs on (e.g. http://localhost:3000) |
BETTER_AUTH_SECRET | Auth secret. The CLI generates one at install time |
GOOGLE_CLIENT_ID | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | Google OAuth client secret |
MOBILE_OAUTH_REDIRECT_URL | Deep link the Expo app returns to after Google sign-in (default hypestack://callback) |
RESEND_API_KEY | Transactional email (password reset, verification) |
SUPER_ADMIN_EMAIL | Bootstrap admin emails for the admin app |
Google sign-in flow
The pack starts Google sign-in from GET /auth/google/:intent, which calls Better Auth's signInSocial. After Google
redirects back to /api/auth/callback/google, Better Auth exchanges the code, creates the session, and sends the
browser to / (or /onboarding if the user has no organization).
Admin app
This starter also ships a separate admin app for managing users and organizations.
Set SUPER_ADMIN_EMAIL in apps/backend/.env to sign in the first time.