A fully working authentication system using only SvelteKit's built-in tools — no external auth library required. This demo showcases best practices for session management, role-based permissions, and reactive auth state distribution using Svelte 5's new context API.
Companion Article: This demo accompanies the article "Production Authentication with Context" on The Hackpile Chronicles, where the patterns and architecture are explained in depth.
hooks.server.ts — unauthorized users never see protected pages$derived ensures auth state stays in sync with server after invalidation<RequirePermission> wrapper for conditional rendering# Clone the repository
git clone https://github.com/yourusername/auth-context-demo.git
cd auth-context-demo
# Install dependencies
pnpm install
# Start the development server
pnpm dev
Open http://localhost:5173 in your browser.
| Password | Role | Permissions | |
|---|---|---|---|
| alice@example.com | password123 | Admin | All permissions including admin:system |
| bob@example.com | password123 | Editor | Can read/write posts and comments |
/api/auth/login → Server validates credentials → Sets httpOnly cookiehooks.server.ts validates cookie → Loads user + permissions → Stores in event.locals+layout.server.ts returns event.locals.user and event.locals.permissions+layout.svelte calls createAuthContext(data) → Makes auth available everywhere// Roles are just named bundles of permissions (server-side only)
const roles = {
admin: ['admin:system', 'manage:users', 'read:posts', 'write:posts', ...],
editor: ['read:posts', 'write:posts', 'read:comments', 'write:comments']
}
// User gets permissions through their role assignments
// Never send role names to the client — only resolved permissions
$derived instead of $state?// ❌ WRONG — captures snapshot at creation time
const user = $state(data.user);
// ✅ CORRECT — re-evaluates when data changes
const user = $derived(getData().user);
After calling invalidate('auth:session'), the layout's load function re-runs and provides new props. $derived automatically picks up the changes, while $state would silently hold stale data.
src/
├── hooks.server.ts # Session validation + route guards
├── routes/
│ ├── +layout.server.ts # Load user + permissions from locals
│ ├── +layout.svelte # Create auth context once at root
│ ├── +page.svelte # Public landing page
│ ├── login/ # Login form + POST handler
│ ├── dashboard/ # Protected route (any authenticated user)
│ ├── admin/ # Admin-only route (requires admin:system)
│ └── api/auth/ # Login/logout endpoints
└── lib/
├── auth/
│ ├── auth-context.svelte.ts # Context creation + getters
│ └── types.ts # User, Permission, AuthState types
├── components/
│ ├── Navigation.svelte # Shows auth-aware nav links
│ └── RequirePermission.svelte # Conditional rendering wrapper
└── server/
└── db.ts # In-memory "database" (demo only)
src/lib/auth/auth-context.svelte.tsCreates and exports the auth context:
// Call ONCE in root +layout.svelte
const auth = createAuthContext(() => data);
// Use anywhere in any component
const auth = getAuthContext();
if (auth.hasPermission('admin:system')) {
/* ... */
}
src/hooks.server.tsServer-side session validation and route protection:
export const handle: Handle = async ({ event, resolve }) => {
const token = event.cookies.get('session');
// Validate token, load user + permissions
event.locals.user = user;
event.locals.permissions = permissions;
// Block access to protected routes
if (requiresAuth && !user) redirect(303, '/login');
if (requiresAdmin && !permissions.includes('admin:system')) {
redirect(303, '/dashboard');
}
return resolve(event);
};
src/lib/components/RequirePermission.svelteDeclarative permission-based rendering:
<RequirePermission permission="admin:system">
<a href="/admin">Admin Panel</a>
</RequirePermission>
Children only render if the user has the required permission.
This is a learning demo, NOT production-ready code.
For production-ready authentication, consider Better Auth, Lucia,or any other robust authentication solution.
# Run type checking
pnpm check
# Run linting
pnpm lint
# Format code
pnpm format
pnpm build
pnpm preview
MIT — see LICENSE for details.
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.