fix(frontend): Propagate API auth errors to original requestor (#10716)

- Resolves #10713

### Changes 🏗️

- Remove early exit in API proxy that suppresses auth errors
- Remove unused `proxy-action.ts`

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Publish Agent dialog works when logged out
  - [x] Publish Agent dialog works when logged in

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
This commit is contained in:
Reinier van der Leer
2025-08-25 20:02:41 +01:00
committed by GitHub
parent bc43d05cac
commit da585a34e1
5 changed files with 27 additions and 64 deletions

View File

@@ -10,8 +10,8 @@ from starlette.status import HTTP_401_UNAUTHORIZED
from .config import settings
from .jwt_utils import parse_jwt_token
security = HTTPBearer()
logger = logging.getLogger(__name__)
bearer_auth = HTTPBearer(auto_error=False)
async def auth_middleware(request: Request):
@@ -20,11 +20,10 @@ async def auth_middleware(request: Request):
logger.warning("Auth disabled")
return {}
security = HTTPBearer()
credentials = await security(request)
credentials = await bearer_auth(request)
if not credentials:
raise HTTPException(status_code=401, detail="Authorization header is missing")
raise HTTPException(status_code=401, detail="Not authenticated")
try:
payload = parse_jwt_token(credentials.credentials)

View File

@@ -98,8 +98,23 @@ function createResponse(
}
}
function createErrorResponse(error: unknown): NextResponse {
console.error("API proxy error:", error);
function createErrorResponse(
error: unknown,
path: string,
method: string,
): NextResponse {
if (
error &&
typeof error === "object" &&
"status" in error &&
[401, 403].includes(error.status as number)
) {
// Log this since it indicates a potential frontend bug
console.warn(
`Authentication error in API proxy for ${method} ${path}:`,
"message" in error ? error.message : error,
);
}
// If it's our custom ApiError, preserve the original status and response
if (error instanceof ApiError) {
@@ -147,7 +162,6 @@ async function handler(
const contentType = req.headers.get("Content-Type");
let responseBody: any;
const responseStatus: number = 200;
const responseHeaders: Record<string, string> = {
"Content-Type": "application/json",
};
@@ -166,9 +180,13 @@ async function handler(
return createUnsupportedContentTypeResponse(contentType);
}
return createResponse(responseBody, responseStatus, responseHeaders);
return createResponse(responseBody, 200, responseHeaders);
} catch (error) {
return createErrorResponse(error);
return createErrorResponse(
error,
path.map((s) => `/${s}`).join(""),
method,
);
}
}

View File

@@ -34,7 +34,7 @@ export function useAgentSelectStep({
const agents: Agent[] =
(myAgents?.status === 200 &&
myAgents.data?.agents
myAgents.data.agents
.map(
(agent): Agent => ({
name: agent.agent_name,

View File

@@ -261,13 +261,7 @@ export async function makeAuthenticatedRequest(
"Authentication request failed during logout, ignoring:",
errorDetail,
);
return null;
}
// For authentication errors outside logout, log but don't throw
// This prevents crashes when session expires naturally
console.warn("Authentication failed:", errorDetail);
return null;
}
// For other errors, throw ApiError with proper status code

View File

@@ -1,48 +0,0 @@
"use server";
import * as Sentry from "@sentry/nextjs";
import {
buildRequestUrl,
makeAuthenticatedFileUpload,
makeAuthenticatedRequest,
} from "./helpers";
import { getAgptServerApiUrl } from "@/lib/env-config";
export interface ProxyRequestOptions {
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
path: string;
payload?: Record<string, any>;
contentType?: string;
}
export async function proxyApiRequest({
method,
path,
payload,
contentType = "application/json",
}: ProxyRequestOptions) {
return await Sentry.withServerActionInstrumentation(
"proxyApiRequest",
{},
async () => {
const baseUrl = getAgptServerApiUrl();
const url = buildRequestUrl(baseUrl, path, method, payload);
return makeAuthenticatedRequest(method, url, payload, contentType);
},
);
}
export async function proxyFileUpload(
path: string,
formData: FormData,
): Promise<string> {
return await Sentry.withServerActionInstrumentation(
"proxyFileUpload",
{},
async () => {
const baseUrl = getAgptServerApiUrl();
const url = baseUrl + path;
return makeAuthenticatedFileUpload(url, formData);
},
);
}