# CompositeAuth Class The `CompositeAuth` class allows you to combine multiple authentication providers into a single auth handler. It tries each provider in order until one succeeds. ## Use Cases - Support both API keys and OAuth tokens - Migrate between auth providers without breaking existing clients - Allow multiple identity providers (e.g., Clerk for web, API keys for integrations) - Gradual rollout of new authentication methods ## Installation CompositeAuth is included in `@mastra/core`, no additional packages required. ```typescript import { CompositeAuth } from '@mastra/core/server'; ``` ## Usage Example Combine SimpleAuth (for API keys) with Clerk (for user sessions): ```typescript import { Mastra } from '@mastra/core'; import { CompositeAuth, SimpleAuth } from '@mastra/core/server'; import { MastraAuthClerk } from '@mastra/auth-clerk'; // API key users type ApiKeyUser = { id: string; name: string; type: 'api-key'; }; const apiKeyAuth = new SimpleAuth({ tokens: { 'sk-integration-key-123': { id: 'integration-1', name: 'CI/CD Pipeline', type: 'api-key', }, }, }); // Clerk users (from web app) const clerkAuth = new MastraAuthClerk({ publishableKey: process.env.CLERK_PUBLISHABLE_KEY, secretKey: process.env.CLERK_SECRET_KEY, jwksUri: process.env.CLERK_JWKS_URI, }); export const mastra = new Mastra({ server: { auth: new CompositeAuth([apiKeyAuth, clerkAuth]), }, }); ``` ## How It Works When a request comes in, CompositeAuth: 1. Extracts the token from the `Authorization` header 2. Tries each provider's `authenticateToken()` method in order 3. Returns the user from the first provider that succeeds 4. Returns `null` (401 Unauthorized) if all providers fail For authorization, it calls each provider's `authorizeUser()` method until one returns `true`. ```typescript // Pseudocode of CompositeAuth behavior async authenticateToken(token, request) { for (const provider of this.providers) { const user = await provider.authenticateToken(token, request); if (user) return user; // First match wins } return null; // All providers failed } ``` ## Provider Order The order of providers matters. Place the most common authentication method first for better performance: ```typescript // If most requests use Clerk, put it first new CompositeAuth([ clerkAuth, // Checked first (most common) apiKeyAuth, // Checked second (less common) ]); // If most requests use API keys, put it first new CompositeAuth([ apiKeyAuth, // Checked first (most common) clerkAuth, // Checked second (less common) ]); ``` ## Multiple OAuth Providers Support users from different identity providers: ```typescript import { CompositeAuth } from '@mastra/core/server'; import { MastraAuthClerk } from '@mastra/auth-clerk'; import { MastraAuthAuth0 } from '@mastra/auth-auth0'; const clerkAuth = new MastraAuthClerk({ publishableKey: process.env.CLERK_PUBLISHABLE_KEY, secretKey: process.env.CLERK_SECRET_KEY, jwksUri: process.env.CLERK_JWKS_URI, }); const auth0Auth = new MastraAuthAuth0({ domain: process.env.AUTH0_DOMAIN, audience: process.env.AUTH0_AUDIENCE, }); export const mastra = new Mastra({ server: { auth: new CompositeAuth([clerkAuth, auth0Auth]), }, }); ``` ## Migration Example Migrate from JWT to Clerk while maintaining backwards compatibility: ```typescript import { CompositeAuth } from '@mastra/core/server'; import { MastraJwtAuth } from '@mastra/auth'; import { MastraAuthClerk } from '@mastra/auth-clerk'; // Legacy JWT auth (existing clients) const legacyAuth = new MastraJwtAuth({ secret: process.env.JWT_SECRET, }); // New Clerk auth (new clients) const clerkAuth = new MastraAuthClerk({ publishableKey: process.env.CLERK_PUBLISHABLE_KEY, secretKey: process.env.CLERK_SECRET_KEY, jwksUri: process.env.CLERK_JWKS_URI, }); // Support both during migration export const mastra = new Mastra({ server: { auth: new CompositeAuth([ clerkAuth, // New auth method (preferred) legacyAuth, // Legacy support ]), }, }); ``` ## With Custom Providers Combine built-in providers with custom implementations: ```typescript import { CompositeAuth, SimpleAuth } from '@mastra/core/server'; import { MyCustomAuth } from './my-custom-auth'; const apiKeyAuth = new SimpleAuth({ tokens: { 'sk-key-123': { id: 'user-1', name: 'API User' }, }, }); const customAuth = new MyCustomAuth({ apiUrl: process.env.CUSTOM_AUTH_URL, }); export const mastra = new Mastra({ server: { auth: new CompositeAuth([apiKeyAuth, customAuth]), }, }); ``` ## Error Handling CompositeAuth silently catches errors from individual providers and moves to the next one. This prevents one failing provider from blocking authentication: ```typescript // If clerkAuth throws an error, apiKeyAuth still gets tried new CompositeAuth([clerkAuth, apiKeyAuth]); ``` To debug authentication issues, add logging to your custom providers or check individual provider configurations. ## Limitations - All providers share the same token from the `Authorization` header - User types may differ between providers (use discriminated unions if needed) - No built-in way to identify which provider authenticated a request ### Handling Different User Types When providers return different user types, use a discriminated union: ```typescript type ApiKeyUser = { type: 'api-key'; id: string; name: string; }; type ClerkUser = { type: 'clerk'; sub: string; email: string; }; type User = ApiKeyUser | ClerkUser; // In your application code function handleUser(user: User) { if (user.type === 'api-key') { console.log('API key user:', user.name); } else { console.log('Clerk user:', user.email); } } ``` ## Related - [Auth Overview](https://mastra.ai/docs/server/auth) - Authentication concepts - [Simple Auth](https://mastra.ai/docs/server/auth/simple-auth) - Token-to-user mapping - [Custom Auth Provider](https://mastra.ai/docs/server/auth/custom-auth-provider) - Build your own provider