OAuth2PasswordBearer only extracts a bearer token from the Authorization header and returns 401 if it is missing. It does not validate the token, and its tokenUrl argument does not create a login endpoint. You must write validation and the token route yourself. A scan will test whether you did.
What the tutorial says
FastAPI's security tutorial explains that OAuth2PasswordBearer implements the OAuth2 password flow with bearer tokens. You create an instance with a tokenUrl and pass it to Depends. The dependency provides a string with the token. It only looks at the Authorization header, expects a value of the form Bearer followed by the token, and responds with 401 Unauthorized before your function runs if the header is absent or not a bearer token.
The tutorial is clear that at this stage it does not validate the token. It calls this "a start already".
What tokenUrl is and is not
| Question | Answer |
|---|---|
| Does tokenUrl create the endpoint? | No, you write the path operation |
| Where does it show up? | In the OpenAPI description and the interactive docs |
| Is it relative? | Yes if given as token, equivalent to dot slash token, so it works behind a proxy |
| Does Swagger UI use it? | Yes, the Authorize button sends username and password to it |
The Authorize form needs the python-multipart package because credentials go as form data.
The gap a scan finds
Because the dependency only checks that something shaped like a bearer token is present, an implementation that stops there accepts any string. A scan that sends a made up token and gets 200 has found exactly that.
Your dependency should, at minimum:
- Verify the signature using your key or secret and the expected algorithm.
- Check expiry and other claims you rely on.
- Load the user and confirm they are active.
- Return a clear 401 on failure, with no detail about why.
Our OAuth2 and JWT mistakes in FastAPI post covers pitfalls in this step.
Steps to test your own app
- Call a protected route with no header. Expect 401.
- Call it with the header Bearer and a random string. Expect 401. A 200 means validation is missing.
- Call it with an expired token from your own issuer. Expect 401.
- Call it with a valid token for a different user against someone else's record. Expect a refusal, which tests ownership rather than authentication. See where the BOLA check lives.
- Make sure the token endpoint is rate limited and does not reveal whether a username exists.
- Put these checks in automated tests and run an authenticated scan against staging.
Applying it everywhere
Attach the validating dependency at app or router level so no route is left behind. The global dependencies post shows how.
Try it yourself
Click through it at your own pace, or open it full screen. Look for tests that send invalid credentials.
Safeguard's SBOM and dependency scanning covers FastAPI and JWT libraries in your project, and a free scan is available.
Quick check
Does Depends(oauth2_scheme) mean the user is authenticated?
No. It means a bearer token string was present. Validate it in your own code.
Why do I need python-multipart?
The Swagger UI Authorize form sends credentials as form data, which requires it. It comes with fastapi standard installs.
Sources
Last reviewed: 19 October 2026.