Authentication
The ChaasKit Template supports multiple authentication methods including email/password, OAuth providers, and magic links.
Configuration
Configure authentication in config/app.config.ts:
auth: {
methods: ['email-password', 'google', 'github', 'magic-link'],
allowUnauthenticated: false,
magicLink: {
enabled: true,
expiresInMinutes: 15,
},
gating: {
mode: 'open',
inviteExpiryDays: 7,
waitlistEnabled: true,
},
}
Available Methods
Email/Password
Built-in email and password authentication with bcrypt hashing.
Configuration:
auth: {
methods: ['email-password'],
}
Endpoints:
POST /api/auth/register- Create accountPOST /api/auth/login- Log inPOST /api/auth/logout- Log out
Google OAuth
Setup:
- Create a project at console.cloud.google.com
- Enable the Google+ API
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
{API_URL}/api/auth/google/callback
Environment:
GOOGLE_CLIENT_ID="your-client-id"
GOOGLE_CLIENT_SECRET="your-client-secret"
Configuration:
auth: {
methods: ['google'],
}
GitHub OAuth
Setup:
- Go to GitHub Settings > Developer settings > OAuth Apps
- Create a new OAuth App
- Set Authorization callback URL:
{API_URL}/api/auth/github/callback
Environment:
GITHUB_CLIENT_ID="your-client-id"
GITHUB_CLIENT_SECRET="your-client-secret"
Configuration:
auth: {
methods: ['github'],
}
Magic Links
Passwordless authentication via email links.
Configuration:
auth: {
methods: ['magic-link'],
magicLink: {
enabled: true,
expiresInMinutes: 15,
},
}
Note: Requires email service configuration (not included by default). Magic link emails use the email configuration in config/app.config.ts (see Configuration docs).
Signup Gating and Waitlist
Signups can be restricted by mode and optionally backed by a waitlist. Invite tokens bypass gating.
Modes:
openinvite_onlyclosedtimed_windowcapacity_limit
Behavior:
POST /api/auth/registerandPOST /api/auth/magic-linkacceptinviteTokento bypass gating.- When gating blocks signup, the API responds with
403and includeswaitlistEnabledso the client can offer a waitlist form. - Waitlist signup is via
POST /api/auth/waitlist.
See docs/configuration.md for gating config options and docs/admin.md for the admin waitlist UI.
Anonymous Users
Allow users to chat without authentication:
auth: {
allowUnauthenticated: true,
}
Anonymous users:
- Can create threads and send messages
- Cannot access saved threads after session ends
- No access to premium features
JWT Tokens
Authentication uses JWT tokens stored in HTTP-only cookies.
Token Structure:
{
sub: userId,
email: userEmail,
iat: issuedAt,
exp: expiresAt,
}
Cookie Settings:
httpOnly: true- Not accessible via JavaScriptsecure: true- HTTPS only in productionsameSite: 'lax'- CSRF protection
Protected Routes
Backend
Use middleware to protect routes:
import { requireAuth, optionalAuth } from '../middleware/auth.js';
// Requires authentication
router.get('/private', requireAuth, async (req, res) => {
// req.user is guaranteed to exist
res.json({ userId: req.user.id });
});
// Optional authentication
router.get('/public', optionalAuth, async (req, res) => {
// req.user may or may not exist
if (req.user) {
res.json({ message: 'Hello, ' + req.user.email });
} else {
res.json({ message: 'Hello, anonymous' });
}
});
Frontend
Use the AuthContext:
import { useAuth } from '../contexts/AuthContext';
function MyComponent() {
const { user, isAuthenticated, login, logout } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
return <div>Hello, {user.email}</div>;
}
User Model
model User {
id String @id @default(cuid())
email String @unique
passwordHash String?
name String?
avatarUrl String?
// OAuth
oauthProvider String?
oauthId String?
// Subscription
plan String @default("free")
// Settings
settings Json @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Security Considerations
- Password Hashing: bcrypt with automatic salt
- JWT Secrets: Use 32+ character random strings
- HTTPS: Required in production
- CSRF Protection: SameSite cookies
- Rate Limiting: Built-in via express-rate-limit
Customization
Add a New OAuth Provider
-
Install Passport strategy:
pnpm add passport-twitter -F @chaaskit/server -
Add route in
packages/server/src/api/auth.ts -
Configure strategy in passport setup
-
Add to config:
auth: { methods: ['twitter'], }
Custom Auth Provider
Implement using the registry pattern:
// extensions/auth-providers/my-provider.ts
import { BaseAuthProvider } from '@chaaskit/server';
export class MyAuthProvider extends BaseAuthProvider {
async authenticate(credentials) {
// Custom auth logic
}
}
Register:
registry.register('auth-provider', 'my-provider', MyAuthProvider);