Skip to main content
The Mastra server supports middleware for authentication, authorization, request context, and custom logic.

Built-in Middleware

The server automatically registers these middleware functions during initialization:
  1. Context Middleware - Sets up request context and dependencies
  2. Authentication Middleware - Verifies user tokens
  3. Authorization Middleware - Checks user permissions

Authentication

Configure authentication in your Mastra server config:
import { Mastra } from '@mastra/core';
import { MastraServer } from '@mastra/hono';
import { Hono } from 'hono';

const mastra = new Mastra({
  server: {
    auth: {
      // Verify the authentication token
      authenticateToken: async (token: string, request: Request) => {
        // Verify JWT, API key, etc.
        const user = await verifyToken(token);
        if (!user) return null;
        return user;
      },
      
      // Simple authorization - check if user is allowed
      authorizeUser: async (user: any, request: Request) => {
        return user.isActive === true;
      },
    },
  },
});

const app = new Hono();
const server = new MastraServer({ app, mastra });

await server.init(); // Registers auth middleware

Token Sources

The server checks for tokens in this order:
  1. Authorization header: Bearer <token>
  2. apiKey query parameter: ?apiKey=<token>
# Using Authorization header
curl -X POST http://localhost:3000/api/agents/myAgent/generate \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Hello"}]}'

# Using query parameter
curl -X POST "http://localhost:3000/api/agents/myAgent/generate?apiKey=your-token" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Hello"}]}'

Authorization

Path-Based Authorization

Control access based on request path and method:
const mastra = new Mastra({
  server: {
    auth: {
      authenticateToken: async (token: string) => {
        // Token verification
        return await verifyToken(token);
      },
      
      // Check authorization based on path and method
      authorize: async (path: string, method: string, user: any) => {
        // Admin-only routes
        if (path.startsWith('/api/admin')) {
          return user.role === 'admin';
        }
        
        // Read-only for regular users
        if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
          return user.role === 'admin' || user.role === 'editor';
        }
        
        // Anyone can read
        return true;
      },
    },
  },
});

Rule-Based Authorization

Define authorization rules using patterns:
const mastra = new Mastra({
  server: {
    auth: {
      authenticateToken: async (token: string) => {
        return await verifyToken(token);
      },
      
      rules: [
        {
          // Allow all GET requests
          pathPattern: '/api/**',
          methods: ['GET'],
          authorize: async (user) => true,
        },
        {
          // Admins only for workflow execution
          pathPattern: '/api/workflows/:workflowId/execute',
          methods: ['POST'],
          authorize: async (user) => user.role === 'admin',
        },
        {
          // Team members can use specific agents
          pathPattern: '/api/agents/:agentId/generate',
          methods: ['POST'],
          authorize: async (user, context) => {
            const { agentId } = context.pathParams;
            return user.permissions.includes(`agent:${agentId}`);
          },
        },
      ],
    },
  },
});

Public Routes

Make specific routes publicly accessible:
const mastra = new Mastra({
  server: {
    auth: {
      authenticateToken: async (token: string) => {
        return await verifyToken(token);
      },
      
      // Define public paths (no authentication required)
      publicPaths: [
        { path: '/api/health', methods: ['GET'] },
        { path: '/api/agents', methods: ['GET'] }, // List agents publicly
      ],
    },
  },
});

Development Playground Access

The development playground can bypass authentication:
const mastra = new Mastra({
  server: {
    auth: {
      authenticateToken: async (token: string) => {
        return await verifyToken(token);
      },
      
      // Allow playground requests in development
      allowPlayground: process.env.NODE_ENV === 'development',
    },
  },
});

Custom Middleware (Hono)

Add custom middleware to your Hono app:
import { Hono } from 'hono';
import { MastraServer } from '@mastra/hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';

const app = new Hono();

// Add CORS middleware
app.use('*', cors({
  origin: ['http://localhost:3001'],
  credentials: true,
}));

// Add logging middleware
app.use('*', logger());

// Custom request logging
app.use('*', async (c, next) => {
  const start = Date.now();
  await next();
  const duration = Date.now() - start;
  console.log(`${c.req.method} ${c.req.path} - ${duration}ms`);
});

const server = new MastraServer({ app, mastra });
await server.init();

Custom Middleware (Express)

Add custom middleware to your Express app:
import express from 'express';
import { MastraServer } from '@mastra/express';
import cors from 'cors';
import morgan from 'morgan';

const app = express();

// Add CORS
app.use(cors({
  origin: 'http://localhost:3001',
  credentials: true,
}));

// Add logging
app.use(morgan('combined'));

// Custom middleware
app.use((req, res, next) => {
  console.log(`Incoming request: ${req.method} ${req.path}`);
  next();
});

// Body parsing (required for Express)
app.use(express.json());

const server = new MastraServer({ app, mastra });
await server.init();

Request Context

The context middleware automatically parses and sets up request context:
// Client sends request context
fetch('/api/agents/myAgent/generate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    messages: [{ role: 'user', content: 'Hello' }],
    requestContext: {
      userId: 'user-123',
      sessionId: 'session-abc',
      metadata: { source: 'web' },
    },
  }),
});
Access context in your agent:
import { RequestContext } from '@mastra/core/request-context';

const agent = {
  name: 'My Agent',
  instructions: 'You are a helpful assistant',
  model: 'gpt-4',
  
  // Use context in hooks
  onStepStart: ({ requestContext }) => {
    const userId = requestContext.get('userId');
    console.log('Processing request for user:', userId);
  },
};

Error Handling

The server automatically handles errors and returns appropriate status codes:
// Authentication error
401: { error: 'Authentication required' }

// Authorization error
403: { error: 'Access denied' }

// Validation error
400: {
  error: 'Invalid request body',
  issues: [
    { field: 'messages', message: 'Required' }
  ]
}

// Server error
500: { error: 'Internal server error' }

Next Steps

Server Adapters

Learn about different server adapters

Observability

Monitor your server with observability

Build docs developers (and LLMs) love