Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPBearer security scheme is returning 403 instead or 401 #2026

Closed
aaaaahaaaaa opened this issue Sep 8, 2020 · 11 comments
Closed

HTTPBearer security scheme is returning 403 instead or 401 #2026

aaaaahaaaaa opened this issue Sep 8, 2020 · 11 comments

Comments

@aaaaahaaaaa
Copy link

HTTPBearer security scheme enabled as a dependency is returning a 403 when a request is unauthenticated because of a missing or a malformed authorization header. In those scenarios, a 401 should be returned instead.

@aaaaahaaaaa aaaaahaaaaa added the question Question or problem label Sep 8, 2020
@ArcLightSlavik
Copy link
Contributor

Could you provide an example of how you are getting that?
Docs say:

If it doesn't see an Authorization header, or the value doesn't have a Bearer token, it will respond with a 401 status code error (UNAUTHORIZED) directly.

@raphaelauv
Copy link
Contributor

raphaelauv commented Sep 9, 2020

@aaaaahaaaaa
Copy link
Author

aaaaahaaaaa commented Sep 10, 2020

@raphaelauv @ArcLightSlavik
I believe the status code is coming from here: https://github.com/tiangolo/fastapi/blob/55b9faeb48f9c8676cd56adc8f8d75040e3f1010/fastapi/security/http.py#L114

My setup is the following:

...
from fastapi.security import HTTPBearer

security = HTTPBearer()

@app.get("/test")
async def test(bearer_token = Depends(security)):
    ...

Then if you don't provide a bearer token via the authorization header, this produces a 403 by default with the error message "Not authenticated". Again in that scenario, I believe it should be a 401.

@aaaaahaaaaa
Copy link
Author

To be clear here: I only want to support Authorization header to do the JWT verification. I don't want to support the full OAuth2 flows which is why I'm not using the OAuth2PasswordBearer scheme.

@raphaelauv
Copy link
Contributor

@aaaaahaaaaa You could open a PR to correct this

@OYE93
Copy link

OYE93 commented Oct 29, 2020

For API_KEY when the authorization is missing then it's a 403

https://github.com/tiangolo/fastapi/blob/e77ea635777d2494690ba3eb62bd005b9edeefde/fastapi/security/api_key.py#L27

but for bearer it's a 401 if it's missing

https://github.com/tiangolo/fastapi/blob/e77ea635777d2494690ba3eb62bd005b9edeefde/fastapi/security/oauth2.py#L158

I'd like to know why for API_KEY, the error should be 403, but 401 for bearer, thanks

@dorinclisu
Copy link

It makes total sense to have 401 returned, I'm sure tiangolo did not mean 403 and it was just a small mishap

@iantimmis
Copy link

This is still not fixed

@mglickVA
Copy link

mglickVA commented Jul 9, 2022

@tiangolo is this something that was intentionally implemented this way?

Tests are checking for this as well, but my understanding is that a 401 is the appropriate response.

@icvorovic
Copy link

There is a workaround solution:

  • pass auto_error=False when you are creating HTTPBearer object
  • In this way, created HTTPBearer object will not raise an HTTPException with status code 403. It will return None within its __call__ method, therefore you will check is returned value equal to None and raise the appropriate HTTPException:
security = HTTPBearer(auto_error=False)

@app.get("/test")
async def test(bearer_token = Depends(security)):
   if not bearer_token:
      raise HTTPException(
          status_code=status.HTTP_401_UNAUTHORIZED,
          detail="Missing bearer token",
      )

@potens1
Copy link

potens1 commented Oct 16, 2022

I think this should be qualified as bug, not question, since, from MDN:

The HyperText Transfer Protocol (HTTP) 401 Unauthorized response status code indicates that the client request has not been completed because it lacks valid authentication credentials for the requested resource.

and

The HTTP 403 Forbidden response status code indicates that the server understands the request but refuses to authorize it.
This status is similar to 401, but for the 403 Forbidden status code re-authenticating makes no difference. The access is permanently forbidden and tied to the application logic, such as insufficient rights to a resource.

In this case, could (re-)authenticating permit access to the resource ? Definitively yes (As I understand, 403 is: try as much as you want, you will never have access), I tend to think 401 is authentication, 403 is permissions.

Why it does matter ? Normally, when client receive 401, either the credentials are wrong, either the app is not logged anymore (i.e session timeout), so the normal process is to go to login page again and retry. On 403, there is no need to go there again since it is already authenticated (or, login again but with different credentials).

My solution (that I find hideous) is to do this:

from typing import Optional
from fastapi.security import APIKeyHeader as ApiKeyHeader403
from starlette.exceptions import HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED
from starlette.requests import Request



class APIKeyHeader(ApiKeyHeader403):
    async def __call__(self, request: Request) -> Optional[str]:
        try:
            api_key = await super().__call__(request)
        except HTTPException as exception:
            raise HTTPException(
                    status_code=HTTP_401_UNAUTHORIZED, detail=exception.detail
                )
        return api_key

And then use the APIKeyHeader as before.

PS: This should not be necessary, or I miss the point, but since the only check done is on the header presence, this should be corrected upstream with 401.

It works because right now, the only exception on APIKeyHeader is when the header is missing, but if someday fastapi implement permissions, I'm not sure it will still be valid.

@fastapi fastapi locked and limited conversation to collaborators Feb 28, 2023
@tiangolo tiangolo converted this issue into discussion #9130 Feb 28, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

10 participants