HTTP Authentication

WWW-Authenticate

Es la cabecera HTTP con la que un servidor responde 401 Unauthorized para decirle al cliente cómo debe autenticarse. No es un mecanismo de login concreto: es el protocolo de negociación que anuncia qué esquema de credenciales acepta el recurso.

Estándar desde RFC 7235, es la base del discovery de autenticación en la web: el cliente no necesita conocer de antemano el método; lo aprende del propio reto del servidor.

El flujo de reto–respuesta

1
GET /recurso
El cliente pide un recurso protegido sin credenciales.
2
401 + WWW-Authenticate
El servidor rechaza y describe el esquema requerido en la cabecera.
3
Authorization: …
El cliente reintenta con la credencial del esquema indicado.
4
200 OK
El servidor valida y entrega el recurso.

El paso 2 es la clave: la cabecera WWW-Authenticate lleva el scheme(Basic, Digest, Bearer…) y parámetros como realm,scope o nonce. El cliente responde reflejando esa información en la cabecera Authorization.

Por qué el discovery ayuda a la web

Sin configuración previa. Un cliente genérico puede llegar a cualquier recurso, recibir un 401 y saber al instante qué credencial preparar.

Interoperabilidad. Navegadores, librerías HTTP y agentes hablan el mismo lenguaje. El servidor cambia su política de auth y los clientes se adaptan solos.

Evolución sin romper clientes. Con RFC 9728 el reto puede apuntar a un documento de metadatos, encadenando descubrimiento de servidores de autorización, scopes y endpoints. Esto es lo que hace viables ecosistemas como OAuth, OIDC, agentes y MCP.

Challenges en vivo

Esquemas de autenticación

Basic

RFC 7617No recomendado

Envía usuario y contraseña codificados en Base64 en cada petición. Base64 no es cifrado — cualquiera que intercepte la cabecera lee la contraseña en claro.

Reto del servidor
WWW-Authenticate: Basic realm="demo"
  • ·Reversible al instante: atob("dXNlcjpwYXNzd29yZA==") → "user:password".
  • ·Obliga a usar HTTPS siempre; sin TLS la credencial viaja expuesta.
  • ·No hay expiración ni revocación: la credencial vale hasta que cambie la contraseña.
  • ·El navegador muestra un diálogo nativo de usuario/contraseña.
Respuesta del cliente
Authorization: Basic dXNlcjpwYXNzd29yZA==
Compatible con
  • Chrome
  • Firefox
  • Safari
  • Edge
  • curl
  • wget
  • http (HTTPie)
  • axios
  • requests
  • Postman
  • Insomnia

Digest

RFC 7616Heredado

Esquema desafío–respuesta: el servidor envía un nonce y el cliente responde con un hash, nunca con la contraseña. Mejora a Basic pero MD5 ya es débil.

Reto del servidor
WWW-Authenticate: Digest realm="demo", nonce="…", algorithm=MD5, qop="auth"
  • ·La contraseña nunca viaja por la red, solo un hash derivado.
  • ·El nonce mitiga ataques de repetición (replay).
  • ·qop="auth" protege también el método y la URI de la petición.
  • ·Sigue dependiendo de MD5/SHA-256; menos práctico que tokens para APIs.
Respuesta del cliente
Authorization: Digest username="user", realm="demo", nonce="…", uri="/demo/digest", response="…", qop=auth, nc=00000001, cnonce="…"
Compatible con
  • Chrome
  • Firefox
  • Safari
  • Edge
  • curl
  • wget
  • http (HTTPie)
  • requests
  • Postman
  • Insomnia

Bearer

RFC 6750Recomendado para APIs

El cliente porta un token de acceso (a menudo un JWT) emitido por un servidor de autorización. "El que lo porta, lo usa" — por eso el token debe protegerse como una credencial.

Reto del servidor
WWW-Authenticate: Bearer
  realm="demo",
  scope="read write",
  error="invalid_token",
  error_description="El token ha expirado o es inválido",
  error_uri="https://…/docs/errors/invalid_token"
  • ·realm (opcional) — ámbito de protección; no debe repetirse.
  • ·scope (opcional) — lista de scopes requeridos separados por espacio, ej. "read write".
  • ·error (opcional) — razón del rechazo: invalid_request · invalid_token · insufficient_scope.
  • ·error_description (opcional) — explicación legible del error, solo para desarrolladores.
  • ·error_uri (opcional) — URI absoluta con documentación del error.
  • ·Si el token es un JWT, su header y payload son legibles (no cifrados) — no guardes secretos dentro.
Opción A · URL definida explícitamente (out-of-band)
# El cliente conoce el token endpoint de antemano (documentación, SDK, config)
→ POST https://auth.example.com/token
   grant_type=client_credentials&scope=read
← 200 { "access_token": "eyJ…", "token_type": "Bearer", "expires_in": 3600 }
Opción B · URL inferida vía RFC 8414 (issuer conocido)
# Si se conoce el issuer, se infiere la URL del AS metadata:
→ GET https://auth.example.com/.well-known/oauth-authorization-server
← 200 {
    "token_endpoint": "https://auth.example.com/token",
    "authorization_endpoint": "https://auth.example.com/authorize"
  }
# Fallback OIDC si 404:
→ GET https://auth.example.com/.well-known/openid-configuration
Respuesta del cliente
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
  • ·RFC 6750 no incluye la URL del AS en el reto — el cliente debe conocerla de antemano o inferirla.
  • ·Opción A (definida): el token endpoint se configura explícitamente en el cliente (SDK, env var, docs).
  • ·Opción B (inferida): si se conoce el issuer, se construye /.well-known/oauth-authorization-server siguiendo RFC 8414.
  • ·Para discovery completamente automático desde el reto 401, usar Bearer + Discovery (RFC 9728).
Compatible con
  • curl
  • http (HTTPie)
  • Postman
  • Insomnia
  • Claude
  • Perplexity

NTLM

MS-NLMPHeredado · Windows

NT LAN Manager — protocolo challenge-response de Microsoft. El cliente envía un NEGOTIATE, el servidor responde con un CHALLENGE (nonce de 8 bytes) y el cliente retorna un mensaje AUTHENTICATE con un hash NTLMv2. Ligado a la conexión y centrado en Windows.

Reto del servidor
WWW-Authenticate: NTLM
  • ·Handshake de tres mensajes: NEGOTIATE → CHALLENGE → AUTHENTICATE.
  • ·La contraseña nunca viaja en la red — solo una respuesta derivada con HMAC-MD5.
  • ·Ligado a la conexión: los tres mensajes deben ir por la misma conexión TCP.
  • ·NTLMv2 agrega un challenge del cliente y timestamp para prevenir ataques precomputados.
  • ·Sin autenticación mutua por defecto — el cliente no verifica la identidad del servidor.
  • ·Supersedido por Kerberos en entornos Active Directory; los navegadores requieren config empresarial para usarlo.
1 · Cliente envía NEGOTIATE (Tipo 1)
→ GET /demo/ntlm
   Authorization: NTLM TlRMTVNTUAABAAAAB4II…   (base64 Tipo 1)
← 401 WWW-Authenticate: NTLM TlRMTVNTUAACAAAAB…  (base64 Tipo 2 challenge)
2 · Servidor retorna CHALLENGE (Tipo 2)
NTLM Tipo 2 — CHALLENGE
  ServerChallenge : a1b2c3d4e5f60718   (8 bytes aleatorios)
  TargetName      : DEMO
  TargetInfo      : [ MsvAvNbDomainName="DEMO" ]
3 · Cliente envía AUTHENTICATE (Tipo 3)
NTHash        = MD4(UTF-16LE(contraseña))
NTLMv2Hash    = HMAC-MD5(NTHash, upper(usuario) + dominio)
NTProofStr    = HMAC-MD5(NTLMv2Hash, serverChallenge + Blob)
NTResponse    = NTProofStr + Blob

→ GET /demo/ntlm
   Authorization: NTLM TlRMTVNTUAADAAAA…   (base64 Tipo 3)
← 200 OK
Compatible con
  • Chrome
  • Firefox
  • Safari
  • Edge
  • curl
  • Postman

Kerberos (Negotiate)

RFC 4559Empresarial · SSO

El esquema Negotiate envuelve un AP-REQ de Kerberos dentro de un token GSSAPI/SPNEGO. El cliente nunca escribe una contraseña — presenta un ticket emitido por el Centro de Distribución de Claves (KDC) del dominio de Active Directory, habilitando Single Sign-On real.

Reto del servidor
WWW-Authenticate: Negotiate
  • ·Single Sign-On: el navegador usa la sesión Kerberos activa de Windows/macOS — sin diálogo de contraseña.
  • ·Autenticación mutua: el cliente prueba su identidad con el ticket; el servidor prueba la suya con su clave de largo plazo.
  • ·El ticket tiene vida corta (por defecto 10 h) y es emitido por el KDC, no por el servidor web.
  • ·El servidor debe estar registrado en AD como SPN: HTTP/hostname@REALM.
  • ·SPNEGO (RFC 4178) es el envoltorio de negociación; puede caer en NTLM si Kerberos falla.
  • ·Los navegadores requieren allowlist explícita vía política (AuthServerAllowlist / trusted-uris).
  • ·No se puede usar fuera de un entorno de dominio — requiere un KDC en vivo para validar tickets.
1 · Servidor emite reto Negotiate
← 401 WWW-Authenticate: Negotiate
2 · Cliente obtiene ticket Kerberos (SSPI/GSSAPI)
# El navegador usa la sesión Windows/macOS de Kerberos de forma transparente
El KDC emite un Service Ticket para el SPN: HTTP/hostname@REALM
  El ticket está cifrado con la clave del servidor (AES)
  La clave de sesión es compartida entre cliente y servidor
3 · Cliente envía AP-REQ envuelto en SPNEGO
GSSAPI Application (0x60)
  OID 1.3.6.1.5.5.2  — SPNEGO
  negTokenInit [0]
    mechTypes: [1.2.840.113554.1.2.2]  — Kerberos 5
    mechToken: AP-REQ {
      pvno=5, msg-type=14,
      ap-options: MUTUAL-REQUIRED,
      ticket: <cifrado con clave del servidor>,
      authenticator: <cifrado con clave de sesión>
    }

→ GET /demo/kerberos
   Authorization: Negotiate YIIBpTCCAaGgAwIBBaEDAgEC…
← 200 OK
Compatible con
  • curl

Bearer + Discovery

RFC 9728Moderno · Discovery

OAuth 2.0 Protected Resource Metadata. El reto 401 ya no solo dice "necesitas un Bearer": apunta a un documento de metadatos que describe DÓNDE y CÓMO obtener un token. El cliente descubre todo automáticamente.

Reto del servidor
WWW-Authenticate: Bearer resource_metadata="https://…/.well-known/oauth-protected-resource.json"
  • ·resource_metadata — único parámetro del reto; URL del documento de metadatos del recurso.
  • ·El cliente extrae authorization_servers[0] como issuer identifier del AS.
  • ·Habilita clientes genéricos: descubren el flujo completo a partir de un único 401.
  • ·Clave para ecosistemas como agentes y MCP, donde el cliente no conoce de antemano al emisor.
1 · Recurso retorna 401
← 401 WWW-Authenticate: Bearer resource_metadata="https://…/.well-known/oauth-protected-resource.json"
2 · Metadatos del recurso — RFC 9728
→ GET https://…/.well-known/oauth-protected-resource.json
← 200 {
    "resource": "https://…/demo/bearer2",
    "authorization_servers": ["https://auth.example.com"],
    "scopes_supported": ["read", "write"]
  }
3a · AS Metadata — RFC 8414
→ GET https://auth.example.com/.well-known/oauth-authorization-server
← 200 {
    "issuer": "https://auth.example.com",
    "authorization_endpoint": "https://auth.example.com/authorize",
    "token_endpoint": "https://auth.example.com/token",
    "scopes_supported": ["read", "write"],
    "grant_types_supported": ["authorization_code", "client_credentials"]
  }
3b · Fallback OpenID Connect Discovery
→ GET https://auth.example.com/.well-known/openid-configuration
← 200 {
    "issuer": "https://auth.example.com",
    "authorization_endpoint": "https://auth.example.com/authorize",
    "token_endpoint": "https://auth.example.com/token",
    "userinfo_endpoint": "https://auth.example.com/userinfo",
    "jwks_uri": "https://auth.example.com/jwks"
  }
4 · Obtener token y reintentar
→ POST https://auth.example.com/token
← 200 { "access_token": "eyJ…", "token_type": "Bearer", "expires_in": 3600 }

→ GET https://…/demo/bearer2
   Authorization: Bearer eyJ…
← 200 OK
  • ·Paso 3a — RFC 8414: busca /.well-known/oauth-authorization-server[/{path}] usando el issuer.
  • ·Paso 3b — Si 404, fallback a OpenID Connect Discovery: /.well-known/openid-configuration.
  • ·La construcción de la URL sigue la misma regla: well-known insertado entre host y path del issuer.
  • ·El documento AS expone authorization_endpoint, token_endpoint, scopes_supported, grant_types…
  • ·Con esos endpoints el cliente completa el flujo OAuth y obtiene un token Bearer válido.
Compatible con
  • Claude
  • Perplexity

Glosario de términos

AS
Authorization Server — servidor que emite tokens de acceso tras verificar la identidad del cliente.
RS
Resource Server — servidor que aloja el recurso protegido y valida el token presentado.
Bearer
Esquema de token donde "quien lo porta, lo usa". El token debe protegerse como una credencial.
JWT
JSON Web Token — token firmado (y opcionalmente cifrado) cuyo header y payload son legibles en Base64.
Scope
Ámbito de acceso solicitado, ej. "read write". El AS puede otorgar un subconjunto de los scopes pedidos.
Nonce
Número usado una sola vez. En Digest previene ataques de repetición; en OIDC vincula el token al cliente.
Realm
Cadena que identifica el espacio de protección. Varias rutas del mismo servidor pueden compartir un realm.
Issuer
Identificador canónico del AS (URL). Se usa para derivar las URLs de discovery sin configuración previa.
OIDC
OpenID Connect — capa de identidad sobre OAuth 2.0 que añade un ID Token con datos del usuario.
MCP
Model Context Protocol — protocolo para agentes de IA que usa Bearer + Discovery (RFC 9728) para autenticarse.