Saltar al contenido principal
Al usar varios dominios personalizados, debe configurar sus SDK de Auth0 para usar el dominio personalizado adecuado para la autenticación. Esta guía abarca la configuración de los SDK para distintas plataformas y escenarios.

Conceptos clave

Parámetro de dominio

Todos los SDK de Auth0 requieren un parámetro domain que especifica qué dominio de Auth0 se usará para la autenticación. Si usa dominios personalizados, establezca este parámetro en su dominio personalizado en lugar del dominio canónico de Auth0. Sin dominio personalizado:
domain: 'tenant.auth0.com'
Con un dominio personalizado:
domain: 'login.example.com'

Emisor del token

Al usar un dominio personalizado, los tokens tendrán la reclamación iss (emisor) configurada con su dominio personalizado:
{
  "iss": "https://login.example.com/",
  "sub": "auth0|123456",
  "aud": "your-client-id"
}
Debe configurar la validación de su token para aceptar su dominio personalizado como emisor válido.

SDK de autenticación

Al usar MCD, el cliente es responsable de proporcionar y validar todos los dominios personalizados. Al configurar los SDK para resolver los dominios personalizados del Tenant mediante las funciones de resolución de dominios, usted es responsable de garantizar que todos los dominios resueltos sean de confianza. Una configuración incorrecta del resolvedor de dominios puede permitir la omisión de la autenticación en la relying party o exponer la aplicación a falsificación de solicitudes del lado del servidor. No configurar correctamente sus dominios y servidores proxy puede generar vulnerabilidades críticas de seguridad por las que Okta no asume ninguna responsabilidad.

SDK SPA de Auth0 (JavaScript)

Para aplicaciones de página única que usan el SDK SPA de Auth0:

Next.js

Para aplicaciones de Next.js que usan el SDK de Auth0 para Next.js (v4+): Conceptos clave de MCD con Next.js:
  • Un solo Tenant de Auth0, varios dominios: Todos los dominios personalizados comparten el mismo clientId y clientSecret, ya que pertenecen al mismo Tenant de Auth0.
  • Función DomainResolver: El parámetro domain acepta una función (config: { headers: Headers; url?: URL }) => Promise<string> | string. Esto permite resolver el dominio dinámicamente para cada solicitud en función de los encabezados de la solicitud entrante.
  • Almacenamiento en caché de instancias: El SDK almacena automáticamente en caché las instancias de Auth0Client por dominio mediante una caché LRU acotada (máximo 100 entradas) para mejorar el rendimiento.
  • Aislamiento de sesiones: Las sesiones creadas a través de un dominio personalizado quedan aisladas en ese dominio y no pueden usarse indistintamente con sesiones de otro dominio.
  • Parámetro URL: El parámetro url del resolvedor es undefined en Server Components y Server Actions; solo está disponible en middleware o rutas de API.
  • Ajuste de la caché de descubrimiento: Configura el almacenamiento en caché de los metadatos de OIDC con la opción discoveryCache:
    const auth0 = new Auth0Client({
      // ... otra configuración
      discoveryCache: {
        ttl: 600,      // Caché durante 10 minutos (predeterminado)
        maxEntries: 100  // Número máximo de emisores almacenados en caché (predeterminado: 100, expulsión LRU)
      }
    });
    

SDK de React de Auth0

Para aplicaciones React que usan el SDK de React de 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>
  );
}
Para escenarios con varios dominios:
import { Auth0Provider } from '@auth0/auth0-react';

function App() {
  // Determinar el dominio personalizado según el entorno o contexto
  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

Para las aplicaciones que usan 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'
});

// Iniciar sesión
webAuth.authorize();

Node.js (Express)

Para aplicaciones Node.js que usan express-openid-connect:
const { auth } = require('express-openid-connect');

app.use(
  auth({
    authRequired: false,
    auth0Logout: true,
    issuerBaseURL: 'https://login.example.com',  // Tu dominio personalizado
    baseURL: 'http://localhost:3000',
    clientID: 'YOUR_CLIENT_ID',
    secret: 'YOUR_CLIENT_SECRET'
  })
);
Para escenarios multiinquilino:
const { auth } = require('express-openid-connect');

// Middleware para determinar el dominio personalizado por solicitud
app.use((req, res, next) => {
  // Extraer el identificador de Tenant del subdominio, ruta o encabezado
  const tenant = req.subdomains[0] || 'default';

  // Asignar el Tenant al dominio personalizado
  const domainMap = {
    'customer1': 'login.customer1.com',
    'customer2': 'login.customer2.com',
    'default': 'login.example.com'
  };

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

// Configuración de autenticación dinámica
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 para móviles

iOS (Swift)

Con 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)")
        }
    }
Para seleccionar el dominio dinámicamente:
import Auth0

class AuthService {
    private let clientId = "YOUR_CLIENT_ID"

    func getAuth0Domain() -> String {
        // Determinar el dominio según la configuración de la aplicación
        if let savedDomain = UserDefaults.standard.string(forKey: "auth0Domain") {
            return savedDomain
        }
        return "login.example.com" // Predeterminado
    }

    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)

Uso de 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"  // Tu dominio personalizado
)

WebAuthProvider.login(account)
    .withScheme("demo")
    .withScope("openid profile email")
    .start(this, object : Callback<Credentials, AuthenticationException> {
        override fun onSuccess(credentials: Credentials) {
            // Manejar el éxito
        }

        override fun onFailure(exception: AuthenticationException) {
            // Manejar el error
        }
    })
Para admitir varios dominios:
class AuthManager(private val context: Context) {
    private val clientId = "YOUR_CLIENT_ID"

    private fun getAuth0Domain(): String {
        // Recuperar de las preferencias compartidas o la configuración de la aplicación
        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

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

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

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

Flutter

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

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

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

SDK de administración

Los SDK de administración se utilizan para interactuar con la Management API de Auth0. Al usar dominios personalizados, es posible que deba incluir el encabezado auth0-custom-domain o usar el dominio predeterminado.

SDK de administración de Node.js

import { ManagementClient, CustomDomainHeader } from "auth0";

// Dominio personalizado global (enviado en todas las solicitudes en lista blanca)
const management = new ManagementClient({
  domain: 'tenant.auth0.com',
  clientId: 'YOUR_M2M_CLIENT_ID',
  clientSecret: 'YOUR_M2M_CLIENT_SECRET',
  withCustomDomainHeader: 'login.example.com', 
});

// Listar usuarios (endpoint en lista blanca - el encabezado se envía automáticamente)
const users = await management.users.getAll();

// Anulación por solicitud (tiene precedencia sobre la configuración global)
const reqOptions = {
  ...CustomDomainHeader("specific-user-request.exampleco.com"),
};
const response = await management.users.getAll({}, reqOptions);

SDK de administración para Python

from auth0.management import ManagementClient, CustomDomainHeader

# Dominio personalizado global (enviado en todas las solicitudes de la lista blanca)
client = ManagementClient(
    domain='tenant.auth0.com',
    client_id='YOUR_M2M_CLIENT_ID',
    client_secret='YOUR_M2M_CLIENT_SECRET',
    custom_domain='login.example.com',
)

# Listar usuarios (endpoint en lista blanca - el encabezado se envía automáticamente)
users = client.users.list()

# Anulación por solicitud (tiene precedencia sobre el global)
client.users.create(
    connection='Username-Password-Authentication',
    email='user@example.com',
    password='SecurePass123!',
    request_options=CustomDomainHeader('login.brand2.com'),
)

SDK de administración para Go

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

// Nivel de cliente: aplica automáticamente el encabezado de dominio personalizado a los endpoints en lista blanca
mgmt, err := management.New(
    "{yourDomain}",
    option.WithClientCredentials("{yourClientId}", "{yourClientSecret}"),
    option.WithCustomDomainHeader("login.example.com"),
)
if err != nil {
    // Gestionar error
}

// Listar usuarios (endpoint en lista blanca - el encabezado se envía automáticamente)
userList, err := mgmt.Users.List(context.Background(), nil)

// Anulación por solicitud (tiene prioridad sobre el nivel de cliente)
userList, err := mgmt.Users.List(
    context.Background(),
    nil,
    option.WithCustomDomainHeader("specific-request.exampleco.com"),
)

Validación de tokens

Si usa dominios personalizados, actualice la validación de tokens para aceptar el dominio personalizado como emisor.

Node.js (Express)

Con express-jwt o 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'  // Dominio personalizado
    }),
    audience: 'YOUR_API_IDENTIFIER',
    issuer: 'https://login.example.com/',  // Dominio personalizado como emisor
    algorithms: ['RS256']
  })
);
Para varios dominios personalizados:
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) => {
        // Extraer el emisor del token para determinar el URI de 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,  // Aceptar múltiples emisores
    algorithms: ['RS256']
  })
);

Python (Flask)

Con 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()

        # Compatibilidad con múltiples dominios personalizados
        valid_issuers = [
            'https://login.brand1.com/',
            'https://login.brand2.com/',
            'https://login.example.com/'
        ]

        try:
            # Obtener JWKS del dominio personalizado
            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)

Uso de 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);

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

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }
}

Configuración específica del entorno

Use variables de entorno para administrar dominios personalizados entre entornos:

Estructura del archivo .env

# Desarrollo
AUTH0_DOMAIN=dev.example.com
AUTH0_CLIENT_ID=dev_client_id
AUTH0_CLIENT_SECRET=dev_client_secret

# Staging
# AUTH0_DOMAIN=staging.example.com
# AUTH0_CLIENT_ID=staging_client_id
# AUTH0_CLIENT_SECRET=staging_client_secret

# Producción
# AUTH0_DOMAIN=login.example.com
# AUTH0_CLIENT_ID=prod_client_id
# AUTH0_CLIENT_SECRET=prod_client_secret

Carga de la configuración

require('dotenv').config();

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

Solución de problemas

Problemas comunes

ProblemaCausaSolución
Error de emisor no válidoLa validación del token espera el dominio canónico, pero recibe un dominio personalizadoActualiza la validación del token para que acepte el dominio personalizado como emisor
Error al obtener el JWKSEl URI del JWKS apunta al dominio canónicoActualiza el URI del JWKS para usar el dominio personalizado: https://custom-domain/.well-known/jwks.json
Incompatibilidad del URI de redirecciónLa URL de callback no coincide con los URI de redirección configuradosAgrega la URL de callback del dominio personalizado a la configuración de la aplicación
Errores de origen cruzado (CORS)El dominio personalizado no está entre los orígenes permitidosAgrega el dominio personalizado a Allowed Web Origins en la configuración de la aplicación
Lock no se cargaFalta configurationBaseUrlAgrega el parámetro configurationBaseUrl con la URL regional del CDN

Prácticas recomendadas

  1. Usa variables de entorno: Almacena los dominios personalizados en archivos de configuración específicos de cada entorno
  2. Valida varios emisores: Si usas varios dominios personalizados, configura la validación de tokens para aceptar todos como emisores válidos
  3. Actualiza las URL de callback: Asegúrate de agregar todos los dominios personalizados a Allowed Callback URLs en la configuración de la aplicación
  4. Haz pruebas exhaustivas: Prueba la autenticación con cada dominio personalizado antes de pasar a producción
  5. Supervisa los emisores de tokens: Registra y supervisa el claim iss en los tokens para garantizar que se use el dominio personalizado correcto
  6. Documenta las asignaciones de dominios: Mantén una documentación clara sobre qué aplicaciones usan qué dominios personalizados
  7. Gestiona los fallos correctamente: Implementa un manejo adecuado de errores para los fallos de autenticación
  8. Almacena en caché JWKS: Almacena en caché los datos de JWKS para mejorar el rendimiento y reducir las solicitudes

Más información