Closed samedii closed 3 years ago
Optional authentication is against common practices. Why exactly you need something like this?
I see. I have a rest api where I want to return a list of public objects A and if the user is authorized I would have liked to show any private objects A belonging to that user in that list too. I guess I have to duplicate a lot of endpoints instead then or do you know of a better solution?
public and secure endpoints needs to be seperated. You can create handler to deal with endpoint. If auth user is supplied to handler it will return puplic + owned objects. If auth user is not provided it will only return public.
Okay, thanks :+1:
It is already supported, although you have to be extra careful.
By default auto_error = True
so you will set it to false. In case of a failed authentication / authorization, auth.get_user
returns None instead of raising Auth0UnauthenticatedException that triggers the typical 401 response.
auth = Auth0(...)
dangerous_auth = Auth0(..., auto_error=False)
@router.get("/secure", dependencies=[Depends(auth.implicit_scheme)])
def get_secure(
user: Optional[Auth0User] = Security(dangerous_auth.get_user)
):
if user is None:
return {"message": "public user"}
return {"message": f"{user}"}
Note that you can have multiple Auth0 objects in the same app, so if you have some endpoints that always need authentication (no public mixup), I recommend using the regular auth
and leave dangerous_auth
only for those public endpoints.
Are you sure that it works? I tried to do the same but I get 403 Error: Forbidden
{
"detail": "Not authenticated"
}
This is my code:
import os
from fastapi import APIRouter, Depends, Security
from fastapi_auth0 import Auth0, Auth0User
from typing import Optional
auth = Auth0(
domain=os.environ["AUTH0_DOMAIN"],
api_audience=os.environ["AUTH0_API_AUDIENCE"],
)
guest_auth = Auth0(
domain=os.environ["AUTH0_DOMAIN"],
api_audience=os.environ["AUTH0_API_AUDIENCE"],
auto_error=False,
)
router = APIRouter()
@router.get("/secure", dependencies=[Depends(auth.implicit_scheme)])
def get_secure(
user: Optional[Auth0User] = Security(guest_auth.get_user)
):
if user is None:
return {"message": "public user"}
return {"message": f"{user}"}
And I try to do this:
curl -X 'GET' \
'http://localhost:8000/secure' \
-H 'accept: application/json'
I also tried switching out Depends(auth.implicit_scheme)
for Depends(guest_auth.implicit_scheme)
Note that my API is a "third party" application in case that matters
The issue seems to arise in get_user
. The auto_error
is not passed through. Example where the default is auto_error=True
async def get_user(self,
security_scopes: SecurityScopes,
creds: HTTPAuthorizationCredentials = Depends(Auth0HTTPBearer(auto_error=False))
) -> Optional[Auth0User]:
if creds is None:
return None
I don't see any pretty solution for this but I'm not used to the "Depends" pattern. Do you see a nice solution to setting auto_error
dynamically in the default value?
Released fix as 0.2.1
Is this already possible? Is it bad practice? If not, how difficult do you think it would it be to implement?
If you can point me in the right direction then I can try to create a PR
E.g. something along the lines of