跳至主要內容

複合身份驗證

CompositeAuth 類別讓你將多個身份驗證 Provider 組合成單一身份驗證處理器。它會依次嘗試每個 Provider,直至其中一個成功為止。

使用情境
使用情境 的直接連結

  • 同時支援 API 金鑰和 OAuth token
  • 在不同身份驗證 Provider 之間遷移,而不影響現有客戶端
  • 允許使用多個身份 Provider(例如網絡使用 Clerk,整合則使用 API 金鑰)
  • 逐步推出新的身份驗證方法

安裝
安裝 的直接連結

CompositeAuth 已包含在 @mastra/core 內,毋須安裝額外依賴套件。

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

使用範例
使用範例 的直接連結

將 SimpleAuth(用於 API 金鑰)與 Clerk(用於使用者 session)組合:

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 header 擷取 token
  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。這可避免單一 Provider 發生故障時阻礙身份驗證:

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

如要偵錯身份驗證問題,請在自訂 Provider 中加入記錄,或檢查個別 Provider 的設定。

限制
限制 的直接連結

  • 所有 Provider 共用來自 Authorization header 的同一個 token
  • 不同 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)
}
}