Composite Auth - Combining Multiple Authentication Providers in Mastra

Trust: ★★★☆☆ (0.90) · 0 validations · developer_reference

Published: 2026-05-10 · Source: crawler_authoritative

Tình huống

Mastra SDK documentation for combining multiple authentication providers into a single auth handler, targeting developers implementing auth in their applications.

Insight

The CompositeAuth class allows combining multiple authentication providers into a single auth handler that tries each provider in order until one succeeds. It is included in @mastra/core (no additional packages required). The class works by extracting the token from the Authorization header, then iterating through each provider’s authenticateToken() method—returning the user from the first provider that succeeds, or null (401 Unauthorized) if all providers fail. For authorization, it calls each provider’s authorizeUser() method until one returns true. The provider order matters for performance—place the most common authentication method first. CompositeAuth silently catches errors from individual providers and moves to the next one, preventing one failing provider from blocking authentication. Limitations include: all providers share the same token from the Authorization header, user types may differ between providers (use discriminated unions), and no built-in way to identify which provider authenticated a request. Built-in providers include SimpleAuth for API keys, MastraAuthClerk for Clerk, MastraAuthAuth0 for Auth0, and MastraJwtAuth for JWT. Custom providers can be implemented and combined with built-in ones.

Hành động

Import CompositeAuth from ‘@mastra/core/server’. Create instances of desired auth providers (e.g., SimpleAuth for API keys, MastraAuthClerk for Clerk sessions). Pass an array of provider instances to CompositeAuth in the desired order. Assign the CompositeAuth instance to server.auth in your Mastra constructor. For different user types across providers, use a discriminated union type to handle them in application code. To debug auth issues, add logging to custom providers or check individual provider configurations.

Kết quả

Returns the authenticated user from the first provider that successfully validates the token, or null (401 Unauthorized) if all providers fail.

Điều kiện áp dụng

Included in @mastra/core, no additional packages required. Available for use with any Mastra-compatible auth providers.


Nội dung gốc (Original)

Composite Auth

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.

import { CompositeAuth } from '@mastra/core/server'

Usage example

Combine SimpleAuth (for API keys) with Clerk (for user sessions):

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<ApiKeyUser>({
  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.

// 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:

// 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:

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:

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:

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:

// 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:

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)
  }
}

Liên kết

Xem thêm: