Production-ready SvelteKit template for OAuth/OIDC authentication using the Backend-for-Frontend (BFF) pattern
| Функция | Описание |
|---|---|
| 🔒 Secure by Design | Токены никогда не покидают сервер, только HTTP-only cookies в браузере |
| ⚡ PKCE Flow | Защита от перехвата authorization code |
| 📦 Гибкое хранение сессий | Memory (dev), Redis (prod), PostgreSQL |
| 🛡️ Rate Limiting | Встроенная защита от brute-force атак |
| 🎯 Type-Safe | Полная поддержка TypeScript с типами SvelteKit |
| 🚀 Svelte 5 | Современные реактивные паттерны с runes |
| 🔄 Token Refresh | Автоматическое обновление токенов перед истечением |
| 🧹 Session Cleanup | Автоматическая очистка истекших сессий |
| 🍪 HTTP-Only Cookies | Безопасное хранение сессий |
| 🛡️ CSRF Protection | Встроенная защита от CSRF атак |
git clone https://github.com/FrankFMY/auth-bff-oidc-template.git
cd auth-bff-oidc-template
pnpm install
Create .env file in the project root:
# OIDC Configuration
OIDC_ISSUER=https://your-oidc-provider.com
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=http://localhost:5173/auth/callback
# Session Configuration (optional)
# SESSION_SECRET=your-random-secret-key
pnpm dev
Open http://localhost:5173 in your browser.
No additional setup required. Memory store is used by default.
⚠️ Warning: Memory store is NOT suitable for production. Sessions are lost on server restart.
pnpm add ioredis
.env:REDIS_URL=redis://localhost:6379
src/lib/server/auth/index.ts and uncomment Redis configuration:// Uncomment this section
import { Redis } from "ioredis";
import { RedisSessionStore } from "./stores/redis.js";
import { REDIS_URL } from "$env/static/private";
const redis = new Redis(REDIS_URL || "redis://localhost:6379");
const sessionStore = new RedisSessionStore(redis);
export const authService = new BFFAuthService(
{
issuer: OIDC_ISSUER,
clientId: OIDC_CLIENT_ID,
clientSecret: OIDC_CLIENT_SECRET,
redirectUri: OIDC_REDIRECT_URI,
scopes: ["openid", "profile", "email"],
},
sessionStore,
);
pnpm add pg
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
data JSONB NOT NULL,
expires_at BIGINT NOT NULL
);
CREATE INDEX idx_sessions_expires_at ON sessions(expires_at);
.env:DATABASE_URL=postgresql://user:password@localhost:5432/dbname
src/lib/server/auth/index.ts and uncomment PostgreSQL configuration.src/
├── lib/
│ └── server/
│ └── auth/
│ ├── bff.ts # Core BFF Auth Service
│ ├── index.ts # Auth configuration
│ ├── middleware.ts # Authentication middleware
│ ├── rate-limiter.ts # Rate limiting
│ ├── session-store.ts # Session store interface
│ ├── utils.ts # Utility functions
│ └── stores/
│ ├── memory.ts # Memory session store
│ ├── redis.ts # Redis session store
│ └── postgres.ts # PostgreSQL session store
├── routes/
│ ├── +layout.server.ts # User data injection
│ ├── +page.svelte # Home page
│ ├── auth/
│ │ ├── login/+server.ts # Login endpoint
│ │ ├── callback/+server.ts # OAuth callback
│ │ └── logout/+server.ts # Logout endpoint
│ └── api/
│ └── user/
│ └── profile/+server.ts # Protected API example
└── hooks.server.ts # Global hooks (auth middleware)
| Технология | Версия | Назначение |
|---|---|---|
| SvelteKit | 2.x | Fullstack фреймворк |
| Svelte 5 | 5.x | UI библиотека с Runes |
| TypeScript | 5.9+ | Типизация |
| Vite | 7.x | Сборщик и dev server |
| Технология | Версия | Назначение |
|---|---|---|
| SvelteKit | 2.x | Backend API routes |
| Node.js | 20+ | JavaScript runtime |
| Технология | Назначение |
|---|---|
| Memory Store | Development (in-memory) |
| Redis | Production sessions (через ioredis) |
| PostgreSQL | Production sessions (через pg) |
| Технология | Назначение |
|---|---|
| pnpm | Package manager |
| ESLint | Линтинг кода |
| Prettier | Форматирование кода |
| tsx | TypeScript execution |
sequenceDiagram
participant Browser
participant BFF (SvelteKit)
participant OIDC Provider
Browser->>BFF: GET /auth/login
BFF->>BFF: Generate PKCE challenge
BFF->>OIDC Provider: Redirect to authorization URL
OIDC Provider->>Browser: Login page
Browser->>OIDC Provider: Enter credentials
OIDC Provider->>BFF: Redirect to /auth/callback?code=...
BFF->>OIDC Provider: Exchange code for tokens (with PKCE)
OIDC Provider->>BFF: Return tokens
BFF->>BFF: Store tokens in session
BFF->>Browser: Set HTTP-only cookie, redirect to /
Browser->>BFF: GET / (with cookie)
BFF->>BFF: Validate session
BFF->>Browser: Return protected page
| Функция | Реализация |
|---|---|
| Токены не покидают сервер | Access/refresh токены хранятся только на сервере |
| HTTP-Only Cookies | Session IDs в безопасных HTTP-only cookies |
| PKCE | Защита от перехвата authorization code |
| Rate Limiting | Настраиваемые лимиты на auth endpoints |
| CSRF Protection | Встроенная защита SvelteKit от CSRF |
| Token Refresh | Автообновление за 5 минут до истечения |
| Session Expiration | Автоматическая очистка истекших сессий |
// src/routes/dashboard/+page.server.ts
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ locals }) => {
if (!locals.user) {
redirect(303, "/auth/login");
}
return {
user: locals.user,
};
};
// src/routes/api/posts/+server.ts
import { json, error } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
export const GET: RequestHandler = async ({ locals }) => {
if (!locals.user) {
error(401, "Unauthorized");
}
const posts = await db.getPosts(locals.user.sub);
return json(posts);
};
<!-- src/routes/+page.svelte -->
<script lang="ts">
import type { PageProps } from "./$types";
let { data }: PageProps = $props();
</script>
{#if data.user}
<h1>Welcome, {data.user.name}!</h1>
<a href="/auth/logout">Logout</a>
{:else}
<a href="/auth/login">Login</a>
{/if}
Configure in src/lib/server/auth/rate-limiter.ts:
const limiter = new RateLimiter({
windowMs: 15 * 60 * 1000, // 15 minutes
maxRequests: 5, // 5 requests per window
keyGenerator: (request) => {
// Generate unique key per IP
return request.headers.get("x-forwarded-for") || "unknown";
},
});
Configure session expiration time:
// Redis
const sessionStore = new RedisSessionStore(redis, {
prefix: "session:",
defaultTTL: 86400, // 24 hours in seconds
});
// PostgreSQL
const sessionStore = new PostgresSessionStore(pool, {
tableName: "sessions",
cleanupIntervalMs: 3600000, // Cleanup every hour
});
pnpm dev # Запустить dev сервер
pnpm check # TypeScript проверка
pnpm check:watch # TypeScript проверка в watch режиме
pnpm lint # Проверка ESLint
pnpm format # Форматирование Prettier
pnpm build # Production сборка
pnpm preview # Превью production сборки
pnpm copyright:add # Добавить copyright headers в файлы
Создайте .env файл в корне проекта или настройте переменные окружения в production:
# OIDC Configuration
OIDC_ISSUER=https://your-oidc-provider.com
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=http://localhost:5173/auth/callback
# Session Storage (выберите один)
REDIS_URL=redis://localhost:6379
# или
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Optional
SESSION_SECRET=your-random-secret-key
| Переменная | Описание | Обязательная |
|---|---|---|
OIDC_ISSUER |
URL OIDC провайдера | ✅ |
OIDC_CLIENT_ID |
Client ID от OIDC провайдера | ✅ |
OIDC_CLIENT_SECRET |
Client Secret от OIDC провайдера | ✅ |
OIDC_REDIRECT_URI |
Redirect URI для callback | ✅ |
REDIS_URL |
URL Redis (для Redis store) | ⚠️ Для production |
DATABASE_URL |
URL PostgreSQL (для Postgres store) | ⚠️ Для production |
SESSION_SECRET |
Секрет для сессий | ❌ Опционально |
pnpm build
The build output will be in the .svelte-kit directory. Configure your deployment platform to serve this directory.
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Contributions are welcome! Please feel free to submit a Pull Request.
RU: Данное ПО разработано Артёмом Прянишниковым в инициативном порядке. НЕ является служебным произведением (ст. 1295 ГК РФ). Права принадлежат автору.
EN: This software was developed independently by Artyom Pryanishnikov. NOT a work-for-hire. All rights reserved.
| Разрешено / Allowed | Запрещено / Restricted |
|---|---|
| ✅ Личное использование | ❌ Коммерция без договора |
| ✅ Изучение кода | ❌ SaaS / Перепродажа |
| ✅ Внутренние тесты | ❌ Удаление авторства |
| ✅ Форки для обучения | ❌ Конкурирующие продукты |
Commercial Contact: [email protected]
This project is licensed under PolyForm Shield License 1.0.0 with proprietary clauses protecting the author's intellectual property rights.
This software is an INITIATIVE DEVELOPMENT by Artyom Pryanishnikov.
Any commercial use by entities other than the Copyright Holder requires a separate license agreement.
Contact for licensing: Pryanishnikovartem@gmail.com
Disputes shall be resolved in the Arbitrazh Court of Saratov Region (Арбитражный суд Саратовской области), Saratov, Russia.
⭐ Если проект был полезен, поставьте звезду! ⭐