Composite Auth - Kết Hợp Nhiều Nhà Cung Cấp Xác Thực Trong Mastra

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

Published: 2026-05-09 · Source: crawler_authoritative

Tình huống

Tài liệu kỹ thuật về class CompositeAuth trong framework Mastra, dùng để kết hợp nhiều authentication providers thành một auth handler duy nhất.

Insight

CompositeAuth là class trong @mastra/core cho phép kết hợp nhiều nhà cung cấp xác thực (authentication providers) thành một handler duy nhất. Class này thử lần lượt từng provider theo thứ tự cho đến khi có provider thành công. Các use cases chính bao gồm: hỗ trợ cả API keys và OAuth tokens, migrate giữa các auth providers mà không làm break clients hiện tại, cho phép nhiều identity providers (ví dụ Clerk cho web, API keys cho integrations), và gradual rollout các phương thức xác thực mới. Cách hoạt động: CompositeAuth extract token từ Authorization header, thử lần lượt mỗi provider’s authenticateToken() method, trả về user từ provider đầu tiên thành công, hoặc trả về null (401 Unauthorized) nếu tất cả đều thất bại. Thứ tự providers rất quan trọng vì provider đầu tiên được check trước - nên đặt phương thức phổ biến nhất lên đầu để tối ưu hiệu suất. Error handling: CompositeAuth silently catches errors từ các individual providers và chuyển sang provider tiếp theo, ngăn một provider thất bại không block toàn bộ authentication. Limitations: tất cả providers chia sẻ cùng một token từ Authorization header, user types có thể khác nhau giữa các providers (cần dùng discriminated unions), và không có cách built-in để xác định provider nào đã authenticate request.

Hành động

Import CompositeAuth từ ‘@mastra/core/server’. Khởi tạo từng provider riêng lẻ (SimpleAuth cho API keys, MastraAuthClerk cho Clerk, MastraAuthAuth0 cho Auth0, MastraJwtAuth cho JWT). Truyền array các providers vào CompositeAuth([provider1, provider2, …]). Đặt provider phổ biến nhất ở đầu array để tối ưu performance. Khi cần handle các user types khác nhau, sử dụng discriminated union type (ví dụ: type ApiKeyUser = { type: ‘api-key’, id: string, name: string } | type ClerkUser = { type: ‘clerk’, sub: string, email: string }). Để debug, thêm logging vào custom providers hoặc kiểm tra từng provider configuration riêng lẻ.

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

Tất cả providers phải chia sẻ cùng một token từ Authorization header. User types có thể khác nhau giữa các providers. Không có cách built-in để xác định provider nào đã authenticate request. CompositeAuth chỉ hỗ trợ các providers có interface authenticateToken() và authorizeUser() methods.


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: