sveltekit-auth-example
v5.8.5
Published
SvelteKit Authentication Example
Maintainers
Readme
SvelteKit Authentication and Authorization Example
A complete, production-ready authentication and authorization starter for Svelte 5 and SvelteKit 2. Skip the boilerplate — get secure local accounts, Google OAuth, MFA, email verification, role-based access control, OWASP-compliant password hashing, and bot protection out of the box.
Features
| | | | ------------------------------------------------ | --------------------------------------- | | ✅ Local accounts (email + password) | ✅ Sign in with Google / Google One Tap | | ✅ Multi-factor authentication (MFA via email) | ✅ Email verification | | ✅ Forgot password / email reset (Brevo) | ✅ User profile management | | ✅ Session management + timeout | ✅ Rate limiting | | ✅ Role-based access control | ✅ Password complexity enforcement | | ✅ Content Security Policy (CSP) | ✅ OWASP-compliant password hashing | | ✅ Cloudflare Turnstile CAPTCHA (bot protection) | |
Stack
- SvelteKit — Single Page Application
- PostgreSQL 16+ — database with server-side hashing
- Tailwind CSS 4 — styling
- TypeScript — end-to-end type safety
Requirements
- Node.js 24.14.0 or later
- PostgreSQL 16 or later
- A Brevo account (for transactional emails)
- A Google API client ID (for Sign in with Google)
- A Cloudflare Turnstile site key and secret key (for bot protection on auth forms)
Setting up the project
- Get the project and set up the database
# Clone the repo to your current directory
git clone https://github.com/nstuyvesant/sveltekit-auth-example.git
# Install the dependencies
cd sveltekit-auth-example
yarn install
# Create PostgreSQL database (only works if you have PostgreSQL installed)
bash db_create.shCreate a Google API client ID per these instructions. Make sure you include
http://localhost:3000andhttp://localhostin the Authorized JavaScript origins, andhttp://localhost:3000/auth/google/callbackin the Authorized redirect URIs for your Client ID for Web application. Do not access the site using http://127.0.0.1:3000 — usehttp://localhost:3000or it will not work.Create a free Brevo account and generate an API Key under SMTP & API settings. Set
EMAILto the sender address verified in your Brevo account.Create a .env file at the top level of the project with the following values (substituting your own id and PostgreSQL username and password):
DATABASE_URL=postgres://user:password@localhost:5432/auth
DATABASE_SSL=false
DOMAIN=http://localhost:3000
JWT_SECRET=replace_with_your_own
BREVO_KEY=replace_with_your_own
EMAIL=replace_with_your_own
PUBLIC_GOOGLE_CLIENT_ID=replace_with_your_own
PUBLIC_TURNSTILE_SITE_KEY=replace_with_your_own
TURNSTILE_SECRET_KEY=replace_with_your_ownRun locally
# Start the dev server and open the app in a new browser tab
yarn dev -- --openBuild and preview
# Build for production
yarn build
# Preview the production build
yarn previewValid logins
The db_create.sql script adds three users to the database with obvious roles:
| Email | Password | Role | | ------------------- | ------------ | ------- | | [email protected] | Admin1234! | admin | | [email protected] | Teacher1234! | teacher | | [email protected] | Student1234! | student |
MFA note: Local account logins require a 6-digit code sent to the user's email address. To successfully log in with the seed accounts above, either update their email addresses in the database to your own (
UPDATE users SET email = '[email protected]' WHERE email = '[email protected]';), or retrieve the code directly from themfa_codestable after submitting the login form (SELECT code FROM mfa_codes;).
How it works
The website supports two types of authentication:
- Local accounts via username (email) and password
- The login form (/src/routes/login/+page.svelte) sends the login info as JSON to endpoint /auth/login
- The endpoint passes the JSON to PostgreSQL function authenticate(json) which hashes the password and compares it to the stored hashed password in the users table. The function returns JSON containing a session ID (v4 UUID) and user object (sans password).
- The endpoint sends this session ID as an httpOnly SameSite cookie and the user object in the body of the response.
- The client stores the user object in
appState(see /src/lib/app-state.svelte.ts). - Further requests to the server include the session cookie. The hooks.ts handle() method extracts the session cookie, looks up the user and attaches it to RequestEvent.locals so server-side code can check locals.user.role to see if the request is authorized and return an HTTP 401 status if not.
- Sign in with Google
- Sign in with Google is initialized in /src/routes/+layout.svelte.
- Google One Tap prompt is displayed on the initially loaded page unless Intelligent Tracking Prevention is enabled in the browser.
- Sign in with Google button is on the login page (/src/routes/login/+page.svelte) and register page (/src/routes/register/+page.svelte).
- Clicking either button opens a new window asking the user to authorize this website. If the user OKs it, a JSON Web Token (JWT) is sent to a callback function.
- The callback function (in /src/lib/google.ts) sends the JWT to an endpoint on this server /auth/google.
- The endpoint decodes and validates the user information then calls the PostgreSQL function start_gmail_user_session to upsert the user to the database returning a session id in an httpOnly SameSite cookie and user in the body of the response.
- The client stores the user object in
appState(see /src/lib/app-state.svelte.ts). - Further requests to the server work identically to local accounts above.
There is some overhead to checking the user session in a database each time versus using a JWT; however, validating each request avoids problems discussed in this article. For a high-volume website, I would use Redis or the equivalent.
The forgot password / password reset functionality uses a JWT and Brevo to send the email. You would need to have a Brevo account and set the BREVO_KEY and EMAIL environment variables (see setup instructions above). Email sending is in src/lib/server/brevo.ts and the email templates are in src/lib/server/email/. This code could easily be replaced by nodemailer or something similar.
