Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC/Draft] Add FastAPI SecurityBase-based authentication switch to Saleor's offline JWT (JWK) verification #33

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

pkucmus
Copy link
Collaborator

@pkucmus pkucmus commented Apr 7, 2022

Why the change

Schema documentation

FastAPI being tied to OAS should have apps built with it describing the API appropriately. The previous usage of simple deps was not leveraging the securityScheme definition, now it does.
Ref: https://swagger.io/docs/specification/authentication/api-keys/#multiple
image

Precise authentication

More importantly, the proposed change reflects the truth more accurately. A Saleor app on every call does multiple authentication checks, at the very least it authenticates a Saleor instance (SaleorDomainSecurity) then it validates the requestor either with SaleorUserAuthSecurity for Dashboard users or with SaleorWebhookSecurity when validating the source of a webhook event. The two latter ones are always dependent on domain authentication hence the dependency - this means we have endpoints secured with:

  • Saleor Domain Validation
  • Saleor Domain Validation AND Saleor User Authentication
  • Saleor Domain Validation AND Saleor Webhook Signature Verification
    (the AND is important here)

The auto_error logic is important as it allows to wrap the security methods with an additional layer in case there will be an endpoint with a OR security like

(Saleor Domain Validation **AND** Saleor Webhook Signature Verification) 
OR 
(Saleor Domain Validation **AND** Shared Secret Authentication)

in that case one might want to create something like the following:

saleor_auth = SaleorUserAuthSecurity(auto_error=False)
shared_secret_auth = SharedSecretAuth(auto_error=False)

async def my_authentication(
    saleor_principal: SaleorPrincipal = Security(saleor_auth),
    principal: Principal = Security(shared_secret_auth),
):
    if saleor_principal:
        return saleor_principal
    elif principal:
        return principal
    raise HTTPException(
        status_code=HTTP_401_UNAUTHORIZED,
        detail=f"Provided domain {saleor} is invalid.",
    )

Caveat: FastAPI does not have the ability to fully support what is described here - fastapi/fastapi#2835 - a user will need to rely on the verbosity of the errors when there is AND authentication despite the schema describing it being an OR

To do:

  • fix docs
  • fix unit tests
  • unify the Principal (SaleorPrincipal) I don't want to have special logic in handlers that would need to recognize the type of the principal - one representation should be enough

@pkucmus pkucmus self-assigned this Apr 7, 2022
@pkucmus pkucmus added this to the 0.4.0 milestone Apr 7, 2022

settings = Settings(
debug=True,
saleor_domain="172.17.0.1:8000",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a sample app for the wider audience I would add a comment here on why this IP is here and how to handle it for mac users.

auto_error: bool = True,
):
self.model = APIKeyModel(
**{"in": APIKeyIn.header},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why u need to unpack it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in being a Python keyword, can't be used as an argument, that's how they defined the args there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But correct me if I'm wrong - it does not matter if u use it as an fn keyword argument, right? Ur not shadowing anything

auto_error: bool = True,
):
self.model = APIKeyModel(
**{"in": APIKeyIn.header},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

if self.auto_error:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail=f"Missing {SALEOR_TOKEN_HEADER.upper()} header.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we be consistent across errors?

Suggested change
detail=f"Missing {SALEOR_TOKEN_HEADER.upper()} header.",
detail=f"Missing token {SALEOR_TOKEN_HEADER.upper()} header.",

Maybe create a custom exception class that would be consistent with generating error messages?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that to allow some control over the error payload of a service which would be good but might the detrimental - now if an app does not care about a custom payload you do not need to do anything. If it does want to hide this default and use a different error schema it would be a matter of making an error handler for HTTPException.
If we went for a custom exception here we would force the user to create a custom error handler every time.

@pkucmus pkucmus marked this pull request as draft April 8, 2022 11:41
Copy link
Member

@koradon koradon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks ok for me but the CI is not passing and there are no tests really for the new things. I would like to see unit tests for the new Security classes.
Also, I can't see any test that uses saleor_user_auth_security.

If we are already here I would rename samples to examples. It is more descriptive.

@pkucmus
Copy link
Collaborator Author

pkucmus commented Apr 10, 2022

@koradon I'll do all that once we approve and agree that this PoC is the way to go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants