Giter Site home page Giter Site logo

tokusumi / fastapi-cloudauth Goto Github PK

View Code? Open in Web Editor NEW
320.0 320.0 35.0 158 KB

Simple integration between FastAPI and cloud authentication services (AWS Cognito, Auth0, Firebase Authentication).

License: MIT License

Python 98.53% Shell 1.47%
auth0 aws-cognito fastapi fastapi-cloudauth firebase-authentication python verification

fastapi-cloudauth's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

fastapi-cloudauth's Issues

bump allowed fastapi version

on pypi the fastapi requirement is listed as 'fastapi>=0.60.1,<0.61.0', so doesn't allow the latest version.
Any chance of getting that bumped up?
(Would do a pr but can't see in this repo where it's specified)

cognito: "Validation Error for Claims" - when using custom attributes in cognito

Hey,
I just can't get cognito to work with this library...
I am using the second example

def secure_access(current_user: AccessUser = Depends(auth.claim(AccessUser))):
    # access token is valid and getting user info from access token
    return f"Hello", {current_user.sub}

with the example

class AccessUser(BaseModel):
    sub: str

But as soon as I add custom attributes that are set in my cognito pool, like "email" or "family_name", it throws the error
"Validation Error for Claims"

How can I add custom attributes to grab them like current_user.family_name ?

Feature: Add scopes required to the openapi docs

Hey guys so I wanted to request this feature that I think might be an interesting addition to the library that consists of adding on the Authorization documentation of each request under "HTTP Authorization Scheme" a new row called "Scopes Accepted/Required" where it would be a list of the scopes the backend requires for the request to be accepted.

If you guys could point me to where I could start exploring this feature I might be able to develop my self and contribute but right now Im not sure where does the library define documentation sections.

Here is a image of the place I refer to.
image

Clarification on Role Based Access Control (RBAC) using firebase auth

Thank you for your work on this library, it simplified the standard use case of authentication for my API by a lot.

However, the documentation on RBAC and verifying claims is currently rather minimal and I was not able to figure out how to implement it.

Specifically I do not understand the following line: Use as (auth is this instanse and app is fastapi.FastAPI instanse):

What exactly is auth an instance of for the Firebase case and how can I get it?

Once solved, I can create a PR with some additional explication to the readme for clarification.

Auth0 scopes possibly not working correctly

I tried replicating the tests and they all pass in theory, but I noticed the permissions fail to set (403 response).

collecting ... 
-------------------------------------------------------------------------------- live log collection --------------------------------------------------------------------------------
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /dbconnections/signup HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /oauth/token HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /oauth/token HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /oauth/token HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443

DEBUG urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /api/v2/users/auth0%7C5fe724da128f9f00699d0c67/permissions HTTP/1.1" 403 None

DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /oauth/token HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /oauth/token HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "POST /oauth/token HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "GET /.well-known/jwks.json HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "GET /.well-known/jwks.json HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "GET /.well-known/jwks.json HTTP/1.1" 200 None
DEBUG    urllib3.connectionpool:connectionpool.py:971 Starting new HTTPS connection (1): awsvideo.eu.auth0.com:443
DEBUG    urllib3.connectionpool:connectionpool.py:452 https://awsvideo.eu.auth0.com:443 "GET /.well-known/jwks.json HTTP/1.1" 200 None
collected 14 items                                                                                                                                                                  

tests/test_auth0.py::test_valid_token 
----------------------------------------------------------------------------------- live log call -----------------------------------------------------------------------------------
DEBUG    asyncio:selector_events.py:59 Using selector: KqueueSelector
PASSED                                                                                                                                                                        [  7%]
tests/test_auth0.py::test_no_token PASSED                                                                                                                                     [ 14%]
tests/test_auth0.py::test_incompatible_kid_token PASSED                                                                                                                       [ 21%]
tests/test_auth0.py::test_no_kid_token PASSED                                                                                                                                 [ 28%]
tests/test_auth0.py::test_not_verified_token PASSED                                                                                                                           [ 35%]
tests/test_auth0.py::test_valid_scope PASSED                                                                                                                                  [ 42%]
tests/test_auth0.py::test_invalid_scope PASSED                                                                                                                                [ 50%]
tests/test_auth0.py::test_get_current_user PASSED                                                                                                                             [ 57%]
tests/test_auth0.py::test_not_verified_user_no_error PASSED                                                                                                                   [ 64%]
tests/test_auth0.py::test_insufficient_current_user_info PASSED                                                                                                               [ 71%]
tests/test_auth0.py::test_insufficient_current_user_info_no_error PASSED                                                                                                      [ 78%]
tests/test_base.py::test_raise_error_invalid_set_scope PASSED                                                                                                                 [ 85%]
tests/test_base.py::test_return_instance_with_scope PASSED                                                                                                                    [ 92%]
tests/test_base.py::test_forget_def_user_info PASSED                                                                                                                          [100%]

================================================================================ 14 passed in 4.44s =================================================================================

Screenshot 2020-12-26 at 14 15 43

How can I get the groups/scopes a user is associated in Cognito?

So I implemented my apis with Cognito and this library and everything works really well but I have a couple requests that I wanted to chose what data to return based on the scope of the user. For example if the user scope is admin return all entries but if not only return the entries for that user.

Is this possible with this library?

Unhandled exceptions on malformed tokens

Hello! congrats for the library.

I can see that there is no exception handling for malformed tokens so currently in my application I'm receiving a JWTError from the jose library in some cases which ends up in a 500 error.

I would expect to be handle at https://github.com/tokusumi/fastapi-cloudauth/blob/master/fastapi_cloudauth/base.py#L77

I cannot see an easy and clean way to solve it without overwrite half of the classes I am using. Am I missing something?

Thanks!

CognitoCurrentUser() produces "Validation Error for Claims"

When using CognitoCurrentUser(region=settings.aws_region, userPoolId=settings.aws_cognito_user_pool_id) in my endpoint depencies, it consistently errors a 403: "Validation Error for Claims". Further investigation indicated this has to do with an issue mapping the Cognito reply to the CognitoClaims Pydantic model here:

current_user = self.user_info.parse_obj(claims)

The ValidationError is:

ValidationError(model='CognitoClaims', errors=[{'loc': ('cognito:username',), 'msg': 'field required', 'type': 'value_error.missing'}])

and indeed, the 'cognito:username' field is not available in the claims:

{'sub': '5a8b7ac6-5cd3-4279-b89b-5f59ca6c4144', 'cognito:groups': ['Users'], 'iss': '[Redacted]', 'version': 2, 'client_id': '[Redacted]', 'event_id': 'b0fcb01a-971b-40fe-942c-550dbf0915d2', 'token_use': 'access', 'scope': 'openid email', 'auth_time': 1614092323, 'exp': 1614095923, 'iat': 1614092323, 'jti': '48884493-8fad-47e5-8d8b-a4f44aa41900', 'username': '5a8b7ac6-5cd3-4279-b89b-5f59ca6c4144'}

but 'username' is!

I wasn't able to find why Cognito returns the claims in this different way.

Proposed compatibility fix

class CognitoClaims(BaseModel):
    username: str = Field(alias="cognito:username")
    email: str = Field(None, alias="email")

    class Config:
        allow_population_by_field_name = True

Adding the allow_population_by_field_name = True in the CognitoClaims model config makes it compatible with this other Cognito output.

firebase.JWKS public keys expire and don't get refreshed

This took me a while to track down. I would sometimes get {"detail":"JWK public Attribute for authorization token not found"} after 7 days of the fastapi instance being up. This hinted towards something expiring.

The firebase.JWKsVerifier class sets self._jwks_to_key = jwks.keys; where jwks is a firebase.JWKS instance. JWKS.firebase is constructed like so:

    @classmethod
    def firebase(cls, url: str) -> "JWKS":
        """
        get and parse json into jwks from endpoint for Firebase,
        """
        certs = requests.get(url).json()
        keys = {
            kid: jwk.construct(publickey, algorithm="RS256")
            for kid, publickey in certs.items()
        }
        return cls(keys=keys)

What this means is the keys are queried with certs = requests.get(url).json() and stored for as long as the instance is up, but they are never refreshed.

@tokusumi I can raise a PR to fix this if you're too busy; but i'd like your take on how to proceed with it. I'm not sure where to even do the detection for expired keys.

Using auth0 id_token possibly insecure

First of all I have to admit that I don't fully grasp all the oauth2 and OIDC concepts, which is probably why I stumbled accross this project to begin with, not knowing how to implement all the details myself.

Anyway, I've been reading auth0 documentation and this source code and I see it's possible to retrieve user email using Auth0Claims. But these claims are valid only if FastAPI gets an id_token as Bearer, which is recommended against in this article: https://auth0.com/blog/why-should-use-accesstokens-to-secure-an-api/ that mentions id_token is only meant for the client (which I understand is the frontend, not FastAPI).

I would love to find a reason why the official recommendation is not valid in this project.

In my fastapi API I need to know the user email for every request authorization. That's because I support multi-tenancy, where each user has CRUD access only to their own resources. I think this should be a fairly common scenario.. so I'm puzzled why there wouldn't be a straightforward way to do it.

Firebase - Unauthenticated request is not raising an exception

When injecting the FirebaseClaims object, no exception is raised for an unauthenticated user. It looks like it should raise a 401 exception because auto_error is set to True.

Additionally, it is unclear how to test this via the Swagger UI. There does not appear to be any authentication UI, as there is with cogito.

Code:

async def get_current_user(settings: Settings = Depends(get_settings)):
    return FirebaseCurrentUser(project_id=settings.firebase_project_id)


@app.get("/protected")
async def protected(current_user: FirebaseClaims = Depends(get_current_user)):
    return f"Hello, {current_user.user_id}"

Take lot of time to start a fastapi project

image

Here I have attached the Image, I have used a firebase example and just try to run on ubuntu 20. x LTS
But it take long time to run while using on local machine (windows) it quickly run with in no time. is there any reason or is there any config where I am making mistake .
same code run on local (windows machine ) fine but take long time on cloud which uses ubuntu

Add required scopes to generated OpenAPI documentation

Hi, firstly, thanks for the great library!

I'm wondering if there's currently any way to allow for the required scopes that are defined in the route decorator to be populated into the OpenAPI documentation that gets generated?

I'm guessing since this library uses the HTTPBearer security scheme rather then OAuth2 that the answer is currently "no". If so, is this something you'd look to add in future? It seems like a pretty handy feature for the API documentation to detail what scopes are required to access certain endpoints.

JWK not found | callback url

Hi, I've setup my fastapi-cloudauth as the readme describes in Example (AWS Cognito),
but can't see anywhere to specify my cognito callback URL
and I'm getting a 401 error with:

{
    "detail": "JWK public Attribute for authorization token not found"
}

i'm calling my server like:

curl --location 'http://127.0.0.1:5000/access' \
--header 'Authorization: Bearer XXX

and my code is basically the hello world example:

import uvicorn
from pydantic import BaseModel
from fastapi import FastAPI, Depends
from fastapi_cloudauth.cognito import Cognito, CognitoCurrentUser, CognitoClaims

app = FastAPI()
auth = Cognito(
    region="eu-xxxx,
    userPoolId="eu-north-xxxx",
    client_id="xxxx"
)

@app.get("/", dependencies=[Depends(auth.scope(["read:users"]))])
def secure():
    # access token is valid
    return "Hello"


class AccessUser(BaseModel):
    sub: str


@app.get("/access/")
def secure_access(current_user: AccessUser = Depends(auth.claim(AccessUser))):
    # access token is valid and getting user info from access token
    return f"Hello", {current_user.sub}


get_current_user = CognitoCurrentUser(
    region="eu-xxxx,
    userPoolId="eu-north-xxxx",
    client_id="xxxx"
)


@app.get("/user/")
def secure_user(current_user: CognitoClaims = Depends(get_current_user)):
    # ID token is valid and getting user info from ID token
    return f"Hello, {current_user.username}"

`

Notes:

  1. I've checked the user pool and it works with other frameworks like flask
  2. I am able to manually see the cognito-idp url and the token there is valid
  3. the server is running on my localhost

Simple Auth0 authentication when using the doc or redoc page

When I use your Python package, things work really well, so many thanks for it. 😀

However, there is one thing, where I ask myself, whether I can do much better here. When I try to reach my doc page, it looks like this:

fastapi_doc

When I click on the locking the top right, it looks like this

fastapi_auth

And after I enter the right token_id, I can work with the doc page as intended.

However, getting the token_id is quite cumbersome, as it usually involves curl requests etc.

It would be much nicer, if I am automatically logged in from Auth0, if I authenticated somewhere else in my browser. If I did not authenticate somewhere, then this here pops up

auth0-login

and after authenticating, I can work as intended. Is that possible?

Add ability to specify `user_info` as an extra parameter when instantiating `UserInfoAuth` derived classes

Hi, thank you for you great work.
I have a question/feature request.

Currently, there is no straightforward way to pass user_info into a respective UserInfoAuth derived class (eg. CognitoCurrentUser) before the instantinaion and only inheritance will work. So it'll be great to have something like this:

current_user_auth = CognitoCurrentUser(
        region=settings.aws_region,
        userPoolId=settings.userpool_id,
        client_id=settings.app_client_id,
        user_info=settings.user_info_class      <---- here
    )

The current implementation of CognitoCurrentUser doesn't allow to do this.

It will be nice to have something like this:

class CognitoCurrentUser(UserInfoAuth):
    """
    Verify ID token and get user info of AWS Cognito
    """

    user_info = CognitoClaims

    def __init__(
        self,
        region: str,
        userPoolId: str,
        client_id: str,
        *args: Any,
        **kwargs: Any,
    ):
        url = f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json"
        jwks = JWKS(url=url)
        user_info = kwargs.get("user_info") or self.user_info           <----- new code
        super().__init__(
            jwks,
            user_info=user_info,   <---- changes
            audience=client_id,
            issuer=f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}",
            extra=CognitoExtraVerifier(
                client_id=client_id,
                issuer=f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}",
                token_use={"id"},
            ),
            *args,
            **kwargs,
        )

or be more explicit and add user_info as a paramter to CognitoCurrentUser __init__ method

Cognito class instances talk to AWS at import time

Because of code like jwks = JWKS.fromurl(url) in the __init__(), it is impossible to import modules without them trying to connect to AWS Cognito at import time. This can cause a variety of problems, e.g. - unable to run unit tests without an Internet connection.

How to catch exception or change the Scope not matched message

Hi,

Is it possible to catch the "Scope not matched" and display a more accurate message to the user.

`app.include_router(
manager.router,
prefix="/manager",
dependencies=[Depends(auth.scope('cognitogroup1))],
)
...
@router.get("/units")
def list_units(
current_user: AccessUser = Depends(auth.scope(['cognitogroup1']))
):
ret = [];
try:
print(current_user)
except:
raise

return "OK"

`

[Auth0] user_id from current user

Hello,

Is there a way to extract user_id from current user (Auth0) Auth0CurrentUser()? For example, if I use:

get_current_user = Auth0CurrentUser("dev-some-domain.eu.auth0.com")

@router.get("/")
async def get_current_user(current_user=Depends(get_current_user)):
    return current_user

as a result, I get a JSON object containing only name and email. Example:

{
  "name": "[email protected]",
  "email": "[email protected]"
}

Firebase timeline

Hi there,

Thank you for creating this package, I've been looking around for something like this over the past few months.
I'm wondering if you are actively working on the firebase integration and if so what your timeline is like?

Enfore that a user's e-mail is verified in Auth0 for accessing an API

I want to allow users only to access some APIs, if the e-mail of the user is verified. So far I have come up with that code

import os
from pydantic import Field
from fastapi import FastAPI, Depends
from fastapi_cloudauth.auth0 import Auth0CurrentUser, Auth0Claims

app = FastAPI()


class CustomAuth0Claims(Auth0Claims):
    user_id: str = Field(alias="sub")
    nickname: str = Field(alias="nickname")
    is_verified: bool = Field(alias="email_verified")




get_current_user = Auth0CurrentUser(
    domain=os.environ["AUTH0_DOMAIN"],
    client_id=os.environ["AUTH0_CLIENTID"]
)
get_current_user.user_info = CustomAuth0Claims 


@app.get("/user/")
def secure_user(current_user: Auth0Claims = Depends(get_current_user)):
    # ID token is valid and getting user info from ID token
    return f"Hello, {current_user}"

My question is now, how can I create something like get_current_user, say get_current_verified_user, which I can use for an API to enforce that only e-mail verified users are allowed to use it.

Auth0: get_current_user always gives 401

Hi there,

This is almost certainly user error & not a bug, as I'm new to Auth0.

In Auth0, I have configured an application (which is a VueJS client) set up as well as an API (my FastAPI back-end).

I've managed to get authentication working using the example def main_endpoint_test(current_user: AccessUser = Depends(auth.claim(AccessUser))) - when I do this, I can get the user_id/sub, but I don't get the user email.

I tried using the other approach shown in the example: def secure_user(current_user: Auth0Claims = Depends(get_current_user)):. When I use this, I always get a 401 response. I have initialised the get_current_user passing in the domain and client_id as shown in the example - because the domain is working fine in the simpler auth method, maybe my mistake is entering the wrong value for the client_id?

What is the client ID value should I be setting here, is it my Auth0 Application's client ID (i.e. the one for the VueJS client)? Is it the custom API's ID (as far as I can tell, there is no field explicitly labelled "client ID" in auth0's APIs)?

How to inject auth globally and then use .scope?

i have this code

async def init_auth(app: FastAPI) -> None:
    logger.info("Auth: Configuring connection to {0}", repr(AUTH0_DOMAIN))
    app.state.auth0_client = Auth0(domain=AUTH0_DOMAIN)
    logger.info("Auth: Connection configured")


def _get_client(request: Request) -> Auth0:
    return request.app.state.auth0_client


def get_auth() -> Callable:
    return _get_client

init_auth is triggered on app.add_event_handler("startup", init_auth(application))

then this code does work

@router.get("/something",
            response_model=Any,
            dependencies=[
                Depends(get_auth)
            ]
            )
async def get_something(
        request: Request
):
return []

i would like to use auth.scope("read:something") with Depends but i can't seem to make it work

def get_auth_scoped(
        *,
        scope_name: str,
        client: Auth0 = Depends(get_auth)
) -> Callable:
    return client.scope(scope_name=scope_name)

and

@router.get("/something",
            response_model=Any,
            dependencies=[
                Depends(get_auth_scoped(scope_name="read:something"))
            ]
            )
async def get_something(
        request: Request
):
return []

it results in a AttributeError: 'Depends' object has no attribute 'scope'

what am i missing here? please help?

Multiple scope and or combined

Currently, it is only possible to define one scope to be checked.

@app.get("/access/")
def secure_access(scope=Depends(auth.scope("role_admin"))):
    return f"Hello {scope}"

What i need is some way combine (and / or) multiple scopes.

cond = {"combinator": "or", "scopes": ["role_admin", "super_admin"]}
@app.get("/access/")
def secure_access(scope=Depends(auth.scope(cond))):
    return f"Hello {scope}"

What do you think?

Intermittent disconnection - Use with edge device

Hi,
I am trying to use the cognito plugin on robots in a place where internet connection is not stable.

In this case, the whole api won't load because auth is not loading properly. Click to expand the error.

I am confused on how I can handle it. You can reproduce it locally by cutting of the internet and trying to reload.

Having a try/except in the plugin, handle the timeout and return an error would be the solution. I'm trying to do it myself but doesn't seem so straightforward to me. Any idea how to proceed?

Thanks!

Click here for the logs
Process SpawnProcess-9:
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 169, in _new_conn
    conn = connection.create_connection(
  File "/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py", line 73, in create_connection
    for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
  File "/usr/local/lib/python3.8/socket.py", line 918, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -3] Temporary failure in name resolution

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 699, in urlopen
    httplib_response = self._make_request(
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 382, in _make_request
    self._validate_conn(conn)
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 1010, in _validate_conn
    conn.connect()
  File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 353, in connect
    conn = self._new_conn()
  File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 181, in _new_conn
    raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x7fbb0d021d60>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/requests/adapters.py", line 439, in send
    resp = conn.urlopen(
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 755, in urlopen
    retries = retries.increment(
  File "/usr/local/lib/python3.8/site-packages/urllib3/util/retry.py", line 574, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='cognito-idp.us-east-1.amazonaws.com', port=443): Max retries exceeded with url: /us-east-1_9n1ncAENJ/.well-known/jwks.json (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7fbb0d021d60>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/usr/local/lib/python3.8/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/subprocess.py", line 61, in subprocess_started
    target(sockets=sockets)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/main.py", line 407, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File "/usr/local/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.8/site-packages/uvicorn/main.py", line 414, in serve
    config.load()
  File "/usr/local/lib/python3.8/site-packages/uvicorn/config.py", line 300, in load
    self.loaded_app = import_from_string(self.app)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/importer.py", line 20, in import_from_string
    module = importlib.import_module(module_str)
  File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "./main.py", line 15, in <module>
    from routers import (
  File "./routers/account.py", line 4, in <module>
    from core.cognito import auth
  File "./core/cognito.py", line 46, in <module>
    auth = Cognito(
  File "/usr/local/lib/python3.8/site-packages/fastapi_cloudauth/cognito.py", line 24, in __init__
    jwks = JWKS.fromurl(url)
  File "/usr/local/lib/python3.8/site-packages/fastapi_cloudauth/verification.py", line 50, in fromurl
    jwks = requests.get(url).json()
  File "/usr/local/lib/python3.8/site-packages/requests/api.py", line 76, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 542, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 655, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='cognito-idp.us-east-1.amazonaws.com', port=443): Max retries exceeded with url: /us-east-1_9n1ncAENJ/.well-known/jwks.json (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7fbb0d021d60>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution'))

Having trouble figuring out "next steps"

I'm a "noob" where Cognito is concerned and could be missing something about accessing the example API's. I've got an AWS User Pool defined with a user created. How do I log that user in, or get a JWT Token to use to authorize the example API to access the endpoints?
Thanks for your help.

JWKError

Hi, I am trying to make it work with Firebase.
I am using exaclty the same code you have in the examples

import os
from fastapi import FastAPI, Depends
from fastapi_cloudauth.firebase import FirebaseCurrentUser, FirebaseClaims

app = FastAPI()

get_current_user = FirebaseCurrentUser(
    project_id="my-project-id"
)


@app.get("/user/")
def secure_user(current_user: FirebaseClaims = Depends(get_current_user)):
    # ID token is valid and getting user info from ID token
    return f"Hello, {current_user.user_id}"

but I have the following error:

 File "./src/main.py", line 7, in <module>
    get_current_user = FirebaseCurrentUser(
  File "/home/elvis/Documents/dev-tools/API/env/lib/python3.9/site-packages/fastapi_cloudauth/firebase.py", line 28, in __init__
    jwks = JWKS.firebase(url)
  File "/home/elvis/Documents/dev-tools/API/env/lib/python3.9/site-packages/fastapi_cloudauth/verification.py", line 70, in firebase
    keys = {
  File "/home/elvis/Documents/dev-tools/API/env/lib/python3.9/site-packages/fastapi_cloudauth/verification.py", line 71, in <dictcomp>
    kid: jwk.construct(publickey, algorithm="RS256")
  File "/home/elvis/Documents/dev-tools/API/env/lib/python3.9/site-packages/jose/jwk.py", line 79, in construct
    return key_class(key_data, algorithm)
  File "/home/elvis/Documents/dev-tools/API/env/lib/python3.9/site-packages/jose/backends/rsa_backend.py", line 171, in __init__
    raise JWKError(e)
jose.exceptions.JWKError: No PEM start marker "b'-----BEGIN PRIVATE KEY-----'" found

Any idea?

Thank you

"not verified" response - cognito

Hi, maybe I am missing something obvious, but when authenticating using an auth token from cognito, the fastapi-cloudauth responds with 401 {"detail": "Not verified"}

This does not refer to the user I guess, as the user is email & phone verified, but does it refer to the domain (localhost, local dev)?
How would I test my app locally then?

I hope you can point me in the right direction, as this package would be awesome to use!

Standalone function to verify token

One of my endpoints for API receives cognito token as HTTP parameter instead of authentication header (Some issues sending authentication header on frontend). Other endpoints have been implemented just as described in Readme.

Is there any way to verify that token received as a string parameter using fastapi-cloudauth ?

Specify to work with localstack or Cognito Local for local testing?

Not so much an issue as a question, i wanted to work with a local cognito provider while running my service locally before connecting to cognito in production. I noticed I wasn't able to specify a local host url, looking closer into the code looks like its all hardcoded in the Cognito object. Is there anyway for me to specify the url or would i have to create a PR?

class Cognito(ScopedAuth):
    """
    Verify access token of AWS Cognito
    """

    user_info = None

    def __init__(
        self,
        region: str,
        userPoolId: str,
        client_id: str,
        scope_key: Optional[str] = "cognito:groups",
        auto_error: bool = True,
    ):
        url = f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json"
        jwks = JWKS(url=url)
        super().__init__(
            jwks,
            audience=client_id,
            issuer=f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}",
            scope_key=scope_key,
            auto_error=auto_error,
            extra=CognitoExtraVerifier(
                client_id=client_id,
                issuer=f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}",
                token_use={"access"},
            ),
        )

[Cognito] App client_id is not validated for Cognito JWT (access_token)

I faced with an issue when using congito auth, app client_id is not validating during token verification. So you can path any ID and it will work.

# pass some fake client_id
auth = Cognito(region=aws_region, userPoolId=aws_cognito_userpool_id, client_id='foo-bar')

# access_token - obtained from Cognito
http_auth = HTTPAuthorizationCredentials(scheme='Bearer', credentials=access_token)

await auth.verifier.verify_token(http_auth)
True

The problem is that jwt.decode (jose lib) doesn't expect client_id in token and since aud is not defined it skips validation.

Cognito "iat" claim set in the future

We're running an issue where the iat claim (Issued At) in AWS Cognito is ~1 second into the future (on multiple machines and server configurations, synced with NTP).
Would it be helpful to maybe add a configurable time delta to verification to account for these slightly out of sync use cases?
I can create a PR for this, but I first wanted to check if this would be anything that could be incorporated.

Recommend way to use cognito as a dependency

Hello!

I'm trying to use cloudauth as a dependency injected value, and am having a bit of trouble getting it setup.

Basically, I have auth setup like:

def my_scoped_auth(conf: config.Config = config_dependency):
    """Get an auth scope dependency for DICOM reading."""
    auth = Cognito(region=conf.cognito_region, userPoolId=conf.cognito_pool_id)
    auth.scope_key = "scope"
    auth.scope_name = "my_scope"
    return auth


@app.get("/test", dependencies=[Depends(my_scoped_auth)])
def test_endpoint(
):
    return Response("OK")

This doesn't work since the auth is never actually called.

I've also tried other variations like:

async def my_scoped_auth(conf: config.Config = config_dependency):
    """Get an auth scope dependency for DICOM reading."""
    auth = Cognito(region=conf.cognito_region, userPoolId=conf.cognito_pool_id)
    auth.scope_key = "scope"
    auth.scope_name = "my_scope"
    return await auth()

But this fails since the call now no longer has context of the dependency tree.

There maybe something obvious here, but couldn't figure out how to get this working.

For some additional context, I'm trying to link this up so I can have my unit tests decoupled ala:

@pytest.fixture()
def client(test_config, cognito, backend_client):
    """Simple happy path client."""
    app.dependency_overrides[main.get_config] = lambda: mock_config
    app.dependency_overrides[main.my_scoped_auth] = lambda: mock_cognito
    client = TestClient(app)
    return client

So the usual globally scoped object doesn't work.

Thanks!

Auth0 token

First of all, thanks for your work, easy and clear to use 😄

The issue I'm facing isn't related to repo itself, but on how to generate the Auth0 token to have all the needed information to use the methods properly.

For some endpoints that I'm implementing I'll just need to check if the token has the required scopes and for some others I'll need to use: current_user: Auth0Claims = Depends(auth.get_current_user). However I can't find a way to request to Auth0 a token with all of this information: username and scopes. I don't know if I'm misunderstanding some information/concepts or what is going wrong...

Would appreciate some information with the steps to follow. Thanks in advance!

User token (JWK) validation

I notice there is (not jet) a validation of the JWK token in your code.
So if you disable or delete a user from Cognito the tokens which have been assinged before are still valid if validating against your own /well-known/jwks.json url.

Is there a easy way to work around this? For now I check the token on the API end point get_user so get the current status of the user.

Mock auth0 for tests

Request: adding documentation of mocking auth for tests.

I tried:

"""tests/test_templates.py"""
from fastapi.testclient import TestClient
from tests import auth_with_mocked_auth

client = TestClient(auth_with_mocked_auth())

def test_create_template():
    response = client.get("/templates/")
    assert response.status_code == 200
"""tests/__init__.py"""
def auth_with_mocked_auth():
    def override_auth_dependency():
        return {
             'iss': '<auth0 endoint>',
             'sub': 'auth0|<auth0 id>',
             'aud': [
                 '<endpoints>',
             ],
             'iat': <...>,
             'exp': <...>,
             'azp': '<...>,
             'scope': 'openid profile email read:current_user update:current_user_metadata',
             'permissions': [<...>]
        }

    app.dependency_overrides[auth] = override_auth_dependency()
    return app

Yet, getting 403 with details: "Not authenticated"

JWKS.firebase() only works if `cryptography` is installed

This took me a while to track down.

Reproduce with: python -c 'from fastapi_cloudauth.verification import *; print(JWKS.firebase("https://www.googleapis.com/robot/v1/metadata/x509/[email protected]"))'

If cryptography is installed, this succeeds.

If it's not installed, it fails with the following traceback:

Traceback (most recent call last):
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/jose/backends/rsa_backend.py", line 152, in __init__
    self._prepared_key = pyrsa.PublicKey.load_pkcs1(key)
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/key.py", line 124, in load_pkcs1
    return method(keyfile)
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/key.py", line 327, in _load_pkcs1_pem
    der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY')
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in load_pem
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in <listcomp>
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 75, in _pem_lines
    raise ValueError('No PEM start marker "%r" found' % pem_start)
ValueError: No PEM start marker "b'-----BEGIN RSA PUBLIC KEY-----'" found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/jose/backends/rsa_backend.py", line 155, in __init__
    self._prepared_key = pyrsa.PublicKey.load_pkcs1_openssl_pem(key)
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/key.py", line 356, in load_pkcs1_openssl_pem
    der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY')
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in load_pem
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in <listcomp>
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 75, in _pem_lines
    raise ValueError('No PEM start marker "%r" found' % pem_start)
ValueError: No PEM start marker "b'-----BEGIN PUBLIC KEY-----'" found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/jose/backends/rsa_backend.py", line 158, in __init__
    self._prepared_key = pyrsa.PrivateKey.load_pkcs1(key)
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/key.py", line 124, in load_pkcs1
    return method(keyfile)
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/key.py", line 591, in _load_pkcs1_pem
    der = rsa.pem.load_pem(keyfile, b'RSA PRIVATE KEY')
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in load_pem
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in <listcomp>
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 75, in _pem_lines
    raise ValueError('No PEM start marker "%r" found' % pem_start)
ValueError: No PEM start marker "b'-----BEGIN RSA PRIVATE KEY-----'" found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/jose/backends/rsa_backend.py", line 161, in __init__
    der = pyrsa_pem.load_pem(key, b"PRIVATE KEY")
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in load_pem
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 101, in <listcomp>
    pem_lines = [line for line in _pem_lines(contents, pem_start, pem_end)]
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/rsa/pem.py", line 75, in _pem_lines
    raise ValueError('No PEM start marker "%r" found' % pem_start)
ValueError: No PEM start marker "b'-----BEGIN PRIVATE KEY-----'" found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/fastapi_cloudauth/verification.py", line 70, in firebase
    keys = {
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/fastapi_cloudauth/verification.py", line 71, in <dictcomp>
    kid: jwk.construct(publickey, algorithm="RS256")
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/jose/jwk.py", line 79, in construct
    return key_class(key_data, algorithm)
  File "/home/adys/.cache/pypoetry/virtualenvs/ola-rJcPxiV6-py3.9/lib/python3.9/site-packages/jose/backends/rsa_backend.py", line 171, in __init__
    raise JWKError(e)
jose.exceptions.JWKError: No PEM start marker "b'-----BEGIN PRIVATE KEY-----'" found

I'm too tired to keep debugging this and figure out why exactly this happens. At first glance it seems that jose has a rsa backend and a cryptography backend and silently falls back to the former if the latter is not installed. They likely don't support the same featureset. Looking at the traceback, I guess the native rsa backend doesn't support certificates… which, to be honest, could use a proper check with a NotImplementedError if that's the case.

I think you should change your dependency from python-jose to python-jose[cryptography].

TypeError: cannot pickle '_cffi_backend.FFI' object

Using a straight copy-paste of your example application, I get this pickle error.

Traceback (most recent call last):
  File "/Users/scott/.local/bin/uvicorn", line 10, in <module>
    sys.exit(main())
  File "/Users/scott/.local/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/scott/.local/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/scott/.local/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/scott/.local/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/scott/.local/lib/python3.8/site-packages/uvicorn/main.py", line 355, in main
    run(**kwargs)
  File "/Users/scott/.local/lib/python3.8/site-packages/uvicorn/main.py", line 379, in run
    server.run()
  File "/Users/scott/.local/lib/python3.8/site-packages/uvicorn/main.py", line 407, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File "uvloop/loop.pyx", line 1456, in uvloop.loop.Loop.run_until_complete
  File "/Users/scott/.local/lib/python3.8/site-packages/uvicorn/main.py", line 414, in serve
    config.load()
  File "/Users/scott/.local/lib/python3.8/site-packages/uvicorn/config.py", line 300, in load
    self.loaded_app = import_from_string(self.app)
  File "/Users/scott/.local/lib/python3.8/site-packages/uvicorn/importer.py", line 20, in import_from_string
    module = importlib.import_module(module_str)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "./main.py", line 12, in <module>
    @app.get("/", dependencies=[Depends(auth.scope("read:users"))])
  File "/Users/scott/.local/lib/python3.8/site-packages/fastapi_cloudauth/base.py", line 83, in scope
    obj = deepcopy(self)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 270, in _reconstruct
    state = deepcopy(state, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 230, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/scott/.pyenv/versions/3.8.3/lib/python3.8/copy.py", line 161, in deepcopy
    rv = reductor(4)
TypeError: cannot pickle '_cffi_backend.FFI' object

Firebase x509 issue

python-jose doesn't support x509 certificates without the extra install option (anymore? I not quite sure if it was a silent breaking change).
This breaks the FirebaseCurrentUser's JWKS generation because google gives x509 files for the public key.

the recommended setup on python-jose is using cryptography as the background, setup as following

pip install python-jose[cryptography]

but is sort of a debate I guess (acknowledging the fact that cryptography requires building and is not a small dependency)

At least it needs some documentation about it, so I would like to know whats good for this library.

Support Multiple clientids for aws cognito

get_current_user = CognitoCurrentUser( region=region, userPoolId=poolid, client_id=clientid )

Often, we would like to support multiple aws Cognito app clients for a single API.
Requesting a feature to support multiple app client ids for the aws Cognito objects.

Maybe something like
get_current_user = CognitoCurrentUser( region=region, userPoolId=poolid, client_id=[clientid1, clientid2] )

auto_error setting not passed to HTTPBearer dependencies

Hi guys, thanks for writing this implementation!

I'm currently trying to fit this one into our set-up but I run into an issue with auto_error=False setting not being respected by the dependency injections inside fastapi_cloudauth. Fastapi-cloudauth builds on the fastapi HTTPBearer dependency injection, which raises an HTTPException when the credentials are not found in the headers. To prevent this exception, it supports the auto_error=False option. We need this to support other authentication methods.

Our authentication set-up is based on this medium article to combine multiple types of authentication. To make this work, the "auto_error=False" is essential, to make sure none of the authentication methods block each other. (see example implementation below)

Proposed fix:
In base.py, there are these dependency injections for HTTPBearer(), which currently don't pass the auto_error parameter:

self, http_auth: HTTPAuthorizationCredentials = Depends(HTTPBearer())
and
self, http_auth: HTTPAuthorizationCredentials = Depends(HTTPBearer())

These dependencies do support the auto_error parameter, like: HTTPBearer(auto_error=self.auto_error). Possibly you'd want to set this up as a partial function in the constructor.


For reference: example implementation
This fails on an HTTPException from HTTPBearer when the OAuth headers are not passed - which unfortunately is the case when another authentication method is used..

api_key_query_dependency = APIKeyQuery(name=settings.api_key_name, auto_error=False)
api_key_header_dependency = APIKeyHeader(name=settings.api_key_name, auto_error=False)
api_key_cookie_dependency = APIKeyCookie(name=settings.api_key_name, auto_error=False)
cognito_auth_dependency = Cognito(region=settings.aws_region,
                                  userPoolId=settings.aws_cognito_user_pool_id,
                                  auto_error=False
                                  ).scope("users")


async def get_authorization(
        api_key_query: str = Security(api_key_query_dependency),
        api_key_header: str = Security(api_key_header_dependency),
        api_key_cookie: str = Security(api_key_cookie_dependency),
        cognito_auth: str = Depends(cognito_auth_dependency),
) -> Security:
    if not cognito_auth \
            or api_key_query == settings.api_key \
            or api_key_header == settings.api_key \
            or api_key_cookie == settings.api_key:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )

Understanding how to use this lib

I am new to Auth0 and l would like to understand the use of this library. I followed the auth0 example; l can see the fastapi schema; and l went to Auth0 dashboard for the domain. And anytime l visit the endpoint, l get forbidden error. My questions is how do l perform below and where do l use them with your package?

  1. Create a user assigned read:users permission in Auth0
  2. Get Access/ID token for the created user

See attached Screenshot:
Screenshot 2021-02-22 at 7 15 36 AM

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.