- Created detailed QA testing report documenting the authentication issues with certificate endpoints, including test results and root cause analysis. - Added final QA report confirming successful resolution of the authentication issue, with all tests passing and security verifications completed. - Included test output logs before and after the fix to illustrate the changes in endpoint behavior. - Documented the necessary code changes made to the route registration in `routes.go` to ensure proper application of authentication middleware.
359 lines
10 KiB
Markdown
359 lines
10 KiB
Markdown
# QA Testing Report: Authentication Fixes for Certificates Page
|
||
**Date:** December 6, 2025
|
||
**Tester:** QA Testing Agent
|
||
**Testing Environment:**
|
||
- Backend: Docker container (charon-debug) at localhost:8080
|
||
- Frontend: Production build served by backend
|
||
- Testing Tool: curl with cookie file
|
||
- Browser: Manual verification in Chrome/Chromium with DevTools
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
**Status:** ❌ **CRITICAL BUG FOUND**
|
||
|
||
### Fixes Under Test:
|
||
1. ✅ **Backend Fix**: Removed incorrect user context checks in `certificate_handler.go` (List, Upload, Delete methods) - **ALREADY APPLIED**
|
||
2. ✅ **Frontend Fix**: Added `withCredentials: true` to axios client in `client.ts` - **ALREADY APPLIED**
|
||
|
||
### Critical Issue Discovered:
|
||
🚨 **Certificate routes are NOT protected by authentication middleware!**
|
||
|
||
The certificate endpoints (`/api/v1/certificates`) are registered OUTSIDE the protected group's closing brace in `routes.go`, meaning they bypass the `AuthMiddleware` entirely. This causes all requests to these endpoints to return 401 Unauthorized, even with valid authentication cookies.
|
||
|
||
---
|
||
|
||
## Phase 1: Certificate Page Authentication Tests
|
||
|
||
### Test 1.1: Login and Cookie Verification
|
||
**Status:** ✅ **PASS**
|
||
|
||
**Steps:**
|
||
1. Register test user via API
|
||
2. Login with test credentials
|
||
3. Inspect cookie file
|
||
4. Verify cookie attributes
|
||
|
||
**Expected Results:**
|
||
- User logs in successfully
|
||
- `auth_token` cookie is present
|
||
- Cookie has HttpOnly, Secure (if HTTPS), SameSite=Strict flags
|
||
- Cookie expiration is 24 hours
|
||
|
||
**Actual Results:**
|
||
- ✅ Login successful (HTTP 200)
|
||
- ✅ `auth_token` cookie created
|
||
- ✅ Cookie details: `#HttpOnly_localhost FALSE / FALSE 1765079377 auth_token eyJhbGc...`
|
||
- ⚠️ HttpOnly flag confirmed in cookie file
|
||
- ℹ️ Secure and SameSite flags need manual verification in browser DevTools (curl doesn't show these)
|
||
|
||
---
|
||
|
||
### Test 1.2: Certificate List (GET /api/v1/certificates)
|
||
**Status:** ❌ **FAIL**
|
||
|
||
**Steps:**
|
||
1. Send GET request to `/api/v1/certificates` with auth cookie
|
||
2. Verify request includes Cookie header
|
||
3. Check response status and body
|
||
|
||
**Expected Results:**
|
||
- Page loads without error
|
||
- GET request includes `Cookie: auth_token=...`
|
||
- Response status: 200 OK (not 401)
|
||
- Certificates are displayed as JSON array
|
||
|
||
**Actual Results:**
|
||
- ✅ Request DOES include `Cookie: auth_token=...` (verified with `-v` flag)
|
||
- ❌ Response status: **401 Unauthorized**
|
||
- ❌ Response body: `{"error":"unauthorized"}`
|
||
- ❌ Certificates are NOT returned
|
||
|
||
**Root Cause Analysis:**
|
||
Using verbose curl, confirmed that the cookie IS being transmitted:
|
||
```
|
||
> Cookie: auth_token=eyJhbGci...
|
||
< HTTP/1.1 401 Unauthorized
|
||
{"error":"unauthorized"}
|
||
```
|
||
|
||
The cookie is valid (works for `/api/v1/auth/me`, `/api/v1/proxy-hosts`, etc.), but `/api/v1/certificates` specifically returns 401.
|
||
|
||
Investigation revealed that certificate routes in `routes.go` are registered OUTSIDE the protected group's closing brace (line 301), so they never receive the `AuthMiddleware`.
|
||
|
||
---
|
||
|
||
### Test 1.3: Certificate Upload (POST /api/v1/certificates)
|
||
**Status:** ⏳ PENDING
|
||
|
||
**Expected Results:**
|
||
- Upload request includes auth cookie
|
||
- Response status: 201 Created
|
||
- Certificate appears in list after upload
|
||
|
||
**Actual Results:**
|
||
_Pending test execution..._
|
||
|
||
---
|
||
|
||
### Test 1.4: Certificate Delete (DELETE /api/v1/certificates/:id)
|
||
**Status:** ⏳ PENDING
|
||
|
||
**Expected Results:**
|
||
- Delete request includes auth cookie
|
||
- Response status: 200 OK
|
||
- Certificate is removed from list
|
||
- Test error case: delete certificate in use (409 Conflict)
|
||
|
||
**Actual Results:**
|
||
_Pending test execution..._
|
||
|
||
---
|
||
|
||
### Test 1.5: Unauthorized Access
|
||
**Status:** ⏳ PENDING
|
||
|
||
**Expected Results:**
|
||
- Direct access to /certificates redirects to login
|
||
- API calls without auth return 401
|
||
|
||
**Actual Results:**
|
||
_Pending test execution..._
|
||
|
||
---
|
||
|
||
## Phase 2: Regression Testing Other Endpoints
|
||
|
||
### Test 2.1: Proxy Hosts Page
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 2.2: Backups Page
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 2.3: Settings Page
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 2.4: User Management
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 2.5: Other Protected Routes
|
||
**Status:** ⏳ PENDING
|
||
|
||
---
|
||
|
||
## Phase 3: Edge Cases and Error Handling
|
||
|
||
### Test 3.1: Token Expiration
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 3.2: Concurrent Requests
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 3.3: Network Errors
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 3.4: Browser Compatibility
|
||
**Status:** ⏳ PENDING
|
||
|
||
---
|
||
|
||
## Phase 4: Development vs Production Testing
|
||
|
||
### Test 4.1: Development Mode
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 4.2: Production Build
|
||
**Status:** ⏳ PENDING
|
||
|
||
---
|
||
|
||
## Phase 5: Security Verification
|
||
|
||
### Test 5.1: Cookie Security
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 5.2: XSS Protection
|
||
**Status:** ⏳ PENDING
|
||
|
||
### Test 5.3: CSRF Protection
|
||
**Status:** ⏳ PENDING
|
||
|
||
---
|
||
|
||
## 🔍 Root Cause Analysis
|
||
|
||
### The Bug
|
||
Certificate routes (`GET /POST /DELETE /api/v1/certificates`) are returning 401 Unauthorized even with valid authentication cookies.
|
||
|
||
### Investigation Path
|
||
1. **Verified frontend fix**: `withCredentials: true` is present in `client.ts` ✅
|
||
2. **Verified backend handler**: No user context checks in `certificate_handler.go` ✅
|
||
3. **Tested cookie transmission**: Cookies ARE being sent in requests ✅
|
||
4. **Tested token validity**: Same token works for other endpoints (`/auth/me`, `/proxy-hosts`, `/backups`) ✅
|
||
5. **Checked middleware order**: Cerberus → Auth → Handler (correct) ✅
|
||
6. **Examined route registration**: **FOUND THE BUG** ❌
|
||
|
||
### The Problem
|
||
In [routes.go](file:///projects/Charon/backend/internal/api/routes/routes.go), lines 134-301 define the `protected` group:
|
||
|
||
```go
|
||
protected := api.Group("/")
|
||
protected.Use(authMiddleware)
|
||
{
|
||
// Many protected routes...
|
||
// ...
|
||
} // Line 301 - CLOSING BRACE
|
||
```
|
||
|
||
**BUT** the certificate routes are registered AFTER this closing brace:
|
||
|
||
```go
|
||
// Line 305-310: Access Lists (also affected!)
|
||
protected.GET("/access-lists/templates", ...)
|
||
protected.GET("/access-lists", ...)
|
||
// ...
|
||
|
||
// Line 318-320: Certificates (BUG!)
|
||
protected.GET("/certificates", certHandler.List)
|
||
protected.POST("/certificates", certHandler.Upload)
|
||
protected.DELETE("/certificates/:id", certHandler.Delete)
|
||
```
|
||
|
||
While these use the `protected` variable, they're added AFTER the `Use(authMiddleware)` block closes, so **they don't actually get the middleware applied**.
|
||
|
||
### Why Other Endpoints Work
|
||
- `/api/v1/proxy-hosts`: Uses `RegisterRoutes(api)` which applies its own auth checks
|
||
- `/api/v1/backups`: Registered INSIDE the protected block (line 142-146)
|
||
- `/api/v1/settings`: Registered INSIDE the protected block (line 155-162)
|
||
- `/api/v1/auth/me`: Registered INSIDE the protected block (line 138)
|
||
|
||
### The Fix
|
||
Move certificate routes (and access-lists routes) INSIDE the protected block, before line 301.
|
||
|
||
---
|
||
|
||
## 📊 Test Results Summary
|
||
|
||
### Automated Test Results
|
||
```
|
||
Total Tests: 13
|
||
✅ Passed: 7
|
||
❌ Failed: 2
|
||
⚠️ Warnings: 1
|
||
⏭️ Skipped: 1
|
||
```
|
||
|
||
### Failing Tests
|
||
1. **Certificate List (GET)** - 401 Unauthorized (should be 200 OK)
|
||
2. **Certificate Upload (POST)** - 401 Unauthorized (should be 201 Created)
|
||
|
||
### Passing Tests
|
||
1. ✅ Login successful
|
||
2. ✅ auth_token cookie created
|
||
3. ✅ Cookie transmitted in requests
|
||
4. ✅ Unauthorized access properly rejected (without cookie)
|
||
5. ✅ Proxy Hosts endpoint works
|
||
6. ✅ Backups endpoint works
|
||
7. ✅ Settings endpoint works
|
||
|
||
### Warnings
|
||
1. ⚠️ Users endpoint returns 403 Forbidden (expected for non-admin user)
|
||
|
||
---
|
||
|
||
## 🎯 Overall Assessment
|
||
|
||
### Status: ❌ **FAIL**
|
||
|
||
The authentication fixes that were supposedly implemented are actually correct:
|
||
- ✅ Frontend `withCredentials: true` is in place
|
||
- ✅ Backend handler has no blocking user context checks
|
||
- ✅ Cookies are being transmitted correctly
|
||
|
||
**However**, a separate architectural bug in route registration prevents the certificate endpoints from receiving authentication middleware, causing them to always return 401 Unauthorized regardless of authentication status.
|
||
|
||
---
|
||
|
||
## 🔧 Required Fix
|
||
|
||
**File**: `backend/internal/api/routes/routes.go`
|
||
|
||
**Change**: Move lines 305-320 (Access Lists and Certificate routes) INSIDE the protected block before line 301.
|
||
|
||
**Before:**
|
||
```go
|
||
protected := api.Group("/")
|
||
protected.Use(authMiddleware)
|
||
{
|
||
// ... many routes ...
|
||
} // Line 301
|
||
|
||
// Access Lists
|
||
protected.GET("/access-lists/templates", ...)
|
||
// ...
|
||
|
||
// Certificate routes
|
||
protected.GET("/certificates", certHandler.List)
|
||
protected.POST("/certificates", certHandler.Upload)
|
||
protected.DELETE("/certificates/:id", certHandler.Delete)
|
||
```
|
||
|
||
**After:**
|
||
```go
|
||
protected := api.Group("/")
|
||
protected.Use(authMiddleware)
|
||
{
|
||
// ... many routes ...
|
||
|
||
// Access Lists
|
||
protected.GET("/access-lists/templates", ...)
|
||
// ...
|
||
|
||
// Certificate routes
|
||
protected.GET("/certificates", certHandler.List)
|
||
protected.POST("/certificates", certHandler.Upload)
|
||
protected.DELETE("/certificates/:id", certHandler.Delete)
|
||
} // Closing brace AFTER all protected routes
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 Re-Test Plan
|
||
|
||
After implementing the fix:
|
||
1. Rebuild Docker container
|
||
2. Re-run automated test script
|
||
3. Verify Certificate List returns 200 OK
|
||
4. Verify Certificate Upload returns 201 Created
|
||
5. Verify Certificate Delete returns 200 OK
|
||
6. Manually test in browser UI
|
||
|
||
---
|
||
|
||
## Test Execution Log
|
||
|
||
**Test Script**: `/projects/Charon/scripts/qa-test-auth-certificates.sh`
|
||
**Test Output**: `/projects/Charon/test-results/qa-auth-test-results.log`
|
||
**Execution Time**: December 6, 2025 22:49:36 - 22:49:52 (16 seconds)
|
||
|
||
### Key Log Entries
|
||
```
|
||
[PASS] Login successful (HTTP 200)
|
||
[PASS] auth_token cookie created
|
||
[PASS] Request includes auth_token cookie
|
||
[FAIL] Authentication failed - 401 Unauthorized (Cookie not being sent or not valid)
|
||
[FAIL] Upload authentication failed - 401 Unauthorized (Cookie not being sent)
|
||
[PASS] Unauthorized access properly rejected (HTTP 401)
|
||
[PASS] Proxy hosts list successful (HTTP 200)
|
||
[PASS] Backups list successful (HTTP 200)
|
||
[PASS] Settings list successful (HTTP 200)
|
||
```
|
||
|
||
### Container Log Evidence
|
||
```
|
||
[GIN] 2025/12/05 - 22:49:37 | 401 | 356.941µs | 172.18.0.1 | GET "/api/v1/certificates"
|
||
[GIN] 2025/12/05 - 22:49:37 | 401 | 387.132µs | 172.18.0.1 | POST "/api/v1/certificates"
|
||
```
|
||
|
||
---
|