Bridge Integration Guide
Learn how to integrate Flowless with your custom backend (Flowfull) using the Bridge API for secure session validation.
Overview
The Bridge API is the secure communication layer between your backend (Flowfull) and Flowless. It allows your backend to validate user sessions and receive trust tokens containing user information.
Prerequisites
Before integrating the Bridge API:
- ✅ Created a Flowless instance
- ✅ Have your Bridge Secret from the Pubflow dashboard
- ✅ Have a backend application (Flowfull)
- ✅ Understand basic authentication concepts
Step 1: Configure Environment Variables
Add these to your backend's .env file:
bash
# Flowless instance URL
FLOWLESS_URL=https://your-instance-name.pubflow.com
# Bridge Secret (KEEP SECURE!)
BRIDGE_SECRET=bridge_secret_abc123xyz789
# Validation mode (STANDARD, ADVANCED, STRICT)
VALIDATION_MODE=STANDARD
# Cache TTL for trust tokens (seconds)
TRUST_TOKEN_CACHE_TTL=300DANGER
Never commit your Bridge Secret to version control!
- Store it in environment variables
- Use different secrets for dev/staging/prod
- Rotate it regularly
Step 2: Create Bridge Validation Function
TypeScript/Node.js Example
typescript
import { LRUCache } from 'lru-cache';
// Cache for trust tokens (5 minutes TTL)
const trustTokenCache = new LRUCache<string, string>({
max: 10000,
ttl: 1000 * 60 * 5, // 5 minutes
});
interface ValidateSessionOptions {
sessionId: string;
ipAddress: string;
userAgent?: string;
deviceId?: string;
}
interface UserData {
id: string;
email: string;
name: string;
last_name?: string;
username?: string;
is_verified: boolean;
}
async function validateSession(options: ValidateSessionOptions): Promise<UserData> {
const { sessionId, ipAddress, userAgent, deviceId } = options;
// Check cache first
const cachedToken = trustTokenCache.get(sessionId);
if (cachedToken) {
// Validate and decode cached token
const userData = await validateTrustToken(cachedToken);
if (userData) {
return userData;
}
}
// Call Flowless Bridge API
const response = await fetch(`${process.env.FLOWLESS_URL}/bridge/validate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Bridge-Secret': process.env.BRIDGE_SECRET!,
},
body: JSON.stringify({
session_id: sessionId,
ip_address: ipAddress,
user_agent: userAgent,
device_id: deviceId,
}),
});
if (!response.ok) {
throw new Error('Session validation failed');
}
const data = await response.json();
if (!data.success || !data.data.valid) {
throw new Error('Invalid session');
}
// Cache the trust token
trustTokenCache.set(sessionId, data.data.trust_token);
return data.data.user;
}
async function validateTrustToken(token: string): Promise<UserData | null> {
// Implement PASETO token validation here
// For now, we'll rely on the cache TTL
// In production, validate the token signature
try {
// Decode and validate PASETO token
// Return user data if valid
return null; // Placeholder
} catch (error) {
return null;
}
}Step 3: Create Authentication Middleware
Express.js Example
typescript
import { Request, Response, NextFunction } from 'express';
// Extend Express Request type
declare global {
namespace Express {
interface Request {
user?: UserData;
sessionId?: string;
}
}
}
async function authMiddleware(req: Request, res: Response, next: NextFunction) {
try {
// Get session ID from header
const sessionId = req.headers['x-session-id'] as string;
if (!sessionId) {
return res.status(401).json({
success: false,
error: 'No session ID provided',
});
}
// Get client info
const ipAddress = (req.headers['x-forwarded-for'] as string) || req.ip || '';
const userAgent = req.headers['user-agent'];
const deviceId = req.headers['x-device-id'] as string;
// Validate session with Flowless
const user = await validateSession({
sessionId,
ipAddress,
userAgent,
deviceId,
});
// Attach user to request
req.user = user;
req.sessionId = sessionId;
next();
} catch (error) {
return res.status(401).json({
success: false,
error: 'Invalid or expired session',
});
}
}
export { authMiddleware };Step 4: Use Middleware in Routes
typescript
import express from 'express';
import { authMiddleware } from './middleware/auth';
const app = express();
// Public routes (no auth required)
app.get('/api/public/health', (req, res) => {
res.json({ success: true, status: 'healthy' });
});
// Protected routes (auth required)
app.get('/api/profile', authMiddleware, (req, res) => {
res.json({
success: true,
data: req.user,
});
});
app.get('/api/items', authMiddleware, async (req, res) => {
// req.user is available here
const items = await getItemsForUser(req.user!.id);
res.json({
success: true,
data: items,
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Step 5: Handle Frontend Requests
React Example
typescript
// api.ts
const API_URL = import.meta.env.VITE_API_URL;
const FLOWLESS_URL = import.meta.env.VITE_FLOWLESS_URL;
// Login function
export async function login(email: string, password: string) {
const response = await fetch(`${FLOWLESS_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (data.success) {
// Store session ID
localStorage.setItem('session_id', data.data.session.session_id);
return data.data.user;
}
throw new Error(data.error);
}
// API call with session
export async function getProfile() {
const sessionId = localStorage.getItem('session_id');
if (!sessionId) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_URL}/api/profile`, {
headers: {
'X-Session-ID': sessionId,
},
});
const data = await response.json();
if (!data.success) {
throw new Error(data.error);
}
return data.data;
}Validation Modes
Choose the validation mode based on your security requirements:
STANDARD (Recommended)
bash
VALIDATION_MODE=STANDARD- ✅ Session ID validation
- ✅ IP address validation
- ⚡ Fast and secure for most applications
ADVANCED
bash
VALIDATION_MODE=ADVANCED- ✅ Session ID validation
- ✅ IP address validation
- ✅ Device ID validation
- 🔒 Better security for mobile apps
STRICT
bash
VALIDATION_MODE=STRICT- ✅ Session ID validation
- ✅ IP address validation
- ✅ Device ID validation
- ✅ User agent validation
- 🔐 Maximum security for sensitive applications
Caching Strategy
Why Cache Trust Tokens?
- Performance: Avoid calling Flowless for every request
- Scalability: Reduce load on Flowless
- Cost: Reduce API usage
Cache Implementation
typescript
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, string>({
max: 10000, // Max 10,000 tokens
ttl: 1000 * 60 * 5, // 5 minutes
});
// Cache hit rate tracking
let cacheHits = 0;
let cacheMisses = 0;
function getCacheHitRate() {
const total = cacheHits + cacheMisses;
return total > 0 ? (cacheHits / total) * 100 : 0;
}Error Handling
typescript
try {
const user = await validateSession({ sessionId, ipAddress });
} catch (error) {
if (error.message === 'Invalid session') {
// Session expired or invalid
return res.status(401).json({ error: 'Please login again' });
}
if (error.message === 'IP address mismatch') {
// Possible session hijacking
return res.status(403).json({ error: 'Security violation detected' });
}
// Other errors
return res.status(500).json({ error: 'Internal server error' });
}Next Steps
- Sessions API - Session management endpoints
- Security Best Practices - Secure your integration
- Testing Guide - Test your integration