Aller au contenu principal

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'utilisation
Lien 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

Installation
Lien 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'utilisation
Lien direct vers Exemple d'utilisation

Associez SimpleAuth, pour les clés d'API, à Clerk, pour les sessions utilisateur :

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

Fonctionnement
Lien direct vers Fonctionnement

Lorsqu'une requête arrive, CompositeAuth :

  1. extrait le jeton de l'en-tête Authorization ;
  2. essaie dans l'ordre la méthode authenticateToken() de chaque Provider ;
  3. renvoie l'utilisateur du premier Provider qui réussit ;
  4. 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 Providers
Lien 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 OAuth
Lien 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 migration
Lien 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és
Lien 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 erreurs
Lien 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.

Limitations
Lien 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'utilisateur
Lien 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)
}
}