{"record":{"id":"a5c4f0f9a8b842df","repo":"different-ai/openwork","slug":"auth-user-or-workload","errorCode":"AUTH_USER_OR_WORKLOAD","errorMessage":"Authorization response had no redirect location","messagePattern":"Authorization response had no redirect location","errorType":"exception","errorClass":"ProbeFailure","httpStatus":null,"severity":"error","filePath":"packages/enterprise-mcp-mock-server/src/testing/probe.ts","lineNumber":647,"sourceCode":"    const oauthState = randomBytes(16).toString(\"base64url\")\n    sensitiveValues.push(verifier, oauthState)\n    const redirectUri = scenario.oauth.redirectUris[0]\n    if (!redirectUri) throw new ProbeFailure(\"CONFIGURATION\", \"configuration\", \"Scenario has no redirect URI\")\n    const authorizeUrl = new URL(authorizationEndpoint)\n    authorizeUrl.searchParams.set(\"response_type\", \"code\")\n    authorizeUrl.searchParams.set(\"client_id\", clientId)\n    authorizeUrl.searchParams.set(\"redirect_uri\", redirectUri)\n    authorizeUrl.searchParams.set(\"scope\", scenario.oauth.authorizationScopes.join(\" \"))\n    authorizeUrl.searchParams.set(\"resource\", mcpUrl)\n    authorizeUrl.searchParams.set(\"state\", oauthState)\n    authorizeUrl.searchParams.set(\"code_challenge\", pkceChallengeValue)\n    authorizeUrl.searchParams.set(\"code_challenge_method\", \"S256\")\n    startedAt = Date.now()\n    const authorizeResponse = await fetchStep(authorizeUrl, { redirect: \"manual\" }, \"AUTH_USER_OR_WORKLOAD\", overallDeadline)\n    if (authorizeResponse.status !== 302) await expectOk(authorizeResponse, \"AUTH_USER_OR_WORKLOAD\")\n    else await discardResponseBody(authorizeResponse, \"AUTH_USER_OR_WORKLOAD\", \"oauth_authorization\")\n    const location = authorizeResponse.headers.get(\"location\")\n    if (!location) throw new ProbeFailure(\"AUTH_USER_OR_WORKLOAD\", \"oauth_authorization\", \"Authorization response had no redirect location\")\n    const callback = new URL(location)\n    const callbackWithoutResponse = new URL(callback)\n    callbackWithoutResponse.searchParams.delete(\"code\")\n    callbackWithoutResponse.searchParams.delete(\"state\")\n    if (callbackWithoutResponse.href !== redirectUri) {\n      throw new ProbeFailure(\"AUTH_USER_OR_WORKLOAD\", \"oauth_authorization\", \"Authorization callback did not exactly match the registered redirect URI\")\n    }\n    if (callback.searchParams.get(\"state\") !== oauthState || !callback.searchParams.get(\"code\")) {\n      throw new ProbeFailure(\"AUTH_USER_OR_WORKLOAD\", \"oauth_authorization\", \"Authorization callback state or code was invalid\")\n    }\n    sensitiveValues.push(callback.searchParams.get(\"code\") ?? \"\")\n    recordPassed(phases, \"AUTH_USER_OR_WORKLOAD\", startedAt, \"Synthetic user authorization and state binding passed\")\n\n    startedAt = Date.now()\n    const tokenForm = new URLSearchParams({\n      grant_type: \"authorization_code\",\n      client_id: clientId,\n      code: callback.searchParams.get(\"code\") ?? \"\",","sourceCodeStart":629,"sourceCodeEnd":665,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/enterprise-mcp-mock-server/src/testing/probe.ts#L629-L665","documentation":"The probe sends the authorization request with redirect: \"manual\" and expects a 302 response carrying a `location` header (the OAuth callback containing the code and state). When the response is a 302 but has no location header — or when a non-302 response passed expectOk yet lacks one — the probe cannot continue the code flow and throws AUTH_USER_OR_WORKLOAD / oauth_authorization.","triggerScenarios":"The authorization endpoint returns 302 without a Location header, or returns a success status (handled by expectOk) whose response carries no Location header, leaving `location` null at probe.ts:646-647.","commonSituations":"A misbehaving or partially-implemented mock/real authorization server; an intermediary (proxy, auth wall, HTML interstitial login page) that breaks the redirect chain; server framework defaulting to an empty redirect.","solutions":["Inspect the authorization server: ensure the /authorize endpoint issues 302 responses with a valid Location header for the request parameters the probe sends (response_type=code, PKCE S256, state, resource).","Remove any proxy or middleware between the probe and the AS that might strip the Location header or return an HTML page.","Check for an HTML login interstitial — the probe performs a synthetic user flow, so the AS must redirect without interactive login."],"exampleFix":"// before (AS handler)\nres.status(302).end()\n// after\nres.status(302).setHeader(\"location\", callbackUrl.href).end()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await probeEnterpriseMcpMockServer(scenario)\n} catch (e) {\n  if (e instanceof ProbeFailure && e.code === 'AUTH_USER_OR_WORKLOAD' && e.message.includes('no redirect location')) {\n    // inspect the AS /authorize endpoint: log full response headers/status\n    console.error('Authorization endpoint did not redirect; check AS 302 + Location behavior', e)\n  } else throw e\n}","preventionTips":["Smoke-test the AS /authorize endpoint returns 302 + Location for PKCE S256 requests.","Avoid proxies/middlewares that rewrite or strip Location headers.","Pin the AS version used by mocks and re-run the probe after upgrades."],"tags":["oauth","http-redirect","authorization-server"],"backgroundTag":"missing-redirect-location","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}