# Headless Login Debug and Error Classification Design

## Scope

This design covers issue `#502` only.

- Target endpoint: `POST /api/v1/auth/headless/password/login`
- Goal: classify 401 failure reasons for headless password login, return a safe response to RP clients, and emit richer diagnostics only when debug logging is enabled
- Non-goal: introduce a shared auth-wide error/observability layer across all handlers. That follow-up work is tracked separately in issue `#503`.

## Problem

Current behavior makes production debugging slow.

- Multiple assertion failures collapse into `invalid_client_assertion`
- The handler often returns early without emitting a reason-specific log line
- RP clients cannot distinguish safe failure categories such as audience mismatch vs signature mismatch
- Server operators cannot quickly tell whether the failure came from client assertion validation or user credential validation

## Constraints

- Response policy must follow the agreed level `2`: return `code + short safe message`
- Debug logs may include level `3`-style diagnostics, but only when the logger is configured for debug level
- Sensitive values must never be logged: raw `client_assertion`, password, session token, cookies
- Existing successful headless password login flow must remain unchanged
- Existing policy requires failing tests first

## Recommended Approach

Introduce a small headless-password-specific failure classification layer inside `auth_handler.go`.

The classification layer should map each failure into:

- `status`
- `code`
- `safeMessage`
- `logMessage`
- `debugFields`

This stays local to the current handler so the change remains small, but the structure should be reusable enough to extract later in issue `#503`.

## Failure Categories

### Client assertion failures

- `invalid_client_assertion_parse`
- `invalid_client_assertion_signature`
- `invalid_client_assertion_iss_sub`
- `invalid_client_assertion_expired`
- `invalid_client_assertion_not_before`
- `invalid_client_assertion_iat_future`
- `invalid_client_assertion_audience`
- `invalid_client_assertion_jwks_load`

### Credential failure

- `password_or_email_mismatch`

## Response Contract

Responses should expose only safe details.

Examples:

- `{"code":"invalid_client_assertion_audience","error":"Client assertion audience mismatch"}`
- `{"code":"invalid_client_assertion_signature","error":"Client assertion signature verification failed"}`
- `{"code":"password_or_email_mismatch","error":"Invalid credentials"}`

The response must not echo:

- expected vs received audience values
- full claim payload
- kid mismatch internals beyond the safe message
- raw token values

## Logging Strategy

### Normal log level

Emit one structured warning/error log per classified failure with:

- `reason_code`
- `client_id`
- `path`
- `req_id` if present in context/log pipeline

Message examples:

- `headless password login client assertion failed`
- `headless password login credential authentication failed`

### Debug log level

Add diagnostic fields that are useful for root cause analysis:

- `expected_audiences`
- `received_audiences`
- `received_kid`
- `jwks_refreshed`
- `claim_issuer`
- `claim_subject`
- `claim_expires_at`
- `claim_not_before`
- `claim_issued_at`
- `login_challenge_prefix`

The `login_challenge` should be truncated before logging.

## Implementation Shape

Inside `backend/internal/handler/auth_handler.go`:

1. Add a small internal type for classified headless assertion errors.
2. Make `validateHeadlessClientAssertionClaims` return structured reasons instead of generic strings.
3. Make `verifyHeadlessClientAssertion` return a classified failure that the handler can both:
   - convert to a safe HTTP response
   - log with optional debug fields
4. Add a small helper that checks whether debug logging is enabled for the current default `slog` logger.
5. Log credential authentication failures with their reason code as well.

## Testing

Add failing tests first in `backend/internal/handler/auth_handler_login_test.go`.

Required tests:

- audience mismatch returns `401 + invalid_client_assertion_audience`
- signature mismatch returns `401 + invalid_client_assertion_signature`
- iss/sub mismatch returns `401 + invalid_client_assertion_iss_sub`
- expired assertion returns `401 + invalid_client_assertion_expired`
- invalid credentials still returns `401 + password_or_email_mismatch`
- debug logger captures diagnostic fields for assertion audience mismatch
- non-debug logger does not emit debug-only fields

## Risks

- Over-classifying too much detail into RP responses could create unnecessary information disclosure
- Logging helper implementation must not assume a specific handler type beyond standard `slog`
- Existing tests may assume the older generic `invalid_client_assertion` code and will need intentional updates

## Success Criteria

- Operators can distinguish signature, claims, and credential failures from logs
- RP clients get safe but actionable reason codes
- Debug mode includes additional diagnostics without leaking secrets
- Existing success path and previously covered headless login behavior remain green
