{"record":{"id":"6c2cc58ba8c78096","repo":"nicolargo/glances","slug":"incorrect-authentication","errorCode":null,"errorMessage":"Incorrect authentication","messagePattern":"Incorrect authentication","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"glances/outputs/glances_restful_api.py","lineNumber":458,"sourceCode":"        basic_creds: Annotated[HTTPBasicCredentials | None, Depends(security)] = None,\n    ):\n        \"\"\"Check if a username/password combination or JWT token is valid.\n\n        Supports both HTTP Basic Auth and Bearer Token (JWT) authentication.\n        JWT Bearer tokens are checked first (manually from header) to avoid\n        HTTPBasic(auto_error=True) rejecting Bearer Authorization headers.\n        If no Bearer token is found, HTTPBasic handles the browser auth dialog.\n        \"\"\"\n        # Try JWT Bearer token first (manually from request header)\n        if self._jwt_handler is not None and self._jwt_handler.is_available:\n            auth_header = request.headers.get(\"Authorization\", \"\")\n            if auth_header.lower().startswith(\"bearer \"):\n                token = auth_header.split(\" \", 1)[1]\n                username = self._jwt_handler.verify_token(token)\n                if username is not None and username == self.args.username:\n                    return username\n                # Invalid Bearer token - reject immediately\n                raise HTTPException(\n                    status.HTTP_401_UNAUTHORIZED,\n                    \"Incorrect authentication\",\n                    {\"WWW-Authenticate\": \"Bearer\"},\n                )\n\n        # Fall back to Basic Auth\n        # If no credentials provided (basic_creds is None), trigger browser dialog\n        if basic_creds is None:\n            # Force HTTPBasic auto_error behavior to trigger browser auth dialog\n            raise HTTPException(\n                status.HTTP_401_UNAUTHORIZED,\n                \"Not authenticated\",\n                {\"WWW-Authenticate\": \"Basic\"},\n            )\n\n        if basic_creds.username == self.args.username:\n            if self._password.check_password(self.args.password, self._password.get_hash(basic_creds.password)):\n                return basic_creds.username","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/nicolargo/glances/blob/a240d8dfb3105a38b5964357ec21768594b0e83e/glances/outputs/glances_restful_api.py#L440-L476","documentation":"During FastAPI dependency authentication, a client sent an 'Authorization: Bearer <token>' header, but the JWT verification either failed (verify_token returned None — expired/invalid signature/token) or the verified subject doesn't equal the configured args.username. Glances rejects immediately with 401 and WWW-Authenticate: Bearer rather than falling back to Basic auth.","triggerScenarios":"curl -H 'Authorization: Bearer xxx' against a --password protected Glances REST API with an expired token, a token minted with a different secret (e.g. after regenerating the JWT secret), or a token whose sub is not the configured username.","commonSituations":"Long-running scripts holding tokens past expiry; containers recreated losing the persisted JWT secret so old tokens no longer verify; proxies injecting stale Authorization headers.","solutions":["POST to /api/4/token with username/password to obtain a fresh Bearer token and retry.","If tokens suddenly all fail, check whether the JWT secret changed (secret file/config) — tokens signed by the old secret are permanently invalid; restart clients to re-auth.","Make sure the token's username matches glances' configured --username."],"exampleFix":"# before\ncurl -H 'Authorization: Bearer <stale-token>' http://host:61208/api/4/cpu\n# 401 Incorrect authentication\n\n# after\nTOKEN=$(curl -s -X POST http://host:61208/api/4/token -d '{\"username\":\"nicolargo\",\"password\":\"pass\"}' | jq -r .access_token)\ncurl -H \"Authorization: Bearer $TOKEN\" http://host:61208/api/4/cpu","handlingStrategy":"try-catch","validationCode":"resp = requests.post(f'{base}/api/4/token', json=creds)\nif resp.status_code != 200:\n    raise SystemExit('refresh credentials')\ntoken = resp.json()['access_token']","typeGuard":null,"tryCatchPattern":"r = requests.get(url, headers={'Authorization': f'Bearer {token}'})\nif r.status_code == 401:\n    token = requests.post(f'{base}/api/4/token', json=creds).json()['access_token']\n    r = requests.get(url, headers={'Authorization': f'Bearer {token}'})","preventionTips":["Refresh tokens before expiry (expires_in from the token response).","Persist the JWT secret so container restarts don't invalidate tokens.","Handle 401 by re-authenticating, not by retrying the same token."],"tags":["authentication","jwt","http-401","rest-api"],"backgroundTag":"jwt-token-invalid","analyzedSha":"a240d8dfb3105a38b5964357ec21768594b0e83e","analyzedAt":"2026-08-27T19:15:19.178Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}