Skip to content

ModelBackend.auhenticate signature should be more relaxed #2591

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

Open
PedroPerpetua opened this issue Mar 31, 2025 · 0 comments
Open

ModelBackend.auhenticate signature should be more relaxed #2591

PedroPerpetua opened this issue Mar 31, 2025 · 0 comments
Labels
bug Something isn't working

Comments

@PedroPerpetua
Copy link

Enhancement

What's wrong

The current type stubs define the authenticate method of the ModelBackend (django.contrib.auth.backends.ModelBackend) as such:

def authenticate(
    self, 
    request: HttpRequest | None, 
    username: str | None = ..., 
    password: str | None = ..., 
    **kwargs: Any,
) -> _UserModel | None: ...

The parent class, BaseBackend defines it as such:

def authenticate(self, request: HttpRequest | None, **kwargs: Any) -> _UserModel | None: ...

When Django looks for which authentication backend to use, it'll always call said backend using the parameters as kwargs, until it finds the first backend that accepts the parameters and returns a valid user (or None if they all fail). The documentation itself instructs the user to define it's own kwargs when defining a custom authentication backend (relevant docs).

However, a very common use case here is to subclass ModelBackend instead of BaseBackend, in cases where we want to make simple adjustments to how authentication is done, but not how it's handled once you have the specific user; in this cases, you'll subclass the ModelBackend, which already has all the code to handle permissions that BaseBackend does not, and simply overwrite the authenticate method so that instead of, for example, a username and password, it uses a token or a code or something of the sorts. For example:

class MyCustomBackend(ModelBackend):

    def authenticate(self, code: Optional[str] = None, **kwargs: Any) -> Optional[User]:
        return User.get_user_for_code(code)

In this simple example Mypy will give an "override" error: Signature of "authenticate" incompatible with supertype "ModelBackend", even though this backend does not use the username / password at all.

Alternatives

There are already alternatives to getting around this of course; simply #type: ignore[override] will do, or adding the typed arguments to the definition even though they are not used at all, as such:

class MyCustomBackend(ModelBackend):

    def authenticate(
        self, 
        username: Optional[str] = None, 
        password: Optional[str] = None, 
        code: Optional[str] = None, 
        **kwargs: Any
    ) -> Optional[User]:
        return User.get_user_for_code(code)

How is that should be

The stubs for the ModelBackend should not override the parent class' method definition at all (it should not define it at all and simply inherit). This will be inline to how all authentication backends are used, and allow users to override with their own type definitions without extra work.

System information

  • OS: -
  • python version: 3.13.2
  • django version: 5.1.7
  • mypy version: 1.15.0
  • django-stubs version: 5.1.3
  • django-stubs-ext version: 5.1.3
@PedroPerpetua PedroPerpetua added the bug Something isn't working label Mar 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

1 participant