Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ricardomb-tech/auth-service/llms.txt

Use this file to discover all available pages before exploring further.

Every error response from Auth Service — whether it originates from a failed login attempt, an expired token, a validation failure, or an access control check — uses the same application/problem+json content type defined by RFC 9457 (Problem Details for HTTP APIs). This unified contract means your client only needs to handle one error shape regardless of which endpoint it called or at which layer of the service the error occurred.

Error format

All error responses carry the Content-Type: application/problem+json header and a JSON body with the following fields:
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required.",
  "instance": "/api/v1/users/me"
}
type
string
A URI identifying the problem type. Auth Service currently uses "about:blank" for all errors, meaning the title field is the canonical human-readable identifier of the problem type.
title
string
A short, stable, human-readable summary of the problem type. Corresponds to the HTTP reason phrase of the status code (e.g. "Bad Request", "Unauthorized", "Forbidden").
status
number
The HTTP status code as an integer, mirroring the response’s actual HTTP status.
detail
string
A human-readable, instance-specific explanation of this particular occurrence of the problem. This field may be localized (some messages are in Spanish). Do not parse this field programmatically — it is for display and logging purposes only. Status codes and exception types are the stable programmatic signals.
instance
string
The request path that produced the error (e.g. "/auth/login"). Useful for correlating an error body with a specific API call in logs.

HTTP status codes

400 Bad Request

Returned when the request body fails @Valid bean validation (missing required field, value too long, format constraint violated) or when a domain invariant is violated (e.g. verification token already used, password reset token already consumed). Scenario: missing required field
{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "detail": "El email es obligatorio.",
  "instance": "/auth/register"
}
Scenario: blank exchange code
{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "detail": "El código de intercambio es obligatorio.",
  "instance": "/auth/oauth2/exchange"
}
Scenario: domain validation failure (e.g. verification token already used)
{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "detail": "El token de verificación ya ha sido utilizado o ha expirado.",
  "instance": "/auth/verify"
}

401 Unauthorized

Returned for any authentication failure. Auth Service deliberately uses a fixed, generic message for each class of 401 to avoid leaking information about whether an account exists, why a token was rejected, or what the actual cause of a failure was (anti-enumeration / NFR-2 / AD-8). Scenario: missing or invalid JWT (SecurityFilterChain path)
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required.",
  "instance": "/api/v1/users/me"
}
Scenario: wrong email or password
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Email o contraseña incorrectos.",
  "instance": "/auth/login"
}
Scenario: invalid or expired refresh token (including reuse detection)
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Refresh token inválido o expirado.",
  "instance": "/auth/refresh"
}
Scenario: invalid or already-used OAuth2 exchange code
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Código de intercambio inválido o expirado.",
  "instance": "/auth/oauth2/exchange"
}
Scenario: valid JWT whose account no longer exists (treated identically to missing token)
{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication required.",
  "instance": "/api/v1/users/me"
}

403 Forbidden

Returned when the request is authenticated (valid JWT present) but the account does not have sufficient privileges to access the requested resource.
{
  "type": "about:blank",
  "title": "Forbidden",
  "status": 403,
  "detail": "Access denied.",
  "instance": "/api/v1/admin/accounts"
}

404 Not Found

Returned when a requested resource cannot be found. This status is used sparingly — several endpoints that might naturally return 404 (e.g. registration with a duplicate email, resend-verification, forgot-password) intentionally return 202 Accepted instead for anti-enumeration reasons. See the note below.

500 Internal Server Error

Returned for any unhandled exception that is not explicitly mapped to a more specific status code. The detail field is a generic message — the real cause is logged server-side and never exposed to the client (AD-8).
{
  "type": "about:blank",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "Ha ocurrido un error inesperado.",
  "instance": "/auth/login"
}

Two error paths, one contract

Auth Service produces application/problem+json errors from two distinct layers, and both use the exact same format:
  • SecurityFilterChain — errors that occur before the request reaches Spring MVC (e.g. missing or invalid JWT, access denied). These are written directly to the HTTP response by the AuthenticationEntryPoint and AccessDeniedHandler configured in SecurityConfig.
  • GlobalExceptionHandler (@RestControllerAdvice) — domain and application errors that occur inside Spring MVC after the security filters pass (e.g. wrong password, invalid refresh token, domain validation failures). These are translated to ProblemDetail by the advice class.
As a client you do not need to distinguish between these two paths — the response shape is always identical.

Anti-enumeration responses

The following endpoints always return 202 Accepted with a generic success-like message body, regardless of whether the email address exists in the system or whether the operation was actually performed:
  • POST /auth/register — same response whether the email is new or already registered
  • POST /auth/resend-verification — same response whether the account exists and is pending verification or not
  • POST /auth/forgot-password — same response whether the account exists or not
This design prevents an attacker from probing which email addresses are registered by observing differences in HTTP status codes or response bodies. Errors that expose failure information (e.g. POST /auth/verify, POST /auth/reset-password) do so only when the caller already possesses a token, so there is no enumeration risk.

Build docs developers (and LLMs) love