@ideacrafters/fastify-auth
v0.2.0
Published
Comprehensive authentication plugin for Fastify with multi-guard support and flexible ORM integration
Downloads
6
Readme
@ideacrafters/fastify-auth
Comprehensive authentication plugin for Fastify with multi-guard support and flexible ORM integration. Built on top of @fastify/auth for maximum performance and compatibility.
Features
- 🔐 Multiple Authentication Guards: JWT, API Key, Credentials, Session, and Custom guards
- 🎯 Multi-Guard Logic: OR/AND operators for complex authentication flows
- 🔑 Authorization: Role-based and permission-based access control
- 🗄️ ORM Agnostic: Adapter pattern for Prisma, TypeORM, Mongoose, and custom adapters
- ⚡ High Performance: Built-in caching with LRU cache
- 📊 Audit Logging: Success/failure hooks for monitoring
- 🎨 TypeScript: Full type safety and IntelliSense support
- 🚀 Production Ready: Error handling, security headers, and more
Installation
npm install @ideacrafters/fastify-authPeer Dependencies
npm install fastify@^5.0.0Optional Dependencies (based on your needs)
# For Prisma adapter
npm install @prisma/client prisma
# For password hashing (used by credentials guard)
npm install bcryptQuick Start
import Fastify from 'fastify'
import fastifyAuth, { PrismaAdapter } from '@ideacrafters/fastify-auth'
import { PrismaClient } from '@prisma/client'
const fastify = Fastify()
const prisma = new PrismaClient()
// Create adapter
const adapter = new PrismaAdapter(prisma, {
defaultFields: {
include: ['id', 'email', 'name', 'role', 'permissions'],
exclude: ['password']
}
})
// Register plugin
await fastify.register(fastifyAuth, {
adapter,
cache: { enabled: true, ttl: 300000 },
onAuthSuccess: (user, request) => {
console.log(`User ${user.id} authenticated`)
}
})
// Setup guards
fastify.setupAuth({
guards: {
jwt: { type: 'jwt' },
apikey: { type: 'apikey' },
credentials: { type: 'credentials' }
}
})
// Protected route
fastify.get('/profile', {
preHandler: fastify.authenticate('jwt')
}, async (request) => {
return { user: request.user }
})Authentication Guards
JWT Authentication
fastify.setupAuth({
guards: {
jwt: {
type: 'jwt',
options: {
algorithms: ['HS256', 'RS256'],
audience: 'your-app',
issuer: 'auth-server'
}
}
}
})
// Usage
fastify.get('/api/protected', {
preHandler: fastify.authenticate('jwt')
}, handler)API Key Authentication
fastify.setupAuth({
guards: {
apikey: {
type: 'apikey',
options: {
headerName: 'x-api-key', // Default
queryParam: 'api_key', // Optional fallback
extractor: (request) => { // Custom extraction
return request.headers['custom-header']
}
}
}
}
})Credentials Authentication
fastify.setupAuth({
guards: {
credentials: {
type: 'credentials',
options: {
usernameField: 'email', // Default: 'username'
passwordField: 'password', // Default: 'password'
hashPassword: true // Default: true
}
}
}
})
// Login endpoint
fastify.post('/login', {
preHandler: fastify.authenticate('credentials')
}, async (request, reply) => {
const token = await reply.jwtSign({
sub: request.user.id,
email: request.user.email
})
return { token }
})Session Authentication
fastify.setupAuth({
guards: {
session: {
type: 'session',
options: {
cookieName: 'sessionId',
maxAge: 86400000 // 24 hours
}
}
}
})Custom Guards
fastify.setupAuth({
guards: {
custom: {
type: 'custom',
authenticate: async (request, reply, adapter) => {
const token = request.headers['x-custom-auth']
if (!token) return null
const user = await adapter.findUserByCustomField(token)
return user
}
}
}
})Multi-Guard Authentication
OR Logic (Default)
Any of the specified guards can succeed:
fastify.get('/api/data', {
preHandler: fastify.authenticate(['jwt', 'apikey'])
}, handler)AND Logic
All specified guards must succeed:
fastify.post('/api/secure', {
preHandler: fastify.authenticate(['jwt', 'apikey'], { operator: 'AND' })
}, handler)Conditional Authentication
Dynamic guard selection based on request:
fastify.get('/api/flexible', {
preHandler: fastify.authenticate((req) =>
req.headers.authorization ? 'jwt' : 'session'
)
}, handler)Authorization
Role-Based Access Control
fastify.delete('/admin/users/:id', {
preHandler: [
fastify.authenticate('jwt'),
fastify.authorize('admin', 'role')
]
}, handler)
// Multiple roles
fastify.get('/moderator/dashboard', {
preHandler: [
fastify.authenticate('jwt'),
fastify.authorize(['admin', 'moderator'], 'role')
]
}, handler)Permission-Based Access Control
fastify.post('/api/resources', {
preHandler: [
fastify.authenticate('jwt'),
fastify.authorize('create:resources', 'permission')
]
}, handler)Model-Based Access Control
fastify.get('/admin/dashboard', {
preHandler: [
fastify.authenticate(['client', 'admin']),
fastify.authorize('admin', 'usermodel')
]
}, handler)Request Helpers
After authentication, the request object is decorated with helpful methods:
fastify.get('/api/check', {
preHandler: fastify.authenticate('jwt')
}, async (request) => {
return {
isAdmin: request.hasRole('admin'),
canCreate: request.hasPermission('create:resources'),
hasAnyRole: request.hasAnyRole(['admin', 'moderator']),
hasAllPerms: request.hasAllPermissions(['read', 'write']),
userModel: request.isUserModel('admin')
}
})Adapters
Prisma Adapter
import { PrismaAdapter } from '@ideacrafters/fastify-auth/adapters'
const adapter = new PrismaAdapter(prismaClient, {
userModel: 'user', // Default: 'user'
sessionModel: 'session', // Default: 'session'
apiKeyModel: 'apiKey', // Default: 'apiKey'
passwordField: 'password', // Default: 'password'
emailField: 'email', // Default: 'email'
sessionDuration: 86400 // Default: 24 hours
})Custom Adapter
Create your own adapter by extending the base class:
import { AbstractAuthAdapter } from '@ideacrafters/fastify-auth/adapters'
class CustomAdapter extends AbstractAuthAdapter {
async findUserById(id, options) {
// Your implementation
}
async findUserByEmail(email, options) {
// Your implementation
}
// ... implement other required methods
}Caching
Configure caching for improved performance:
await fastify.register(fastifyAuth, {
adapter,
cache: {
enabled: true,
ttl: 300000, // 5 minutes
max: 1000, // Maximum items
updateAgeOnGet: true // Reset TTL on access
}
})Error Handling
Custom error handling:
await fastify.register(fastifyAuth, {
adapter,
errorHandler: async (error, request, reply) => {
if (error.code === 'INVALID_TOKEN') {
reply.code(401).send({ error: 'Token expired' })
} else {
reply.code(500).send({ error: 'Authentication error' })
}
}
})Audit Logging
Monitor authentication events:
await fastify.register(fastifyAuth, {
adapter,
onAuthSuccess: async (user, request) => {
await logService.log('auth.success', {
userId: user.id,
ip: request.ip,
userAgent: request.headers['user-agent']
})
},
onAuthFailure: async (error, request) => {
await logService.log('auth.failure', {
error: error.message,
ip: request.ip
})
}
})TypeScript Support
Full TypeScript support with type augmentation:
declare module 'fastify' {
interface FastifyRequest {
user?: {
id: string
email: string
role: string
permissions: string[]
}
}
}Database Schema Requirements
Minimum Requirements
-- User table
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE,
password VARCHAR(255),
isActive BOOLEAN DEFAULT true
);Recommended Schema
-- User table
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE,
username VARCHAR(255) UNIQUE,
password VARCHAR(255),
role VARCHAR(50),
permissions TEXT[], -- Array of permissions
isActive BOOLEAN DEFAULT true,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Session table
CREATE TABLE sessions (
id UUID PRIMARY KEY,
userId UUID REFERENCES users(id),
token VARCHAR(255) UNIQUE,
expiresAt TIMESTAMP,
data JSONB,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- API Key table
CREATE TABLE api_keys (
id UUID PRIMARY KEY,
userId UUID REFERENCES users(id),
key VARCHAR(255) UNIQUE,
name VARCHAR(255),
isActive BOOLEAN DEFAULT true,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Examples
Check the examples directory for complete examples:
basic-usage.ts- Basic authentication setupmulti-guard.ts- Complex authentication flowscustom-adapter.ts- Custom adapter implementationauthorization.ts- Role and permission-based access control
Performance
- Built on
@fastify/authfor minimal overhead - LRU caching reduces database queries
- Efficient token validation
- Benchmarks: < 1ms authentication overhead
Security Best Practices
- Always use HTTPS in production
- Store secrets securely using environment variables
- Implement rate limiting to prevent brute force attacks
- Use strong JWT secrets (minimum 256 bits)
- Enable audit logging for security monitoring
- Validate and sanitize all input data
- Implement CORS appropriately
- Use secure session cookies (httpOnly, secure, sameSite)
API Reference
Plugin Options
| Option | Type | Description |
|--------|------|-------------|
| adapter | BaseAuthAdapter | Required. Database adapter instance |
| guards | Record<string, GuardConfig> | Guard configurations |
| cache | CacheOptions | Cache configuration |
| defaultGuard | string | Default guard name |
| onAuthSuccess | Function | Success callback |
| onAuthFailure | Function | Failure callback |
| errorHandler | Function | Custom error handler |
Guard Configuration
| Option | Type | Description |
|--------|------|-------------|
| type | 'jwt' \| 'apikey' \| 'credentials' \| 'session' \| 'custom' | Guard type |
| userModel | string | User model name |
| options | object | Guard-specific options |
| authenticate | Function | Custom authentication function (for custom type) |
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
License
MIT © Idea Crafters
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
