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 会:
- 从
Authorization标头提取令牌 - 按顺序尝试每个 Provider 的
authenticateToken()方法 - 返回第一个成功的 Provider 所提供的用户
- 如果所有 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)
}
}