tokusumi / fastapi-cloudauth

Simple integration between FastAPI and cloud authentication services (AWS Cognito, Auth0, Firebase Authentication).
MIT License
331 stars 35 forks source link

Recommend way to use cognito as a dependency #32

Open TimOrme opened 3 years ago

TimOrme commented 3 years ago

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!

tokusumi commented 3 years ago

At first, thank you for trying cloudauth!

You have to pass Cognito instance into Depends, if you would like to call verification process automatically for a request. But If you instantiate Cognito in dependency, there is no way to add that process into dependency trees. So, Alternatively, I recommend you could fire verification process explicitly at the same time when instantiate.

You need to add 2 lines into your code as follows:

from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

async def my_scoped_auth(
    conf: config.Config = config_dependency,
    http_auth: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False)),  # required for verification
):
    """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(http_auth)  # call verification process explicitly