{"record":{"id":"3b2f936ffbcafc9d","repo":"ruvnet/RuView","slug":"invalid-token-3b2f93","errorCode":null,"errorMessage":"Invalid token","messagePattern":"Invalid token","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"plans/phase2-architecture/api-architecture.md","lineNumber":1586,"sourceCode":"            scopes = payload.get('scopes', [])\n            \n            if user_id is None:\n                raise HTTPException(\n                    status_code=status.HTTP_401_UNAUTHORIZED,\n                    detail=\"Invalid authentication credentials\",\n                    headers={\"WWW-Authenticate\": \"Bearer\"},\n                )\n            \n            return {'user_id': user_id, 'scopes': scopes}\n            \n        except jwt.ExpiredSignatureError:\n            raise HTTPException(\n                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=\"Token has expired\",\n                headers={\"WWW-Authenticate\": \"Bearer\"},\n            )\n        except jwt.JWTError:\n            raise HTTPException(\n                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=\"Invalid token\",\n                headers={\"WWW-Authenticate\": \"Bearer\"},\n            )\n    \n    def require_scopes(self, required_scopes: List[str]):\n        \"\"\"Decorator to require specific scopes\"\"\"\n        async def scope_checker(token_data: dict = Depends(self.verify_token)):\n            user_scopes = token_data.get('scopes', [])\n            \n            for scope in required_scopes:\n                if scope not in user_scopes:\n                    raise HTTPException(\n                        status_code=status.HTTP_403_FORBIDDEN,\n                        detail=f\"Not enough permissions. Required scope: {scope}\"\n                    )\n            \n            return token_data","sourceCodeStart":1568,"sourceCodeEnd":1604,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/plans/phase2-architecture/api-architecture.md#L1568-L1604","documentation":"HTTP 401 raised from the catch-all jwt.JWTError handler in verify_token. It means the token failed cryptographic validation outright: bad signature (secret_key or algorithm mismatch), malformed token, or a decode error -- as opposed to a missing 'sub' or an expired signature, which have their own handlers.","triggerScenarios":"Token signed with a different secret than self.secret_key; token algorithm differs from self.algorithm (e.g. token is RS256 but server expects HS256); corrupted/truncated Authorization header; a non-JWT string passed after the Bearer prefix.","commonSituations":"Secret rotated on the issuer but not the API (or vice versa); environment drift between dev/prod secrets; alg-confusion tokens from a different IdP; proxy or client mangling the header.","solutions":["Align secret_key and algorithm between the token issuer and this verifier (check env/config on both sides)","Confirm the client sends 'Authorization: Bearer <jwt>' with the full three-part token and no whitespace corruption","Re-login to get a token minted by the same authority the server trusts","If multiple issuers are legitimate, extend verify_token to try per-issuer keys/algorithms explicitly"],"exampleFix":"# before\nauth = JWTAuth(secret_key=getenv('JWT_SECRET'), algorithm='HS256')  # issuer signs RS256 elsewhere\n\n# after\nauth = JWTAuth(secret_key=getenv('JWT_SECRET'), algorithm='HS256')\n# issuer must sign with the same secret+algorithm:\ntoken = jwt.encode({'sub': user_id, 'scopes': scopes, 'exp': exp}, getenv('JWT_SECRET'), algorithm='HS256')","handlingStrategy":"try-catch","validationCode":"import jwt\ntoken = token.removeprefix('Bearer ').strip()\njwt.decode(token, secret_key, algorithms=['HS256'])  # local canary: fails fast with the same class of error","typeGuard":null,"tryCatchPattern":"try:\n    result = await call_api(token)\nexcept HTTPException as e:\n    if e.status_code == 401 and e.detail == 'Invalid token':\n        token = await login_again()  # signature/algorithm mismatch; get a token from this authority\n    else:\n        raise","preventionTips":["Keep secret_key and algorithm identical on issuer and verifier; rotate both together","Strip 'Bearer ' and whitespace before sending the Authorization header","Log (at debug) which decode stage failed -- signature vs structure -- to speed up diagnosis"],"tags":["python","fastapi","jwt","authentication","signature","http-401"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}