El inicio de sesión OIDC falla con respuestas solo de ID-token tras la actualización a OAuth2 2.x

Tras actualizar nuestros entornos de Discourse autoalojados el 11 de septiembre de 2026, el inicio de sesión mediante OIDC comenzó a fallar con el siguiente error:

(oidc) Authentication failure! jwt_decode_failed:
JWT::DecodeError, Nil JSON web token

Nuestro entorno de desarrollo más antiguo continuó funcionando correctamente.

Entorno

  • Discourse ejecutándose en Docker en una VM de Azure Linux.
  • Azure Front Door → balanceador de carga interno → VM.
  • Discourse alojado bajo /forum.
  • Políticas personalizadas de Azure AD B2C para OIDC.
  • El tráfico saliente de la VM enrutado a través de Azure Firewall.
  • Revisión de Discourse con fallos: 74f839fb7.
  • Revisión de desarrollo funcional: 502aa3687.

Las versiones relevantes de las dependencias eran:

Dependencia Desarrollo funcional Producción con fallos
oauth2 1.4.11 2.0.25
omniauth-oauth2 1.7.3 1.9.0
jwt 2.10.1 3.2.0

El repositorio actualizado incluye d881bf2d4aabcf2430863a96beafaeabaf915702, «DEPS: Upgrade oauth2 to 2.x» (#43523).

Reproducción mínima

Esto no requiere conexión a la red, infraestructura de Azure ni credenciales reales:

require "oauth2"

puts Gem.loaded_specs.fetch("oauth2").version

client = OAuth2::Client.new(
  "dummy",
  "dummy",
  site: "https://example.invalid"
)

token = OAuth2::AccessToken.from_hash(
  client,
  {"id_token" => "dummy-id"}
)

puts "Main token: #{token.token.inspect}"
puts "ID token parameter: #{token["id_token"].inspect}"

Con OAuth2 1.4.11, el token principal está vacío y el parámetro del token ID permanece como "dummy-id".

Con OAuth2 2.0.25, el token principal se convierte en "dummy-id" y el parámetro del token ID se convierte en nil.

Relación con el fallo en Discourse

En la rama sin userinfo, el plugin de OIDC construye el token utilizando:

::OAuth2::AccessToken.from_hash(client, response.parsed)

Más tarde, intenta:

::JWT.decode(access_token["id_token"], nil, false).first

Cuando la biblioteca consume id_token como el token principal, la búsqueda posterior del parámetro devuelve nil.

Solución temporal probada

Cambiamos la construcción del token para preservar el token ID de la respuesta original:

payload = response.parsed
token = ::OAuth2::AccessToken.from_hash(client, payload)
token.params["id_token"] = payload["id_token"] if payload["id_token"]
token

Tras aplicar este parche y reiniciar el contenedor de UAT, el inicio de sesión fue exitoso. No se necesitaron cambios en Front Door, el firewall ni en B2C para esa recuperación.

Esto preserva la validación existente de claims y nonce y deja la rama de userinfo sin cambios.

No se incluye la respuesta cruda del token en vivo; el comportamiento de la biblioteca se reprodujo con datos falsos y la solución temporal se validó mediante un inicio de sesión real en UAT.

¿Esto ya está cubierto por una corrección upstream? De lo contrario, puedo enviar un PR enfocado con cobertura de regresión.

1 me gusta

Esto debería haberse corregido en:

Por favor, infórmennos si sigues viendo problemas después de desplegar ese commit.

7 Me gusta

Gracias. Esto resuelve el problema.

1 me gusta