Production-ready SvelteKit 5 authentication template with SMS and PIN authorization using BFF pattern, PostgreSQL + Drizzle ORM, and enterprise-grade security.
# Clone the repository
git clone https://github.com/FrankFMY/auth-bff-sms-pin-template.git
cd auth-bff-sms-pin-template
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env
# Start PostgreSQL via Docker
docker compose up -d
# Apply database migrations
npm run db:push
# Start development server
npm run dev
Open http://localhost:5173/auth/sms-login
Create .env file:
# PostgreSQL
DB_HOST=localhost
DB_PORT=5433
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=myapp
# SMS Provider (mock for development)
SMS_PROVIDER=mock
# Session
SESSION_MAX_AGE=86400
# Rate Limiting
RATE_LIMIT_OTP_MAX=3
RATE_LIMIT_OTP_WINDOW_MS=60000
# Environment
NODE_ENV=development
graph TB
Client[๐ Client Browser<br/>Svelte 5 + Runes Mode]
subgraph SvelteKit["๐ SvelteKit Server"]
RemoteFunctions[๐ก Remote Functions Layer<br/>requestOTP, verifyOTP<br/>setupPIN, loginWithPIN]
ServiceLayer[โ๏ธ Service Layer<br/>sms-service.ts]
subgraph AuthLogic["๐ Authentication Logic"]
SMSAuth[SMS Auth Service<br/>Argon2, OTP Generation]
RateLimit[Rate Limiter<br/>Brute-force Protection]
SessionStore[Session Store<br/>Memory/Redis]
end
end
Database[(๐๏ธ PostgreSQL<br/>+ Drizzle ORM<br/>โโโโโโโโโโ<br/>๐ Users<br/>๐ฑ OTP Codes<br/>๐ Login Attempts)]
Client -->|Type-Safe RPC| RemoteFunctions
RemoteFunctions --> ServiceLayer
ServiceLayer --> AuthLogic
SMSAuth --> Database
RateLimit --> Database
SessionStore --> Database
style Client fill:#FF3E00,stroke:#333,stroke-width:2px,color:#fff
style SvelteKit fill:#FF3E00,stroke:#333,stroke-width:3px,color:#fff
style Database fill:#9333EA,stroke:#333,stroke-width:2px,color:#fff
style AuthLogic fill:#10B981,stroke:#333,stroke-width:2px
sequenceDiagram
actor User
participant UI as ๐จ Svelte UI
participant RF as ๐ก Remote Functions
participant Auth as ๐ Auth Service
participant DB as ๐๏ธ PostgreSQL
participant SMS as ๐ฑ SMS Provider
%% SMS Login Flow
User->>UI: Enters phone number
UI->>RF: requestOTP(phone)
RF->>Auth: sendOTP(phone)
Auth->>Auth: Generate 6-digit OTP
Auth->>DB: Store OTP (5 min expiry)
Auth->>SMS: Send SMS
SMS-->>User: ๐ฉ SMS with OTP code
Auth-->>RF: Success
RF-->>UI: OTP sent
%% OTP Verification
User->>UI: Enters OTP code
UI->>RF: verifyOTP(phone, code)
RF->>Auth: verifyOTP(phone, code, IP)
Auth->>DB: Check OTP validity
alt OTP Valid
Auth->>DB: Create/Update User
Auth->>DB: Log login attempt (success)
Auth->>DB: Delete used OTP
Auth-->>RF: Success + requiresPinSetup
RF->>RF: Create HTTP-only session
RF-->>UI: Success
alt First Time User
UI->>UI: Show PIN setup
User->>UI: Creates PIN (4-6 digits)
UI->>RF: setupPIN(pin)
RF->>Auth: setupPIN(userId, pin)
Auth->>Auth: Hash PIN (Argon2id)
Auth->>DB: Save PIN hash
Auth-->>RF: Success
RF-->>UI: PIN setup complete
end
UI->>UI: Redirect to Dashboard
else OTP Invalid
Auth->>DB: Increment attempts
Auth->>DB: Log login attempt (failed)
Auth-->>RF: Error + attemptsLeft
RF-->>UI: Show error
end
%% PIN Login Flow (Returning Users)
User->>UI: Enter phone + PIN
UI->>RF: loginWithPIN(phone, pin)
RF->>Auth: loginWithPIN(phone, pin, IP)
Auth->>DB: Get user by phone
Auth->>Auth: Verify PIN (Argon2)
alt PIN Valid
Auth->>DB: Log login attempt (success)
Auth-->>RF: Success
RF->>RF: Create HTTP-only session
RF-->>UI: Success
UI->>UI: Redirect to Dashboard
else PIN Invalid
Auth->>DB: Log login attempt (failed)
Auth-->>RF: Error
RF-->>UI: Show error
end
src/
โโโ lib/
โ โโโ server/
โ โโโ auth/
โ โ โโโ sms-auth.ts # Core SMS/PIN logic
โ โ โโโ sms-service.ts # Service layer for Remote Functions
โ โ โโโ sms-bff.ts # BFF API (HonoJS)
โ โ โโโ sms-middleware.ts # Session middleware
โ โ โโโ sms-rate-limiter.ts # Rate limiting
โ โ โโโ stores/
โ โ โโโ memory.ts # Memory session store (dev)
โ โ โโโ redis.ts # Redis session store (prod)
โ โโโ db/
โ โ โโโ index.ts # PostgreSQL + Drizzle client
โ โ โโโ schema.ts # Database schema
โ โโโ sms/
โ โโโ mock-provider.ts # Mock SMS for dev
โ โโโ real-provider.ts # Real SMS provider
โโโ routes/
โโโ auth/
โ โโโ data.remote.ts # Remote Functions
โ โโโ sms-login/
โ โโโ +page.svelte # Login UI
โโโ +layout.server.ts # Global layout load
โโโ +page.svelte # Home page
SMS OTP Request
OTP Verification
PIN Setup (first-time users)
PIN Login (returning users)
| Operation | Limit | Window |
|---|---|---|
| OTP Request | 3 attempts | 1 minute |
| OTP Verification | 3 attempts | 1 minute |
| PIN Login | 5 attempts | 15 minutes |
X-Frame-Options: DENYX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originStrict-Transport-Security (production only)Content-Security-Policy# Type checking
npm run check
# Linting
npm run lint
# Format code
npm run format
# Build for production
npm run build
# Generate SESSION_SECRET
openssl rand -base64 32
# Update .env.production
SESSION_SECRET=<generated_key>
DB_USER=prod_user_$(openssl rand -hex 4)
DB_PASSWORD=$(openssl rand -base64 24)
Edit src/lib/server/auth/sms-bff.ts:
// Replace:
import { MemorySessionStore } from "./stores/memory";
this.sessionStore = sessionStore ?? new MemorySessionStore();
// With:
import { RedisSessionStore } from "./stores/redis";
this.sessionStore = sessionStore ?? new RedisSessionStore();
Add Redis to docker-compose.yml:
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --requirepass ${REDIS_PASSWORD}
Update src/lib/server/sms/index.ts:
import { RealSMSProvider } from "./real-provider";
export const smsProvider = new RealSMSProvider({
apiKey: process.env.SMS_API_KEY!,
apiUrl: process.env.SMS_API_URL!,
});
npm run build
npm run preview
MIT License - see LICENSE file for details
FrankFMY
Contributions, issues and feature requests are welcome!
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Give a โญ๏ธ if this project helped you!
If you discover a security vulnerability, please email Pryanishnikovartem@gmail.com
Do NOT open a public issue.
โ Production Ready - Fully tested and secure
Made with โค๏ธ by FrankFMY