dorinclisu / fastapi-auth0 Goto Github PK
View Code? Open in Web Editor NEWFastAPI authentication and authorization using auth0.com
License: MIT License
FastAPI authentication and authorization using auth0.com
License: MIT License
Hey, great package. I followed your video guide. If I obtain a token for a user using the authorize button in docs, that works fine. But, if I obtain a token on the front end using the Auth0, that token ends up being malformed even when I provide the audience.
Do you know how the correct token can be obtained thru the javascript SDK?
Hi @dorinclisu thank for the library, this is great and very straightforward!
I have a question/suggestion, I can see that the request for retrieving the JSON web key sets is hardcoded during the entity initialisation (
fastapi-auth0/src/fastapi_auth0/auth.py
Line 85 in ae5256d
So, in my case, my tests suit is fully running locally with a signed token that I'm generating locally instead of making external requests to Auth0; also, for uncoupling the tests suit in case I have to move out of Auth0 in the future.
Currently I avoid the issue monkey patching the entity so it works but I think that could be easy if we can extract that specific pice of code out of the initialisation.
What do you think?
Thanks again!
I'm not sure if this is the proper place to ask, but here I go.
I'm new to auth0 and Fast API and was looking at the different plugins that are available. The main ones that I saw were this plugin and FastAPI Cloud Auth. If I'm not mistaken, I saw that you created an issue for that plugin back in December.
If you don't mind me asking, I was just curious what the advantage of your implementation compared to theirs is and what led to you building your own?
Thanks for your time and effort on this project!
https://auth0.com/blog/preparing-for-rules-and-hooks-end-of-life/
In order to obtain the user email we are relying on auth0 rules which will be deprecated.
https://github.com/dorinclisu/fastapi-auth0#email-field-requirements
This issue is for tracking progress on the required changes.
Pydantic v2 introduces some breaking changes which could affect us, for example #36
FastAPI 0.100 which introduces support for pydantic v2 might have its own particularities to be checked.
Is there a plan to migrate from python-jose
to pyjwt
? Python-jose isn't maintained any more and contains some known vulnerabilities.
I noticed that there was some effort done in #41 , but not sure what happened to it. As an intermediate solution, we could perhaps move to python-jose[cryptography]
which is already recommended above the default python-jose
(with Python backend)?
Is there a nifty way to create an option to only allow m2m clients to use a specific endpoint, or do you suggest just using the CustomAuth0User workflow and then checking the grant-type on each endpoint?
An extension to this question might be allowing only clients with a custom scopes or those which are m2m tokens.
Regards.
First of all thank you for your work on this library, it is the best-integrated way to get Auth0 to work with FastAPI. Would you be interested in publishing this package on PyPi?
When securing endpoint only by decorator dependencies, it doesn't secure it at all...
Here is the relevant code rip:
@app.get("/templates", response_model=Response[List[Template]], dependencies=[Depends(auth.implicit_scheme)])
def list_templates():
data = get_static("templates.json")
return Response(data=data)
I'm expecting to get 403 when I don't send any Authorization header, but i get 200: curl --location --request GET 'http://localhost:8000/templates'
Only when I use auth.get_user as a parameter for list_templates
function like this:
@app.get("/templates", response_model=Response[List[Template]], dependencies=[Depends(auth.implicit_scheme)])
def list_templates(user: Auth0User = Security(auth.get_user, scopes=['read:users'])):
data = get_static("templates.json")
return Response(data=data)
I get 403 when not sending Authorization header.
So, am I missing something? or do I have to use user for authentication will invoke?
By default aud
is not a required field in jwt.decode
. See:
It would be great to set those as required fields on the jwt, if not by default at least via an option.
It would also be great to be able to validate the token is issued for a given Auth0 Application (Client ID), which is set as azp
. See related python-jose
issue
I tried the simple example without the scoping:
from fastapi import FastAPI, Depends, Security
from fastapi_auth0 import Auth0, Auth0User
auth = Auth0(
domain='...',
api_audience='...',
)
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/secure", dependencies=[Depends(auth.implicit_scheme)])
def get_secure(user: Auth0User = Security(auth.get_user)):
return {"message": f"{user}"}
and authenticated a user. But when I tried to execute the /secure endpoint, it returns
{
"detail": "Error parsing Auth0User"
}
and the console error
Handled exception parsing Auth0User: "1 validation error for Auth0User
permissions
Field required [type=missing, input_value={'iss': 'https://dev-aos6...CCDkOMHVc', 'scope': ''}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.1/v/missing"
Traceback (most recent call last):
File "~/server/venv/lib/python3.9/site-packages/fastapi_auth0/auth.py", line 219, in get_user
user = self.auth0_user_model(**payload)
File "~/server/venv/lib/python3.9/site-packages/pydantic/main.py", line 159, in __init__
__pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Auth0User
permissions
Field required [type=missing, input_value={'iss': 'https://dev-aos6...CCDkOMHVc', 'scope': ''}, input_type=dict]
Am I missing a piece of syntax?
Thank you so much for your work so far. It was easy to add Auth0 support to my application.
I have been using it to verify the Bearer token and so far it has worked great. Now, I might have to add cookie support as well.
I was able to adapt your code to add a way to check the Cookie and decode/verify it and return the access_token
to the get_user
but the FastAPI router dependencies still expect the Bearer token so it fails. Or maybe I went about the wrong way.
Can you let me know if what I want to do is possible ?
# fastapi router
router = APIRouter(
prefix="/api/user",
dependencies=[Depends(auth0_object.authcode_scheme)],
responses=default_responses,
)
# Added to your auth.py
from fastapi.security.api_key import APIKeyCookie
from config import auth0_settings
from utils.auth0_cookie_decryptor import validate_cookie
class AppSessionCookie(APIKeyCookie):
async def __call__(self, request: Request):
return await super().__call__(request)
def bearer_or_cookie_check(
bearer_creds: HTTPAuthorizationCredentials = Security(
Auth0HTTPBearer(auto_error=False)
),
session_cookie: str = Security(
AppSessionCookie(name="appSession", auto_error=False)
),
):
if bearer_creds:
return bearer_creds
if session_cookie:
try:
decrypted_cookie = validate_cookie(
jwe_secret=auth0_settings.session_secret.get_secret_value(),
jwe_cookie_string=session_cookie,
)
if decrypted_cookie:
creds = HTTPAuthorizationCredentials(
scheme=decrypted_cookie["token_type"],
credentials=decrypted_cookie["accessToken"],
)
return creds
except Exception as error:
logger.warning("Exception while decrypting cookie: %s", str(error.args))
raise HTTPException(403, detail="Invalid session cookie")
return None
# and changed the depends get_user
creds: Optional[HTTPAuthorizationCredentials] = Depends(
bearer_or_cookie_check
), # changed depends
I see that the authcode_scheme
is also from the Auth0
class and that is where FastAPI checks and rejects if no bearer is found.
Should I just inherit and override the __call__
like you did for OAuth2ImplicitBearer
? This is only for Swagger so should be fine right ?
The weird thing is I can also see the appSession
cookie in Swagger as well. I dunno where it's coming from.
Sorry for the "support" type issue but just wanted to see if I want can be done. Feel free to close it.
EDIT: I realise that the OAuth2AuthorizationCodeBearer
is for use to get the access token directly (right ?) so will I lose that if I override the __call__
method ? The cookie will be set with the domain attribute from the frontend so I don't think I care about getting the cookie from Swagger.
I also understand that the actual verification is done in get_user
so I can exclude dependencies from the router but I will lose the Swagger docs for auth. I would like to keep both.
UPDATE: Added more info about my goal.
I realised I might not have explained my intentions well.
Currently, I get an access_token from a React SPA and this library validates it.
We have another frontend client using Nextjs. That client doesn't have access to the token, instead it encrypts the access_token and the id_token in a cookie using a shared secret. The Next environment itself uses this cookie for Auth.
Now, this results in client requests being proxied via Next which itself requires duplicate proxy API endpoints in the frontend.
Because, the cookie can be sent across the same domain, we can call the backend and any available cookies can be sent with the request.
What I want to do (or kind of have done) is to read this cookie and decrypt it. Then I extract the access_token and pass that to the get_user function from this library.
The cookie is never modified or sent back to the client. The actual authentication is still done by this library. Any error with cookie validation will result in get_user dependency failure.
When I create the user for my FastAPI app via localhost the page redirects me to https://localhost/docs/oauth2-redirect#access_token={token}, and everything works well.
The issue is when I deploy my application on the server, then I get NGINX 404 error. I tried messing with application settings in auth0 but got no results, I am not exactly sure what is wrong here.
Any guidance or tips would be welcome.
PS. my application is using additional prefix path {service-template-python/} I am also using authentication within APIRouter
How can I use the Implicit grant without using swagger?
I managed to get to the LogIn box and authenticate, however I need to accomplish this without swagger.
How can I do a POST request to mi API and send the implicit grant there?
Hi @dorinclisu ,
I really appreciate your work on this project and changing the project to MIT license. Following on from #25 , would you be able to publish an updated package to pypi?
Thanks!
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
@router.get("/secure", dependencies=[Depends(auth.implicit_scheme)])
def get_secure(
user: Optional[Auth0User] = None
):
return {"message": f"{user}"}
Where to get this token?
Nothing found in caches, storage, etc
Let's say I want to create a user in my database if it does no exist right after the Authorization Flow on all the auth required endpoints.
How should I proceed ?
Hey, thanks for working on this! The timing is amazing for me.
I've been trying to get to grips with the code for this module, and I'm just wondering what's the function of the auth0_rule_namespace
constant? The only time it's used seems to be in the Auth0 user model, as an alias for email
. Mostly for my own sanity I was wondering if you could clarify what this is for?
Thanks!
Joel
I've been integrating this package into my application and had a question regarding schemes.
If I understand correctly, the purpose of the auth schemes is to interface with OpenAPI and allow a developer to be able to test the API and authenticate directly from the FastAPI docs section of our APIs. Is that correct?
If so, I tried reading through the source code and didn't see a client credentials (m2m) scheme. Do you have any advice on how I could implement the client credentials scheme for Auth0 so I can authenticate using my m2m client id and client secret from the docs?
If you create an access token for a different API audience, or different tenant, then obviously the authorization should fail.
However the error message is 401 "Malformed token"
.
This is quite misleading, normally when a token is malformed, it is because you just gave a random string, or because you forgot to copy-paste a few characters.
The error comes from:
fastapi-auth0/src/fastapi_auth0/auth.py
Lines 118 to 140 in ae5256d
The kid
is different (because of different api audience), and therefore the rsa_key
is empty, and a general JWTError
is raised.
Is it possible to give a better error message. Maybe something like "Token not authorized"
or "Token has wrong audience"
would be better suited.
when tried to run this command : uvicorn example:app --port 8081
Error : urllib.error.URLError:
Any suggestions please
Hi,
first of all thank you for your work!
In the process of writing tests, I stumbled upon the user authentication. I am not sure how to proceed, is there a way to override the user from the test side therefore skipping the Auth0 check?
Thank you
Hi @dorinclisu ,
I really like your work here and I would like to incorporate it into my solution, which is currently open-sourced under the Apache v2 license.
Since this would not be possible, if I use a package with GPL version 3 license, I was wondering whether you are open to change the license to Apache v2? Or do you have some specific objections to that?
I don't see an issue template format and I haven't contributed much to OSS yet so forgive me if this is messy.
First, this is a great package! It worked very well for me, and I'd love to help make it even better.
I think it would make sense to set auth0_rule_namespace
via environment (or through some other means, but environment is what seems simplest to me). I'd be happy to make a PR with the changes. This would allow users to set their own namespace for the rule in auth0. It would just change this line:
fastapi-auth0/src/fastapi_auth0/auth.py
Line 15 in 66578c4
auth0_rule_namespace: str = os.getenv('AUTH0_RULE_NAMESPACE', 'https://github.com/dorinclisu/fastapi-auth0')
Do you think that'd work fine? If so, I'd be thrilled to make a PR for it (I know it's very small but I figure I'd start small).
Steps I have done:
function addEmailToAccessToken(user, context, callback) {
var namespace = 'namespace';
context.accessToken[namespace + 'email'] = user.email;
return callback(null, user, context);
}
os.environ["AUTH0_RULE_NAMESPACE"] = "namespace"
I have also tried adding passing 'openid profile email' to route and Auth0 scopes to see if makes any difference at all. It didn't
auth.get_user still returns email=None
What am I doing wrong? Thanks in advance.
Quite new to auth0 so really like your repo here. Is there any way to get all the roles, assigned to a user?
class Auth0User(Auth0UserBase):
roles: list[str] = Field(None, alias=f'{settings.auth0_rule_namespace}/roles') # type: ignore [literal-required]
I would like to extend your class Auth0 with an additional roles field, which I have added to my JWT
The main goal is to be able to obtain this field from the JWT passed through the request
@app.get("/api/get_auth0_user")
def get_secure(user: Auth0User = Security(auth.get_user)):
return {"message": f"{user}"}
## Here it would display id, permissions, email, and roles
api_audience
parameter only takes a single string. How can I initiate with support for multiple audience (validates from a list of audience, so multiple clients can connect)
The auth flow currently uses Auth0HTTPBearer, which depends on a Request instance (http), for Websockets, I think we need to use cookies to pass the token?
I'm just wondering if there's a nice way to required specific auth0 permission
s in the Security(get_user)
dependency, similar to how you can require scopes. We have an application which uses permissions heavily and I'm wondering if I've missed something here.
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.