{"id":"0e5e56ac14b68581","repo":"tiangolo/fastapi","slug":"not-enough-permissions","errorCode":null,"errorMessage":"Not enough permissions","messagePattern":"Not enough permissions","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"docs_src/security/tutorial005_an_py310.py","lineNumber":135,"sourceCode":"        detail=\"Could not validate credentials\",\n        headers={\"WWW-Authenticate\": authenticate_value},\n    )\n    try:\n        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])\n        username = payload.get(\"sub\")\n        if username is None:\n            raise credentials_exception\n        scope: str = payload.get(\"scope\", \"\")\n        token_scopes = scope.split(\" \")\n        token_data = TokenData(scopes=token_scopes, username=username)\n    except (InvalidTokenError, ValidationError):\n        raise credentials_exception\n    user = get_user(fake_users_db, username=token_data.username)\n    if user is None:\n        raise credentials_exception\n    for scope in security_scopes.scopes:\n        if scope not in token_data.scopes:\n            raise HTTPException(\n                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=\"Not enough permissions\",\n                headers={\"WWW-Authenticate\": authenticate_value},\n            )\n    return user\n\n\nasync def get_current_active_user(\n    current_user: Annotated[User, Security(get_current_user, scopes=[\"me\"])],\n):\n    if current_user.disabled:\n        raise HTTPException(status_code=400, detail=\"Inactive user\")\n    return current_user\n\n\n@app.post(\"/token\")\nasync def login_for_access_token(\n    form_data: Annotated[OAuth2PasswordRequestForm, Depends()],","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/docs_src/security/tutorial005_an_py310.py#L117-L153","documentation":"In the OAuth2 scopes tutorial, `get_current_user` raises HTTP 401 'Not enough permissions' (with `WWW-Authenticate: Bearer scope=\"...\"`) when the token's scopes do not include every scope required by the route's `Security(..., scopes=[...])`. The token itself is valid and the user exists - it just lacks authorization for this endpoint.","triggerScenarios":"`GET /users/me/items/` (requires scope `items`) with a token whose `scope` claim is only `me`. The mismatch is detected in the `for scope in security_scopes.scopes:` loop at line 133.","commonSituations":"Requesting the wrong scopes at /token; frontend assuming a scope it never asked for; route's required scopes changed but client still requests the old set; scope strings differing by case/whitespace.","solutions":["Re-request a token asking for the required scope, e.g. `scope=items` at /token.","For multiple endpoints, request all needed scopes: `scope=me items`.","Confirm the route's required scopes match what the client requests."],"exampleFix":"# before\n# token requested with scope=me, then:\nGET /users/me/items/   # requires 'items' => 401\n# after\n# request scope='me items' at /token, then retry","handlingStrategy":"validation","validationCode":"def has_scopes(token_scopes, required) -> bool:\n    return all(s in token_scopes for s in required)\n# before calling /users/me/items/, ensure 'items' is in the scopes you requested","typeGuard":"def has_required_scope(token_scopes: list[str], required: list[str]) -> bool:\n    return set(required).issubset(set(token_scopes))","tryCatchPattern":"try:\n    r = client.get('/users/me/items/', headers=auth_header(token))\n    r.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 401 and e.response.json().get('detail') == 'Not enough permissions':\n        token = request_token(scopes=['me','items'])  # widen scopes, retry once\n    raise","preventionTips":["Request the union of scopes every endpoint you call requires.","Match scope strings exactly (case/whitespace) with the server config.","On 'Not enough permissions', re-login for broader scopes rather than retrying blindly."],"tags":["authorization","oauth2","scopes","security","fastapi"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}