Authentification composite
La classe CompositeAuth permet de réunir plusieurs Providers d'authentification dans un gestionnaire unique. Elle essaie chaque Provider dans l'ordre jusqu'à ce que l'un d'eux réussisse.
Cas d'utilisationLien direct vers Cas d'utilisation
- Prendre en charge à la fois les clés d'API et les jetons OAuth
- Migrer d'un Provider d'authentification à un autre sans interrompre les clients existants
- Autoriser plusieurs Providers d'identité, par exemple Clerk pour le web et des clés d'API pour les intégrations
- Déployer progressivement de nouvelles méthodes d'authentification
InstallationLien direct vers Installation
CompositeAuth est inclus dans @mastra/core et ne nécessite aucun package supplémentaire.
import { CompositeAuth } from '@mastra/core/server'
Exemple d'utilisationLien direct vers Exemple d'utilisation
Associez SimpleAuth, pour les clés d'API, à Clerk, pour les sessions utilisateur :
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]),
},
})
FonctionnementLien direct vers Fonctionnement
Lorsqu'une requête arrive, CompositeAuth :
- extrait le jeton de l'en-tête
Authorization; - essaie dans l'ordre la méthode
authenticateToken()de chaque Provider ; - renvoie l'utilisateur du premier Provider qui réussit ;
- renvoie
null(401 Unauthorized) si tous les Providers échouent.
Pour l'autorisation, la classe appelle la méthode authorizeUser() de chaque Provider jusqu'à ce que l'une d'elles renvoie 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
}
Ordre des ProvidersLien direct vers Ordre des Providers
Placez la méthode d'authentification la plus courante en premier, car l'ordre des Providers influe sur les performances :
// 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)
])
Plusieurs Providers OAuthLien direct vers Plusieurs Providers OAuth
Prenez en charge les utilisateurs de différents Providers d'identité :
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]),
},
})
Exemple de migrationLien direct vers Exemple de migration
Migrez de JWT vers Clerk tout en conservant la rétrocompatibilité :
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
]),
},
})
Avec des Providers personnalisésLien direct vers Avec des Providers personnalisés
Associez les Providers intégrés à des implémentations personnalisées :
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]),
},
})
Gestion des erreursLien direct vers Gestion des erreurs
CompositeAuth intercepte silencieusement les erreurs de chaque Provider et passe au suivant. Ainsi, l'échec d'un Provider ne bloque pas l'authentification :
// If clerkAuth throws an error, apiKeyAuth still gets tried
new CompositeAuth([clerkAuth, apiKeyAuth])
Pour diagnostiquer les problèmes d'authentification, ajoutez une journalisation à vos Providers personnalisés ou vérifiez la configuration de chaque Provider.
LimitationsLien direct vers Limitations
- Tous les Providers partagent le même jeton provenant de l'en-tête
Authorization - Les types d'utilisateur peuvent différer entre les Providers ; utilisez des unions discriminées au besoin
- Aucun mécanisme intégré ne permet d'identifier le Provider qui a authentifié une requête
Gérer différents types d'utilisateurLien direct vers Gérer différents types d'utilisateur
Lorsque les Providers renvoient des types d'utilisateur différents, utilisez une union discriminée :
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)
}
}