Authentication Setup
NuxtBase uses Better Auth and enables more than just email/password login.
From the template source, the auth layer includes:
- email and password login
- email verification
- password reset
- magic links
- email OTP
- Google and GitHub OAuth
- two-factor authentication
- passkeys
- organization and admin plugins
This page focuses on the setup decisions you need to make before those features behave correctly.
The Two Base URLs Must Be Correct
Section titled “The Two Base URLs Must Be Correct”The most important auth variables are:
BETTER_AUTH_URL=http://localhost:3000NUXT_PUBLIC_SITE_URL=http://localhost:3000In the template, both values are used to build the trusted origin list. If these URLs are wrong, auth callbacks, verification links, or browser-origin checks become unreliable.
Local Development
Section titled “Local Development”For local development, keep them aligned:
BETTER_AUTH_URL=http://localhost:3000NUXT_PUBLIC_SITE_URL=http://localhost:3000Production
Section titled “Production”For production, move both to your real app origin:
BETTER_AUTH_URL=https://app.example.comNUXT_PUBLIC_SITE_URL=https://app.example.comMinimum Auth Variables
Section titled “Minimum Auth Variables”For the default auth stack to work, configure:
BETTER_AUTH_SECRET=replace-with-a-random-secretBETTER_AUTH_URL=http://localhost:3000NUXT_PUBLIC_SITE_URL=http://localhost:3000BETTER_AUTH_SECRET must be long enough to pass validation. In this template, anything shorter than 32 characters is rejected.
NUXT_PUBLIC_APP_NAME is optional here. If you do not override it, the template falls back to the default app name from siteConfig.appName.
Email Flows Are Enabled by Default
Section titled “Email Flows Are Enabled by Default”Even before you add OAuth, the template expects auth-related emails to work:
- verification email
- password reset email
- magic link email
- verification code email
- organization invitation email
For local development, the default preview email driver is enough to make these flows usable without external email infrastructure.
That means you can finish auth setup first, then move to real email delivery later.
OAuth: Google and GitHub
Section titled “OAuth: Google and GitHub”Google and GitHub login are optional. Each provider is enabled only if both of its env vars are present.
Provider dashboards:
GOOGLE_CLIENT_ID=...GOOGLE_CLIENT_SECRET=...Local setup:
- Open Google Cloud Console.
- Create a new Google Cloud project for this app, or explicitly choose the existing project that already owns this app’s Google sign-in setup.
- Remember that one project only has one OAuth branding / consent screen configuration. Do not mix multiple unrelated apps under the same project unless they intentionally share the same Google auth brand.
- Create an OAuth 2.0 Client ID and choose Web application.
- Set Authorized JavaScript origins to
http://localhost:3000. - Set Authorized redirect URIs to
http://localhost:3000/api/auth/callback/google. - Copy the generated Client ID and Client Secret into
.env. - Restart your local dev server.
Production setup:
- Open the Google Cloud project that is dedicated to this app’s production Google sign-in, or create one if you do not want to share branding with another app.
- Review the OAuth branding / consent screen in that project and make sure it matches the production product name, logo, and support email you actually want users to see.
- Create a production OAuth 2.0 Client ID, or a separate production client inside that project.
- Set Authorized JavaScript origins to your real app origin, for example
https://app.example.com. - Set Authorized redirect URIs to your real callback URL, for example
https://app.example.com/api/auth/callback/google. - Put the production Client ID and Client Secret into your production environment variables.
- Make sure
BETTER_AUTH_URLandNUXT_PUBLIC_SITE_URLuse that same production app origin. - Redeploy the app.
GitHub
Section titled “GitHub”GITHUB_CLIENT_ID=...GITHUB_CLIENT_SECRET=...Local setup:
- Open GitHub Developer Settings and go to OAuth Apps.
- Create a new OAuth App.
- Set Homepage URL to
http://localhost:3000. - Set Authorization callback URL to
http://localhost:3000/api/auth/callback/github. - Copy the generated Client ID and Client Secret into
.env. - Restart your local dev server.
Production setup:
- Open the same GitHub OAuth App, or create a separate production app.
- Set Homepage URL to your real app origin, for example
https://app.example.com. - Set Authorization callback URL to your real callback URL, for example
https://app.example.com/api/auth/callback/github. - Put the production Client ID and Client Secret into your production environment variables.
- Make sure
BETTER_AUTH_URLandNUXT_PUBLIC_SITE_URLuse that same production app origin. - Redeploy the app.
If you configure only one half of a provider pair, startup fails.
OAuth Callback URLs
Section titled “OAuth Callback URLs”When the auth handler is mounted at /api/auth, Better Auth uses these callback paths:
- Google:
/api/auth/callback/google - GitHub:
/api/auth/callback/github
So if your app runs at https://app.example.com, register these full callback URLs with the provider dashboards:
https://app.example.com/api/auth/callback/googlehttps://app.example.com/api/auth/callback/githubPasskeys
Section titled “Passkeys”Passkeys are enabled in the template through @better-auth/passkey.
BETTER_AUTH_PASSKEY_RP_ID and BETTER_AUTH_PASSKEY_RP_NAME define the
Relying Party that the browser shows and validates during passkey
registration and login.
These variables are optional:
BETTER_AUTH_PASSKEY_RP_ID=localhostBETTER_AUTH_PASSKEY_RP_NAME=My SaaSWhat they do:
BETTER_AUTH_PASSKEY_RP_IDis the domain identity for the passkey. Browsers and platform authenticators use it to decide whether a stored passkey is valid for the current site.BETTER_AUTH_PASSKEY_RP_NAMEis the human-readable app name shown to users in the passkey creation or sign-in prompt.
In practice:
- if your app runs locally on
http://localhost:3000,RP_IDis typicallylocalhost - if your app runs on
https://app.example.com,RP_IDshould usually beapp.example.com RP_NAMEshould be the product name users recognize, for exampleNuxtBaseor your own app name
These values matter because passkeys are origin-sensitive:
- if
BETTER_AUTH_PASSKEY_RP_IDdoes not match the real app hostname, passkey registration or login can fail - if
BETTER_AUTH_PASSKEY_RP_NAMEis misleading or stale, users may see the wrong product name in the browser or device passkey UI - if you change the final production domain later, you should review the passkey configuration again before relying on passkeys in production
If you do not set them:
BETTER_AUTH_PASSKEY_RP_IDfalls back to the hostname fromBETTER_AUTH_URLorNUXT_PUBLIC_SITE_URLBETTER_AUTH_PASSKEY_RP_NAMEfalls back toNUXT_PUBLIC_APP_NAME
That default is good enough for many local and single-domain setups.
What Happens After Registration
Section titled “What Happens After Registration”One useful behavior is easy to miss if you only look at the UI: after a user is created, the auth hooks automatically create a default personal organization and attach the user as owner.
That is why auth setup and organization setup are closely connected in NuxtBase.
Recommended Setup Sequence
Section titled “Recommended Setup Sequence”- Set the base URLs and auth secret
- Start the app and verify email/password registration works
- Confirm verification emails or preview files are generated
- Add Google and GitHub only after the core flow is stable
- Configure passkeys after your final app domain is decided
Smoke Test Checklist
Section titled “Smoke Test Checklist”After auth setup, verify:
/registercreates a user/loginworks with email and password- verification or magic-link emails are generated
/dashboardloads after login/dashboard/settingsshows security settings, including passkeys