Auth
Authentication, user management, and notifications for a SvelteKit app: password and passkey login, sessions, registration with invitation gates, password reset, two-factor, and push. No runtime dependencies; sessions, passwords, passkeys and push all run on the Web Crypto API.
Overview
@urbicon-ui/auth covers registration, login (password or passkey), password reset,
sessions, two-factor, and notifications, with zero runtime dependencies. Each flow has two halves:
a server handler you mount on an API route, and a UI component that calls it.
All UI components use @urbicon-ui/blocks primitives, support unstyled / slotClasses / snippet overrides, and read the locale from
the @urbicon-ui/i18n context. English is built in; every other locale needs its
bundle registered once with registerAuthLocale.
For the complete reference — architecture, staged setup, federation (SSO), the adapter contract, and the known-limitations catalog with the production checklist — see the Auth Reference (AUTH.md).
Architecture
Each auth flow has two sides: a UI component (client) and a handler factory (server). The UI component sends a fetch to your SvelteKit
API route; the handler factory is the endpoint behind it. What each handler verifies, hashes, or
rate-limits is the last column below.
| UI Component | Server Handler | Default Endpoint | What it does |
|---|---|---|---|
| LoginPage | createLoginHandler | /api/auth/login | PBKDF2 verify, lockout, JWT session |
| RegisterPage | createRegisterHandler | /api/auth/register | Invitation check, hash, verify email |
| ForgotPasswordPage | createForgotPasswordHandler | /api/auth/forgot-password | Token email, timing-safe |
| ResetPasswordPage | createResetPasswordHandler | /api/auth/reset-password | Token verify, re-hash |
| VerifyEmailPage | createVerifyEmailHandler | /api/auth/verify-email | SHA-256 token check |
| PasskeyManager | createPasskeyHandlers | /api/auth/passkey/* | WebAuthn CBOR/COSE verify |
| InvitationManager | createInvitationHandlers | /api/invitations | authorize-gated CRUD |
| NotificationListener | createStreamHandler | /api/notifications/stream | SSE with keep-alive |
All server handlers are imported from @urbicon-ui/auth/server. Database access
goes through the Adapter pattern: a Prisma adapter is included, and custom adapters implement
the repository interfaces.
Auth Pages
LoginPage
RegisterPage
ForgotPasswordPage
ResetPasswordPage
VerifyEmailPage
Management
InvitationManager
PasskeyManager
AccountSettings
SessionManager
TwoFactorManager
Notifications
NotificationCenter
NotificationBadge
NotificationListener
PushPermissionPrompt
Setup Guide
Integration requires five steps: configure dependencies, add the hook, create API routes, add UI pages, and import the stylesheet.
1. Configure auth dependencies
// src/lib/server/auth.ts
import { createAuthDeps } from '@urbicon-ui/auth/server';
import { createPrismaRepos } from '@urbicon-ui/auth/server/adapters/prisma';
import { createLettermintTransport } from '@urbicon-ui/auth/server/email/lettermint';
import { prisma } from '$lib/server/db';
import { appLogger } from '$lib/server/logging';
import { APP_URL, JWT_SECRET, LETTERMINT_TOKEN } from '$env/static/private';
export const authDeps = createAuthDeps({
config: {
// Required, and never derived from request.url — the Host header is
// attacker-controlled and would point reset links at their domain.
appUrl: APP_URL,
jwt: { secret: JWT_SECRET },
password: { minLength: 8 },
lockout: { maxAttempts: 5, durationMinutes: 15 },
routes: { loginPage: '/auth/login' },
logger: appLogger
},
// Same sink: a missing Prisma model drops its feature, reported here.
repos: createPrismaRepos(prisma, { logger: appLogger }),
email: createLettermintTransport({ token: LETTERMINT_TOKEN, from: 'noreply@example.com' })
});2. Add the SvelteKit hook
The handle hook validates the session, redirects unauthenticated requests off protected
routes, and adds CSRF and security headers. It takes one options object; the redirect target
is config.routes.loginPage from step 1, not a hook option.
// src/hooks.server.ts
import { createAuthHandle, DEFAULT_PUBLIC_ROUTES } from '@urbicon-ui/auth/server';
import { authDeps } from '$lib/server/auth';
export const handle = createAuthHandle({
config: authDeps.config,
repos: authDeps.repos,
// publicRoutes REPLACES the defaults, so spread them in — without that the
// /api/auth/* endpoints below lose their exemption and login answers 401.
// A string is a startsWith prefix ('/' alone would exempt the whole app);
// the exact form publishes one pathname — here the landing page.
publicRoutes: [...DEFAULT_PUBLIC_ROUTES, { path: '/', exact: true }, '/pricing']
});3. Create API route handlers
Each UI component expects a corresponding API endpoint. The handler factories include all validation, hashing, rate limiting, and security.
// Each auth flow needs a SvelteKit API route:
// src/routes/api/auth/login/+server.ts
import { createLoginHandler } from '@urbicon-ui/auth/server';
import { authDeps } from '$lib/server/auth';
export const POST = createLoginHandler(authDeps);
// src/routes/api/auth/register/+server.ts
import { createRegisterHandler } from '@urbicon-ui/auth/server';
import { authDeps } from '$lib/server/auth';
export const POST = createRegisterHandler(authDeps);
// src/routes/api/auth/forgot-password/+server.ts
import { createForgotPasswordHandler } from '@urbicon-ui/auth/server';
import { authDeps } from '$lib/server/auth';
export const POST = createForgotPasswordHandler(authDeps);
// Same pattern for: reset-password, verify-email, logout, me4. Add UI pages
Components read the locale from @urbicon-ui/i18n, so no t prop is
needed when the i18n context is set up. Only English is built in — for any other locale call registerAuthLocale('de', de) once at app start, with the bundle from @urbicon-ui/auth/i18n/de.
<!-- src/routes/auth/login/+page.svelte -->
<script>
import { LoginPage } from '@urbicon-ui/auth';
import { goto } from '$app/navigation';
</script>
<!-- Locale comes from the i18n context -->
<LoginPage
onSuccess={() => goto('/')}
passkeyApiPath="/api/auth/passkey"
rememberMe
/>5. Import the stylesheet
A Tailwind build never scans node_modules on its own. The package ships a
stylesheet whose @source directive points Tailwind at its components — import it
after the blocks one, or the sm: layouts and the link colour of the auth pages are
missing from the compiled CSS.
/* app.css */
@import 'tailwindcss';
@import '@urbicon-ui/blocks/style/index.css'; /* tokens + the blocks @source */
@import '@urbicon-ui/auth/style/index.css'; /* the auth @source */