securitycookiesfastapihttp-status-code-404api-key

How to return a custom error response when using APIKeyCookie in FastAPI?


I have a POST route /publish with a dependency get_current_user. get_current_user has as fastapi.security.APIKeyCookie as dependency.

If authenticated correctly everything is fine but if I do a curl without the cookie I get 404 not found and get_current_user is not evoked.

Why I don't get 401 if the cookie is missing? I also tried setting auto_error=False in APIKeyCookie but even then get_current_user was not evoked.

Is this the expected behavior?

EDIT: Added example

in auth.py

cookie_scheme = fastapi.security.APIKeyCookie(name="access_token_cookie", auto_error=False)
JWT_ALGORITHM = "HS256"


class UserAuthorization(pydantic.BaseModel):
    username: str
    groups: list[str]


async def get_current_user(
    token: t.Annotated[str | None, fastapi.Depends(cookie_scheme)],
) -> UserAuthorization:
    logger.info("inside get_current_user()") # this is never executed

    

in main.py

@app.post(
    "/publish",
    status_code=fastapi.status.HTTP_201_CREATED,
    dependencies=[fastapi.Depends(auth.get_current_user)],
)
...

How I test it:

 $ curl -X POST myserver/publish
 <!doctype html>
 <html lang=en>
 <title>404 Not Found</title>
 <h1>Not Found</h1>
 <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>

Testing with invalid cookie value:

$ curl -X POST myserver/publish -b "access_token_cookie=someWrongValue"
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="myserver/publish">myserver/publish</a>. If not, click the link.

Solution

  • One way to achieve this is to set the auto_error flag to False in APIKeyCookie, and hence, return your own custom error response. The following example is inspired by a solution to a similar problem, as this is demonstrated in this answer. Hence, please refer to that answer for more details, as well as alternative approaches.

    Working Example

    main.py

    from fastapi import FastAPI, Request, Security, Depends, HTTPException
    from fastapi.security.api_key import APIKeyCookie
    
    
    # List of valid API keys
    API_KEYS = [
        'z77xQYZWROmI4fY4',
        'FXhO4i3bLA1WIsvR'
    ]
    api_key_cookie = APIKeyCookie(name="session", auto_error=False)
    
    
    async def check_api_key(request: Request, api_key: str = Security(api_key_cookie)):
        if api_key not in API_KEYS:
            raise HTTPException(status_code=401, detail='Invalid or missing API Key')
    
     
    app = FastAPI(dependencies=[Depends(check_api_key)])
    
    
    @app.get('/')
    async def main():
        return "OK"
    

    test.py

    import requests
    
    cookies = {"session": "z77xQYZWROmI4fY4"}
    r = requests.get(url="http://127.0.0.1:8000/", cookies=cookies)
    print(r.status_code, r.json())