Passer au contenu principal
Lorsque vous utilisez plusieurs domaines personnalisés, vous devez configurer vos SDK Auth0 pour qu’ils utilisent le domaine personnalisé approprié pour l’authentification. Ce guide explique la configuration des SDK sur différentes plateformes et dans différents scénarios.

Concepts clés

Paramètre de domaine

Tous les SDK Auth0 exigent un paramètre domain qui indique quel domaine Auth0 utiliser pour l’authentification. Si vous utilisez des domaines personnalisés, définissez ce paramètre sur votre domaine personnalisé plutôt que sur votre domaine canonique Auth0. Sans domaine personnalisé :
domain: 'tenant.auth0.com'
Avec un domaine personnalisé :
domain: 'login.example.com'

Émetteur du jeton

Lorsque vous utilisez un domaine personnalisé, la revendication iss (émetteur) des jetons est définie sur votre domaine personnalisé :
{
  "iss": "https://login.example.com/",
  "sub": "auth0|123456",
  "aud": "your-client-id"
}
Vous devez configurer la validation du jeton afin d’accepter votre domaine personnalisé comme émetteur valide.

SDK d’authentification

Lorsque vous utilisez MCD, le client est responsable de fournir et de valider tous les domaines personnalisés. Lorsque vous configurez les SDK pour résoudre les domaines personnalisés du locataire à l’aide des fonctions de résolution de domaine, vous devez vous assurer que tous les domaines résolus sont dignes de confiance. Une mauvaise configuration du résolveur de domaine peut permettre de contourner l’authentification auprès de la partie de confiance ou exposer l’application à une falsification de requêtes côté serveur. Si vos domaines et vos serveurs proxy ne sont pas configurés correctement, cela peut créer des vulnérabilités de sécurité critiques dont Okta ne peut être tenue responsable.

SDK Auth0 SPA (JavaScript)

Pour les applications à page unique qui utilisent le SDK Auth0 SPA :

Next.js

Pour les applications Next.js qui utilisent le SDK Next.js d’Auth0 (v4+) : Concepts clés de MCD avec Next.js :
  • Un seul locataire Auth0, plusieurs domaines : Tous les domaines personnalisés partagent le même clientId et le même clientSecret, puisqu’ils appartiennent au même locataire Auth0.
  • Fonction DomainResolver : Le paramètre domain accepte une fonction (config: { headers: Headers; url?: URL }) => Promise<string> | string. Cela permet de résoudre le domaine dynamiquement pour chaque requête selon les en-têtes de la requête entrante.
  • Mise en cache des instances : Le SDK met automatiquement en cache les instances Auth0Client par domaine au moyen d’un cache LRU borné (maximum de 100 entrées) afin d’améliorer les performances.
  • Isolation des sessions : Les sessions créées au moyen d’un domaine personnalisé sont isolées à ce domaine et ne peuvent pas être utilisées de façon interchangeable avec des sessions d’un autre domaine.
  • Paramètre URL : Le paramètre url dans le résolveur vaut undefined dans les Server Components et les Server Actions; il est seulement disponible dans le middleware ou les routes d’API.
  • Ajustement du cache de découverte : Configurez la mise en cache des métadonnées OIDC avec l’option discoveryCache :
    const auth0 = new Auth0Client({
      // ... autre configuration
      discoveryCache: {
        ttl: 600,      // Mettre en cache pendant 10 minutes (par défaut)
        maxEntries: 100  // Nombre maximal d’émetteurs mis en cache (par défaut : 100, éviction LRU)
      }
    });
    

SDK React d’Auth0

Pour les applications React utilisant le SDK React d’Auth0 :
import { Auth0Provider } from '@auth0/auth0-react';

function App() {
  return (
    <Auth0Provider
      domain="login.example.com"
      clientId="YOUR_CLIENT_ID"
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
    >
      <MyApp />
    </Auth0Provider>
  );
}
Dans les scénarios à plusieurs domaines :
import { Auth0Provider } from '@auth0/auth0-react';

function App() {
  // Déterminer le domaine personnalisé selon l'environnement ou le contexte
  const auth0Domain = process.env.REACT_APP_AUTH0_DOMAIN || 'login.example.com';

  return (
    <Auth0Provider
      domain={auth0Domain}
      clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
    >
      <MyApp />
    </Auth0Provider>
  );
}

Auth0.js

Pour les applications utilisant Auth0.js :
const webAuth = new auth0.WebAuth({
  domain: 'login.example.com',
  clientID: 'YOUR_CLIENT_ID',
  redirectUri: window.location.origin + '/callback',
  responseType: 'code',
  scope: 'openid profile email'
});

// Initier la connexion
webAuth.authorize();

Node.js (Express)

Pour les applications Node.js qui utilisent express-openid-connect :
const { auth } = require('express-openid-connect');

app.use(
  auth({
    authRequired: false,
    auth0Logout: true,
    issuerBaseURL: 'https://login.example.com',  // Votre domaine personnalisé
    baseURL: 'http://localhost:3000',
    clientID: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET'
  })
);
Dans les scénarios multilocataires :
const { auth } = require('express-openid-connect');

// Middleware pour déterminer le domaine personnalisé par requête
app.use((req, res, next) => {
  // Extraire l'identifiant du locataire depuis le sous-domaine, le chemin ou l'en-tête
  const tenant = req.subdomains[0] || 'default';

  // Associer le locataire au domaine personnalisé
  const domainMap = {
    'customer1': 'login.customer1.com',
    'customer2': 'login.customer2.com',
    'default': 'login.example.com'
  };

  req.auth0Domain = domainMap[tenant] || domainMap.default;
  next();
});

// Configuration d'authentification dynamique
app.use((req, res, next) => {
  auth({
    authRequired: false,
    auth0Logout: true,
    issuerBaseURL: `https://${req.auth0Domain}`,
    baseURL: req.protocol + '://' + req.get('host'),
    clientID: process.env.AUTH0_CLIENT_ID,
    secret: process.env.AUTH0_CLIENT_SECRET
  })(req, res, next);
});

SDK pour appareils mobiles

iOS (Swift)

Avec Auth0.swift :
import Auth0

let auth0 = Auth0
    .webAuth(clientId: "YOUR_CLIENT_ID", domain: "login.example.com")

auth0
    .scope("openid profile email")
    .start { result in
        switch result {
        case .success(let credentials):
            print("Obtained credentials: \(credentials)")
        case .failure(let error):
            print("Failed with: \(error)")
        }
    }
Pour sélectionner le domaine dynamiquement :
import Auth0

class AuthService {
    private let clientId = "YOUR_CLIENT_ID"

    func getAuth0Domain() -> String {
        // Déterminer le domaine en fonction de la configuration de l'application
        if let savedDomain = UserDefaults.standard.string(forKey: "auth0Domain") {
            return savedDomain
        }
        return "login.example.com" // Par défaut
    }

    func login(completion: @escaping (Result<Credentials, Error>) -> Void) {
        Auth0
            .webAuth(clientId: clientId, domain: getAuth0Domain())
            .scope("openid profile email")
            .start { result in
                completion(result)
            }
    }
}

Android (Kotlin)

Avec Auth0.Android :
import com.auth0.android.Auth0
import com.auth0.android.authentication.AuthenticationAPIClient
import com.auth0.android.provider.WebAuthProvider
import com.auth0.android.result.Credentials

val account = Auth0.getInstance(
    "YOUR_CLIENT_ID",
    "login.example.com"  // Votre domaine personnalisé
)

WebAuthProvider.login(account)
    .withScheme("demo")
    .withScope("openid profile email")
    .start(this, object : Callback<Credentials, AuthenticationException> {
        override fun onSuccess(credentials: Credentials) {
            // Gérer le succès
        }

        override fun onFailure(exception: AuthenticationException) {
            // Gérer l'échec
        }
    })
Pour prendre en charge plusieurs domaines :
class AuthManager(private val context: Context) {
    private val clientId = "YOUR_CLIENT_ID"

    private fun getAuth0Domain(): String {
        // Récupérer depuis les préférences partagées ou la configuration de l'app
        val prefs = context.getSharedPreferences("auth", Context.MODE_PRIVATE)
        return prefs.getString("auth0_domain", "login.example.com") ?: "login.example.com"
    }

    fun login(callback: Callback<Credentials, AuthenticationException>) {
        val account = Auth0.getInstance(clientId, getAuth0Domain())

        WebAuthProvider.login(account)
            .withScheme("demo")
            .withScope("openid profile email")
            .start(context, callback)
    }
}

React Native

Avec react-native-auth0 :
import Auth0 from 'react-native-auth0';

const auth0 = new Auth0({
  domain: 'login.example.com',
  clientId: 'YOUR_CLIENT_ID'
});

// Connexion
auth0.webAuth
  .authorize({
    scope: 'openid profile email'
  })
  .then(credentials => {
    console.log('Logged in!');
  })
  .catch(error => {
    console.log(error);
  });

Flutter

Avec flutter_auth0 :
import 'package:auth0_flutter/auth0_flutter.dart';

final auth0 = Auth0(
  'login.example.com',
  'YOUR_CLIENT_ID'
);

// Connexion
try {
  final credentials = await auth0.webAuthentication().login();
  print('Logged in successfully');
} catch (e) {
  print('Login failed: $e');
}

SDK de gestion

Les SDK de gestion permettent d’interagir avec la Management API d’Auth0. Si vous utilisez des domaines personnalisés, vous devrez peut-être inclure l’en-tête auth0-custom-domain ou utiliser le domaine par défaut.

SDK de gestion pour Node.js

import { ManagementClient, CustomDomainHeader } from "auth0";

// Domaine personnalisé global (envoyé sur toutes les requêtes sur liste blanche)
const management = new ManagementClient({
  domain: 'tenant.auth0.com',
  clientId: 'YOUR_M2M_CLIENT_ID',
  clientSecret: 'YOUR_M2M_CLIENT_SECRET',
  withCustomDomainHeader: 'login.example.com', 
});

// Lister les utilisateurs (point de terminaison sur liste blanche - l'en-tête est envoyé automatiquement)
const users = await management.users.getAll();

// Remplacement par requête (a la priorité sur le global)
const reqOptions = {
  ...CustomDomainHeader("specific-user-request.exampleco.com"),
};
const response = await management.users.getAll({}, reqOptions);

SDK de gestion pour Python

from auth0.management import ManagementClient, CustomDomainHeader

# Domaine personnalisé global (envoyé pour toutes les requêtes sur liste blanche)
client = ManagementClient(
    domain='tenant.auth0.com',
    client_id='YOUR_M2M_CLIENT_ID',
    client_secret='YOUR_M2M_CLIENT_SECRET',
    custom_domain='login.example.com',
)

# Lister les utilisateurs (point de terminaison sur liste blanche - l'en-tête est envoyé automatiquement)
users = client.users.list()

# Remplacement par requête (a la priorité sur le global)
client.users.create(
    connection='Username-Password-Authentication',
    email='user@example.com',
    password='SecurePass123!',
    request_options=CustomDomainHeader('login.brand2.com'),
)

SDK de gestion pour Go

import (
    "context"
    management "github.com/auth0/go-auth0/v2/management/client"
    "github.com/auth0/go-auth0/v2/management/option"
)

// Niveau application : applique automatiquement l'en-tête de domaine personnalisé aux points de terminaison sur liste blanche
mgmt, err := management.New(
    "{yourDomain}",
    option.WithClientCredentials("{yourClientId}", "{yourClientSecret}"),
    option.WithCustomDomainHeader("login.example.com"),
)
if err != nil {
    // Gérer l'erreur
}

// Lister les utilisateurs (point de terminaison sur liste blanche - l'en-tête est envoyé automatiquement)
userList, err := mgmt.Users.List(context.Background(), nil)

// Remplacement par requête (a la priorité sur le niveau application)
userList, err := mgmt.Users.List(
    context.Background(),
    nil,
    option.WithCustomDomainHeader("specific-request.exampleco.com"),
)

Validation du jeton

Lorsque vous utilisez des domaines personnalisés, mettez à jour la validation de votre jeton pour qu’elle accepte le domaine personnalisé comme émetteur.

Node.js (Express)

Avec express-jwt ou jose :
const { expressjwt } = require('express-jwt');
const { expressJwtSecret } = require('jwks-rsa');

app.use(
  expressjwt({
    secret: expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksUri: 'https://login.example.com/.well-known/jwks.json'  // Domaine personnalisé
    }),
    audience: 'YOUR_API_IDENTIFIER',
    issuer: 'https://login.example.com/',  // Domaine personnalisé en tant qu'émetteur
    algorithms: ['RS256']
  })
);
Pour plusieurs domaines personnalisés :
const validIssuers = [
  'https://login.brand1.com/',
  'https://login.brand2.com/',
  'https://login.example.com/'
];

app.use(
  expressjwt({
    secret: expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: (req) => {
        // Extraire l'émetteur du jeton pour déterminer l'URI JWKS
        const token = req.headers.authorization?.split(' ')[1];
        if (token) {
          const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
          return `${payload.iss}.well-known/jwks.json`;
        }
        return 'https://login.example.com/.well-known/jwks.json';
      }
    }),
    audience: 'YOUR_API_IDENTIFIER',
    issuer: validIssuers,  // Accepter plusieurs émetteurs
    algorithms: ['RS256']
  })
);

Python (Flask)

À l’aide de python-jose :
from jose import jwt
from functools import wraps
from flask import request, jsonify

def get_token_auth_header():
    auth = request.headers.get('Authorization', None)
    if not auth:
        raise Exception('Authorization header is expected')

    parts = auth.split()
    if parts[0].lower() != 'bearer':
        raise Exception('Authorization header must start with Bearer')
    elif len(parts) == 1:
        raise Exception('Token not found')
    elif len(parts) > 2:
        raise Exception('Authorization header must be Bearer token')

    return parts[1]

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = get_token_auth_header()

        # Prendre en charge plusieurs domaines personnalisés
        valid_issuers = [
            'https://login.brand1.com/',
            'https://login.brand2.com/',
            'https://login.example.com/'
        ]

        try:
            # Obtenir le JWKS depuis le domaine personnalisé
            unverified = jwt.get_unverified_header(token)
            issuer = jwt.get_unverified_claims(token)['iss']

            if issuer not in valid_issuers:
                raise Exception('Invalid issuer')

            jwks_uri = f"{issuer}.well-known/jwks.json"
            jwks = requests.get(jwks_uri).json()

            payload = jwt.decode(
                token,
                jwks,
                algorithms=['RS256'],
                audience='YOUR_API_IDENTIFIER',
                issuer=valid_issuers
            )
        except Exception as e:
            return jsonify({'error': str(e)}), 401

        return f(*args, **kwargs)

    return decorated

Java (Spring Boot)

Avec Spring Security :
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${auth0.custom-domain}")
    private String customDomain;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .mvcMatchers("/api/public").permitAll()
            .mvcMatchers("/api/private").authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .decoder(jwtDecoder());
    }

    @Bean
    JwtDecoder jwtDecoder() {
        String issuerUri = "https://" + customDomain + "/";

        NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri);

        // Valider l'audience
        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }
}

Configuration propre à chaque environnement

Utilisez des variables d’environnement pour gérer les domaines personnalisés selon l’environnement :

Structure du fichier .env

# Développement
AUTH0_DOMAIN=dev.example.com
AUTH0_CLIENT_ID=dev_client_id
AUTH0_CLIENT_SECRET=dev_client_secret

# Préproduction
# AUTH0_DOMAIN=staging.example.com
# AUTH0_CLIENT_ID=staging_client_id
# AUTH0_CLIENT_SECRET=staging_client_secret

# Production
# AUTH0_DOMAIN=login.example.com
# AUTH0_CLIENT_ID=prod_client_id
# AUTH0_CLIENT_SECRET=prod_client_secret

Chargement de la configuration

require('dotenv').config();

const auth0Config = {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET
};

Dépannage

Problèmes courants

ProblèmeCauseSolution
Erreur d’émetteur invalideLa validation du jeton s’attend au domaine canonique, mais reçoit un domaine personnaliséMettez à jour la validation du jeton pour accepter le domaine personnalisé comme émetteur
Échec de la récupération du JWKSL’URI du JWKS pointe vers le domaine canoniqueMettez à jour l’URI du JWKS pour utiliser le domaine personnalisé : https://custom-domain/.well-known/jwks.json
URI de redirection non concordanteL’URL de rappel ne correspond pas aux URI de redirection configuréesAjoutez l’URL de rappel du domaine personnalisé aux paramètres de l’application
Erreurs d’origine croisée (CORS)Le domaine personnalisé ne figure pas parmi les origines autoriséesAjoutez le domaine personnalisé à Allowed Web Origins dans les paramètres de l’application
Échec du chargement de LockLe paramètre configurationBaseUrl est absentAjoutez le paramètre configurationBaseUrl avec l’URL du CDN régional

Bonnes pratiques

  1. Utilisez des variables d’environnement : stockez les domaines personnalisés dans des fichiers de configuration propres à chaque environnement
  2. Validez plusieurs émetteurs : si vous utilisez plusieurs domaines personnalisés, configurez la validation des jetons pour les accepter tous comme émetteurs valides
  3. Mettez à jour les URL de rappel : assurez-vous que tous les domaines personnalisés sont ajoutés à Allowed Callback URLs dans les paramètres de l’application
  4. Testez de façon approfondie : testez l’authentification avec chaque domaine personnalisé avant la mise en production
  5. Surveillez les émetteurs de jetons : consignez et surveillez la revendication iss dans les jetons pour vous assurer que le bon domaine personnalisé est utilisé
  6. Documentez les mappages de domaines : tenez à jour une documentation claire indiquant quelles applications utilisent quels domaines personnalisés
  7. Gérez correctement les échecs : implémentez une gestion des erreurs appropriée pour les échecs d’authentification
  8. Mettez en cache JWKS : mettez en cache les données JWKS pour améliorer les performances et réduire le nombre de requêtes

En savoir plus