fix(auth): /client/authenticate tampoco responde 404, y el ADR-0165 dice qué oráculo era cada uno - #18
Merged
Conversation
…ice qué oráculo era cada uno Dos correcciones al cambio anterior, ambas salidas de reportarlo aguas arriba (unimar-peru/unimar-ums#214), donde hubo que verificar el código antes de escribir la acusación. ## El mismo 404 estaba en el endpoint que se citaba como ejemplo `ClientAuthEndpoints.GetStatusCode` mapeaba AUTH_004 a 404 mientras colapsaba a 401 todo lo demás de nivel usuario o inquilino. Ahí duele más que en el portal: ese endpoint persigue la indistinguibilidad como diseño explícito —`SpanishMessage` devuelve la MISMA cadena exacta para AUTH_002, AUTH_003, AUTH_004, AUTH_006 y AUTH_017, y no hay humano al otro lado a quien darle la pista—, así que el status era la única señal que quedaba en pie. El comentario de G-053 en ese método decía que «AUTH_004/005 conservan su semántica»: AUTH_005 sí había colapsado, AUTH_004 se quedó en 404 y nadie lo notó. Ningún test lo afirmaba, que es parte del hallazgo. ## AUTH_004 no es «el usuario no existe» El ADR lo daba por sentado y de ahí deducía un barrido anónimo de listas de correo. El código dice otra cosa: en la rama local un usuario inexistente devuelve AUTH_006 —el mismo código que una contraseña equivocada—, así que ahí nunca hubo nada que filtrar. AUTH_004 sólo se emite en la rama federada y DESPUÉS de que el IdP haya autenticado con éxito: para ver ese 404 hay que traer credenciales válidas. Es un oráculo para quien ya está dentro (qué identidades federadas tienen cuenta UMS en qué inquilino), no para un anónimo. Sigue mereciendo el 401 —el mensaje calla eso a propósito y el estado lo regalaba—, pero por la razón correcta. El oráculo anónimo y barato era AUTH_002: la búsqueda del inquilino precede a cualquier credencial, así que separa códigos reales de inventados a petición por candidato sin credencial alguna. Y ése §2.3 lo deja abierto a propósito, porque el cuerpo ya lo revela. Conviene que el ADR no se lea como si cerrara un barrido que no cierra. Verificado: contrato 38/38, integración 314/316 (2 saltados), build 0 errores. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Continuación de #16, salida de reportarlo aguas arriba (unimar-peru/unimar-ums#214): al verificar el código antes de escribir la acusación aparecieron dos cosas que #16 no cerró.
1. El mismo 404 estaba en el endpoint que #16 citaba como ejemplo
ClientAuthEndpoints.GetStatusCodemapeabaAUTH_004a 404 mientras colapsaba a 401 todo lo demás de nivel usuario o inquilino.Ahí duele más que en el portal.
/client/authenticatepersigue la indistinguibilidad como diseño explícito:SpanishMessagedevuelve la misma cadena exacta paraAUTH_002,AUTH_003,AUTH_004,AUTH_006yAUTH_017, y no hay ningún humano al otro lado a quien darle la pista. Con el cuerpo ya uniformado, el status era la única señal que quedaba en pie — y decía «el IdP te autenticó pero aquí no tienes cuenta», que es justo lo que el mensaje se negaba a decir.El comentario de G-053 en ese método afirmaba que «AUTH_004/005 conservan su semántica».
AUTH_005sí había colapsado a 401;AUTH_004se quedó en 404 y nadie lo notó. Ningún test afirmaba ese 404: se pudo cambiar sin tocar una sola aserción, lo que es parte del hallazgo.G-053 no se amplía ni se reinterpreta: se termina de aplicar donde ya regía. Queda como §2.4 del ADR-0165.
2.
AUTH_004no es «el usuario no existe» — el ADR describía una amenaza que este código no tiene#16 dio por sentado que
AUTH_004era «usuario inexistente» y de ahí dedujo el barrido anónimo de una lista de correos. El código dice otra cosa:AuthenticateLocalAsync)AUTH_006AuthenticateIdpAsync)AUTH_004Es decir: en la rama local nunca hubo nada que filtrar, y para llegar al 404 de
AUTH_004hay que traer credenciales válidas del IdP. No es el barrido anónimo de una lista de correos; es un oráculo para quien ya está dentro — alguien con cuenta en el IdP corporativo puede recorrer inquilinos y averiguar en cuáles tiene cuenta UMS una identidad federada. Sigue siendo lo que el mensaje calla a propósito y el estado regalaba, y sigue mereciendo el 401, pero por la razón correcta.El oráculo anónimo y barato era
AUTH_002, noAUTH_004: la búsqueda del inquilino precede a cualquier credencial, así que separa códigos de inquilino reales de inventados a una petición por candidato y sin credencial de ningún tipo. Y ése §2.3 lo deja abierto a propósito, porque el cuerpo ya lo revela. Conviene que el ADR no se lea como si cerrara un barrido que no cierra: alguien lo citará dentro de un año.Corregido en las dos versiones del ADR (ES/EN) y en la ficha TD-009.
Alcance
Un solo cambio de comportamiento:
/api/v1/client/authenticatedevuelve401en vez de404cuando el IdP autentica a alguien sin cuenta UMS en el inquilino. El cuerpo no cambia (ya era el mensaje genérico). El SDK no mira ese status para distinguir nada.Verificación
Ums.ContractTest— 38/38Ums.Presentation.IntegrationTest— 314/316 (2 saltados, los mismos de siempre)dotnet build Ums.sln— 0 errores🤖 Generated with Claude Code