跳到主要内容

Composite Auth

CompositeAuth 类允许你将多个身份验证 Provider 组合为一个身份验证 handler。它会按顺序尝试各个 Provider,直到其中一个成功。

使用场景
使用场景的直接链接

  • 同时支持 API 密钥和 OAuth 令牌
  • 在不中断现有客户端的情况下迁移身份验证 Provider
  • 允许多个身份 Provider(例如 Web 使用 Clerk,集成使用 API 密钥)
  • 逐步推出新的身份验证方法

安装
安装的直接链接

CompositeAuth 包含在 @mastra/core 中,无需安装其他包。

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

用法示例
用法示例的直接链接

将 SimpleAuth(用于 API 密钥)与 Clerk(用于用户会话)组合使用:

src/mastra/index.ts
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]),
},
})

工作原理
工作原理的直接链接

收到请求时,CompositeAuth 会:

  1. Authorization 标头提取令牌
  2. 按顺序尝试每个 Provider 的 authenticateToken() 方法
  3. 返回第一个成功的 Provider 所提供的用户
  4. 如果所有 Provider 都失败,则返回 null(401 Unauthorized)

进行授权时,它会调用每个 Provider 的 authorizeUser() 方法,直到其中一个返回 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 顺序
Provider 顺序的直接链接

Provider 顺序会影响性能,因此请将最常用的身份验证方法放在首位:

// 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)
])

多个 OAuth Provider
多个 OAuth Provider的直接链接

支持来自不同身份 Provider 的用户:

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

迁移示例
迁移示例的直接链接

在保持向后兼容的同时从 JWT 迁移到 Clerk:

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

与自定义 Provider 配合使用
与自定义 Provider 配合使用的直接链接

将内置 Provider 与自定义实现组合使用:

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

错误处理
错误处理的直接链接

CompositeAuth 会静默捕获单个 Provider 的错误并继续尝试下一个,从而避免某个 Provider 失败时阻止身份验证:

// If clerkAuth throws an error, apiKeyAuth still gets tried
new CompositeAuth([clerkAuth, apiKeyAuth])

要调试身份验证问题,请向自定义 Provider 添加日志记录,或检查各个 Provider 的配置。

限制
限制的直接链接

  • 所有 Provider 共享 Authorization 标头中的同一个令牌
  • 不同 Provider 的用户类型可能不同(如有需要,请使用可辨识联合类型)
  • 没有内置方法可识别由哪个 Provider 对请求完成了身份验证

处理不同的用户类型
处理不同的用户类型的直接链接

当 Provider 返回不同的用户类型时,请使用可辨识联合类型:

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