Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions adminapi/auth/auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func BasicAuthHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Error("Authentication Error : ", err)
http.Error(w, "Authentication Error", http.StatusUnauthorized)
return
}
// Add the cookie to the response
w.Header()[xhttp.AUTH_TOKEN] = []string{token}
Expand All @@ -129,6 +130,7 @@ func BasicAuthHandler(w http.ResponseWriter, r *http.Request) {
xwhttp.WriteXconfResponseWithHeaders(w, headers, http.StatusFound, []byte(""))
} else {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
}

Expand Down
1 change: 1 addition & 0 deletions adminapi/canary/canary_settings_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
func PutCanarySettingsHandler(w http.ResponseWriter, r *http.Request) {
if !auth.HasWritePermissionForTool(r) {
xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, "No write permission: tools")
return
}

xw, ok := w.(*xwhttp.XResponseWriter)
Expand Down
33 changes: 33 additions & 0 deletions adminapi/canary/canary_settings_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,36 @@ func TestGetCanarySettingsHandler(t *testing.T) {
GetCanarySettingsHandler(w, req)
assert.Equal(t, http.StatusUnauthorized, w.Status())
}

// TestAuthFailureTerminatesExecution verifies the fail-fast termination guarantee:
// When an auth/authz error response is written, the handler MUST return immediately
// with no subsequent logic execution (Fail-Fast Termination per auth-contract.md).
func TestAuthFailureTerminatesExecution(t *testing.T) {
originalSatOn := common.SatOn
defer func() { common.SatOn = originalSatOn }()

// Enable SAT to trigger permission check
common.SatOn = true

// Create a request without authentication credentials (will fail permission check)
req := httptest.NewRequest(http.MethodPut, testURL, nil)

// Provide invalid JSON that would normally cause http.StatusBadRequest
// if the handler continued executing past the auth failure.
// However, with fail-fast termination, the handler should return
// http.StatusForbidden from the permission check and never reach JSON parsing.
recorder := httptest.NewRecorder()
w := xwhttp.NewXResponseWriter(recorder)
w.SetBody(`{"invalid": json}`) // Malformed JSON

// Call the handler
PutCanarySettingsHandler(w, req)

// CRITICAL ASSERTION: Verify fail-fast termination
// Status should be http.StatusForbidden (from auth failure),
// NOT http.StatusBadRequest (which would occur if JSON parsing ran).
assert.Equal(t, http.StatusForbidden, w.Status(),
"Auth failure must return Forbidden and terminate immediately. "+
"If JSON parsing error (BadRequest) occurs instead, "+
"handler continued executing after auth failure, violating fail-fast guarantee.")
}
1 change: 1 addition & 0 deletions adminapi/lockdown/lockdown_settings_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
func PutLockdownSettingsHandler(w http.ResponseWriter, r *http.Request) {
if !auth.HasWritePermissionForTool(r) {
xhttp.WriteAdminErrorResponse(w, http.StatusForbidden, "No write permission: tools")
return
}

xw, ok := w.(*xhttp.XResponseWriter)
Expand Down
1 change: 1 addition & 0 deletions adminapi/queries/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ func UpdateAppSettings(w http.ResponseWriter, r *http.Request) {
// For updating app settings, tools permission is required
if !auth.HasWritePermissionForTool(r) {
xhttp.WriteAdminErrorResponse(w, http.StatusUnauthorized, "")
return
}

// r.Body is already drained in the middleware
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
schema: spec-driven
created: 2026-04-23
status: accepted
accepted: 2026-04-27
applies_to:
- openspec/specs/auth/auth-contract.md
35 changes: 35 additions & 0 deletions openspec/changes/enforce-auth-failure-termination/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Status: Accepted
Applied to: openspec/specs/auth/auth-contract.md

## Why

Authentication and authorization failures currently rely on handler authors to stop execution after an authentication or authorization failure is produced, which is easy to miss and can allow unintended logic to continue. We need an explicit contract that fail-fast termination is mandatory whenever an auth/authz failure is produced.

## What Changes

- Clarify the auth contract to require that request processing terminates immediately after an authentication or authorization failure is produced.
- Define this behavior as a normative guarantee so downstream handlers and middleware can rely on no post-failure side effects.
- Align xconfadmin auth behavior documentation with this fail-fast requirement so future implementations and refactors preserve the same safety property.

## Non‑Goals

- This change does not alter authentication mechanisms, token types,
permission models, or authorization semantics.
- This change does not introduce new authentication or authorization
capabilities.
- This change does not modify request/response formats or API contracts.

## Capabilities

### New Capabilities
- None.

### Modified Capabilities
- `auth`: Add explicit fail-fast requirements that handler execution MUST NOT continue after authentication or authorization failures are identified.

## Impact

- Affected specs: `openspec/specs/auth/auth-contract.md`.
- Affected implementation areas (future apply phase): auth middleware and handlers that write 401/403/related auth error responses.
- API behavior impact: no new endpoints; clarifies control-flow guarantees for existing auth/authz failure paths.
- System impact: improves safety by preventing accidental post-failure processing and side effects.
20 changes: 20 additions & 0 deletions openspec/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
schema: spec-driven

# Project context (optional)
# This is shown to AI when creating artifacts.
# Add your tech stack, conventions, style guides, domain knowledge, etc.
# Example:
# context: |
# Tech stack: TypeScript, React, Node.js
# We use conventional commits
# Domain: e-commerce platform

# Per-artifact rules (optional)
# Add custom rules for specific artifacts.
# Example:
# rules:
# proposal:
# - Keep proposals under 500 words
# - Always include a "Non-goals" section
# tasks:
# - Break tasks into chunks of max 2 hours
52 changes: 52 additions & 0 deletions openspec/specs/auth/auth-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Authentication Contract (xconfadmin)

## Purpose
This document defines the authentication behavior provided by
xconfadmin as a standalone library.

## Scope
This specification describes:
- credential validation
- identity resolution
- authentication success and failure outcomes
- fail-fast termination after authentication or authorization failure

This specification does not describe:
- business-specific policy enforcement
- application-level authorization
- downstream extensions or constraints

## Guarantees

### Credential Validation
The system SHALL validate supplied credentials and determine
their validity deterministically.

### Authentication Result
On successful authentication, the system SHALL return an
identity representation suitable for downstream use.

### Failure Modes
Authentication failures SHALL result in defined error categories.
Failure handling is subject to the Fail-Fast Termination guarantee
defined below.


### Fail-Fast Termination

After an authentication failure produced by this system, or
an authorization failure surfaced through this system,
request processing MUST terminate immediately.

No downstream handler logic, middleware continuation, or
post-failure side effects SHALL occur after such a failure.

This contract does not define authorization semantics; it specifies
fail-fast termination behavior when such failures are emitted
through this authentication boundary.


## Extension Notice
Downstream systems (including xconfas) may impose additional
authentication or authorization constraints beyond this contract.
Those constraints are explicitly outside the scope of this specification.
Loading