Skip to content

Auth Middleware

Auth Middleware provides flexible route protection patterns for your Flowfull backend. Protect your API routes with minimal code.

Overview

Flowfull provides middleware patterns for different authentication scenarios:

  • requireAuth() - Require authenticated user
  • optionalAuth() - Optional authentication
  • requireUserType() - Require specific user type
  • requirePermission() - Require specific permission

Basic Usage

Require Authentication

typescript
import { Hono } from 'hono';
import { requireAuth } from './lib/auth/middleware';

const app = new Hono();

// Protected route - requires authentication
app.get('/api/profile', requireAuth(), async (c) => {
  const userId = c.get('user_id');
  const email = c.get('email');
  
  return c.json({ userId, email });
});

Optional Authentication

typescript
// Public route with optional auth
app.get('/api/posts', optionalAuth(), async (c) => {
  const userId = c.get('user_id');  // May be undefined
  
  if (userId) {
    // Show personalized posts
    return c.json({ posts: await getPersonalizedPosts(userId) });
  } else {
    // Show public posts
    return c.json({ posts: await getPublicPosts() });
  }
});

Require User Type

typescript
// Admin-only route
app.get('/api/admin/users', requireUserType('admin'), async (c) => {
  const users = await db.selectFrom('users').selectAll().execute();
  return c.json({ users });
});

// Teacher-only route
app.get('/api/teacher/classes', requireUserType('teacher'), async (c) => {
  const classes = await getTeacherClasses(c.get('user_id'));
  return c.json({ classes });
});

Require Permission

typescript
// Require specific permission
app.delete('/api/posts/:id', requirePermission('posts.delete'), async (c) => {
  const postId = c.req.param('id');
  await db.deleteFrom('posts').where('id', '=', postId).execute();
  return c.json({ success: true });
});

Middleware Patterns

Pattern 1: Public Routes

typescript
// No authentication required
app.get('/api/public/stats', async (c) => {
  return c.json({ stats: await getPublicStats() });
});

Pattern 2: Protected Routes

typescript
// Authentication required
app.get('/api/profile', requireAuth(), async (c) => {
  const userId = c.get('user_id');
  return c.json({ profile: await getProfile(userId) });
});

Pattern 3: Conditional Routes

typescript
// Different behavior based on auth
app.get('/api/content', optionalAuth(), async (c) => {
  const userId = c.get('user_id');
  
  if (userId) {
    return c.json({ content: await getPremiumContent(userId) });
  } else {
    return c.json({ content: await getFreeContent() });
  }
});

Pattern 4: Role-Based Routes

typescript
// Different routes for different roles
app.get('/api/dashboard', requireAuth(), async (c) => {
  const userType = c.get('user_type');
  
  switch (userType) {
    case 'admin':
      return c.json({ dashboard: await getAdminDashboard() });
    case 'teacher':
      return c.json({ dashboard: await getTeacherDashboard() });
    case 'student':
      return c.json({ dashboard: await getStudentDashboard() });
    default:
      return c.json({ error: 'Invalid user type' }, 403);
  }
});

Context Variables

After authentication, these variables are available in the context:

typescript
app.get('/api/me', requireAuth(), async (c) => {
  const userId = c.get('user_id');           // User ID
  const email = c.get('email');              // Email
  const userType = c.get('user_type');       // User type
  const sessionId = c.get('session_id');     // Session ID
  const permissions = c.get('permissions');  // Permissions array
  
  return c.json({ userId, email, userType });
});

Validation Modes

You can specify validation mode per route:

typescript
// Standard validation
app.get('/api/profile', requireAuth('STANDARD'), handler);

// Advanced validation for sensitive routes
app.get('/api/billing', requireAuth('ADVANCED'), handler);

// Strict validation for critical operations
app.post('/api/transfer', requireAuth('STRICT'), handler);

Learn more about Validation Modes →

Error Handling

typescript
app.get('/api/protected', requireAuth(), async (c) => {
  try {
    const data = await getData(c.get('user_id'));
    return c.json({ data });
  } catch (error) {
    return c.json({ error: 'Internal server error' }, 500);
  }
});

Custom Middleware

Create custom middleware for specific needs:

typescript
// Require email verification
export function requireVerifiedEmail() {
  return async (c: Context, next: Next) => {
    const userId = c.get('user_id');
    
    const user = await db.selectFrom('users')
      .where('id', '=', userId)
      .select('email_verified')
      .executeTakeFirst();
    
    if (!user?.email_verified) {
      return c.json({ error: 'Email not verified' }, 403);
    }
    
    await next();
  };
}

// Usage
app.get('/api/premium', requireAuth(), requireVerifiedEmail(), handler);

Chaining Middleware

typescript
// Multiple middleware
app.post(
  '/api/admin/users',
  requireAuth('STRICT'),
  requireUserType('admin'),
  requirePermission('users.create'),
  async (c) => {
    // Create user
  }
);

Best Practices

✅ Do

  • Use requireAuth() for protected routes
  • Use optionalAuth() for public routes with personalization
  • Use validation modes appropriately
  • Handle errors gracefully
  • Chain middleware for complex requirements

❌ Don't

  • Skip authentication on sensitive routes
  • Use DISABLED mode in production
  • Ignore authentication errors
  • Hardcode user types in routes
  • Forget to validate permissions

Real-World Examples

E-commerce API

typescript
// Public product listing
app.get('/api/products', optionalAuth(), async (c) => {
  const userId = c.get('user_id');
  const products = await getProducts(userId);
  return c.json({ products });
});

// Protected cart
app.get('/api/cart', requireAuth(), async (c) => {
  const cart = await getCart(c.get('user_id'));
  return c.json({ cart });
});

// Admin product management
app.post('/api/admin/products', requireUserType('admin'), async (c) => {
  const product = await createProduct(await c.req.json());
  return c.json({ product });
});

SaaS Application

typescript
// Public landing page data
app.get('/api/features', async (c) => {
  return c.json({ features: await getFeatures() });
});

// User dashboard
app.get('/api/dashboard', requireAuth(), async (c) => {
  const dashboard = await getDashboard(c.get('user_id'));
  return c.json({ dashboard });
});

// Organization admin
app.get('/api/org/settings', requirePermission('org.manage'), async (c) => {
  const settings = await getOrgSettings(c.get('organization_id'));
  return c.json({ settings });
});

Next Steps

Need Help?

Released under the MIT License.