{"record":{"id":"e2ae54be9c800ef4","repo":"ruvnet/RuView","slug":"missing-or-invalid-authorization-header","errorCode":null,"errorMessage":"Missing or invalid Authorization header","messagePattern":"Missing or invalid Authorization header","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"archive/v1/src/api/routers/auth.py","lineNumber":23,"sourceCode":"\nimport logging\nfrom typing import Optional\n\nfrom fastapi import APIRouter, Request, HTTPException, status\n\nfrom src.api.middleware.auth import token_blacklist\n\nlogger = logging.getLogger(__name__)\n\nrouter = APIRouter(prefix=\"/auth\", tags=[\"auth\"])\n\n\n@router.post(\"/logout\")\nasync def logout(request: Request):\n    \"\"\"Logout by blacklisting the current Bearer token.\"\"\"\n    auth_header = request.headers.get(\"authorization\")\n    if not auth_header or not auth_header.startswith(\"Bearer \"):\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Missing or invalid Authorization header\",\n        )\n\n    token = auth_header.split(\" \", 1)[1]\n    token_blacklist.add_token(token)\n    logger.info(\"Token blacklisted via /auth/logout\")\n\n    return {\"success\": True, \"message\": \"Token revoked\"}\n","sourceCodeStart":5,"sourceCodeEnd":33,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/api/routers/auth.py#L5-L33","documentation":"Raised as HTTP 401 by POST /auth/logout (archive/v1/src/api/routers/auth.py:23) when the Authorization header is missing or does not start with the exact string 'Bearer '. The token after the prefix is what gets added to the in-memory token_blacklist, so logout is impossible without a well-formed Bearer header. Note the check is case-sensitive even though RFC 7235 makes auth schemes case-insensitive.","triggerScenarios":"POST /auth/logout with no Authorization header, with 'Authorization: Token abc' or a bare JWT, or with a lowercase 'bearer ...' value that fails the startswith('Bearer ') check.","commonSituations":"The client stores the token under a different header name; a fetch wrapper attaches the header only to JSON calls and the logout request bypasses it; a proxy strips Authorization; some HTTP clients send the scheme in lowercase.","solutions":["Send exactly 'Authorization: Bearer <token>' on the logout request","If you control the server, parse the scheme case-insensitively (see exampleFix)","If the header is set client-side but still missing server-side, check proxy/middleware configuration"],"exampleFix":"# before\nif not auth_header or not auth_header.startswith('Bearer '):\n    raise HTTPException(401, 'Missing or invalid Authorization header')\ntoken = auth_header.split(' ', 1)[1]\n\n# after\nscheme, _, token = (auth_header or '').partition(' ')\nif scheme.lower() != 'bearer' or not token.strip():\n    raise HTTPException(401, 'Missing or invalid Authorization header')\ntoken = token.strip()","handlingStrategy":"validation","validationCode":"def auth_headers(token):\n    if not token:\n        raise ValueError('not logged in')\n    return {'Authorization': f'Bearer {token}'}\n\nawait client.post('/auth/logout', headers=auth_headers(token))","typeGuard":"def is_bearer_header(h) -> bool:\n    if not isinstance(h, str):\n        return False\n    scheme, _, rest = h.partition(' ')\n    return scheme.lower() == 'bearer' and rest.strip() != ''","tryCatchPattern":"resp = await client.post('/auth/logout', headers=headers)\nif resp.status_code == 401:\n    session.clear()  # header unusable or token unknown — force clean re-login","preventionTips":["Attach the Authorization header via shared client middleware, not per call","Use the exact 'Bearer ' prefix with that casing to match the server check","On 401 from logout, drop local session state anyway — the token is not usable"],"tags":["python","fastapi","authentication","http-401","http-headers"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}