Recently I've been trying my hand at creating alternative sign on methods
for a django system and I've found the whole process fairly clean.
However I did reach bit of a time waste when my "code that should work,
doesn't" -- in my login view, I would `authenticate()` and `login()`
properly, but with a redirect response I would be an `AnonymousUser`
immediately after.
After two days of debugging and re-reading docs, I found that I missed out
a fairly critical sentence: "Authentication backends implements two
required methods". -- my authentication backend (of which I was replacing
the default) - did not implement `get_user()` so we would use the default
`BaseBackend.get_user()` which is to `return None`.
To me, it wasn't quite obvious why the authentication system needs to
implement get_user ( as i'd want to just get the user by pk like any
other) so this was a little bit of time wasting that I feel could be made
a bit more obvious. but I'm happy for other considerations.
Some ideas I had for changing this that might've saved time in the future:
* `BaseBackend` to implement a simple `get_user_model().objects.get(
_meta.pk=pk)` - seeming this is the default for most cases (as far as I
know?)
* `BaseBackend` to raise `NotImplemented` to force implementors to define
''these two required methods'' as that is what is mentioned in the docs
(https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#:~:text=implements%20two%20required%20methods)
* anyone requiring the failthrough approach so that one can auth and
get_user on different backends can just `pass` it
* something else
Happy for some thoughts /feedback / pushback. I just know that this was a
painpoint for me and it wasn't obvious where the `AnonymousUser` was
coming from. Perhaps it's just a documentation change.
--
Ticket URL: <https://code.djangoproject.com/ticket/34032>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
Old description:
New description:
Hi all,
Recently I've been trying my hand at creating alternative sign on methods
for a django system and I've found the whole process fairly clean.
However I did reach bit of a time waste when my "code that should work,
doesn't" -- in my login view, I would `authenticate()` and `login()`
properly, but with a redirect response I would be an `AnonymousUser`
immediately after.
After two days of debugging and re-reading docs, I found that I missed out
a fairly critical sentence: "Authentication backends implements two
required methods". -- my authentication backend (of which I was replacing
the default) - did not implement `get_user()` so we would use the default
`BaseBackend.get_user()` which is to `return None`.
To me, it wasn't quite obvious why the authentication system needs to
implement get_user ( as i'd want to just get the user by pk like any
other) so this was a little bit of time wasting that I feel could be made
a bit more obvious.
Some ideas:
* `BaseBackend` to implement a simple `get_user_model().objects.get(
_meta.pk=pk)` - seeming this is the default for most cases (as far as I
know?)
* `BaseBackend` to raise `NotImplemented` to force implementors to define
''these two required methods'' as that is what is mentioned in the docs
(https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#:~:text=implements%20two%20required%20methods)
* anyone requiring the failthrough approach so that one can auth and
get_user on different backends can just `pass` it
* something else.
Happy for some thoughts/feedback/pushback. This was just a painpoint for
me while developing.
Perhaps it needs to be highlighted in the documentation?
--
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:1>
* owner: nobody => piyushdivyankar1994
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:2>
Comment (by piyushdivyankar1994):
I have made the following changes
{{{
diff --git a/django/contrib/auth/backends.py
b/django/contrib/auth/backends.py
index 4adcf35051..b73d1f9f57 100644
--- a/django/contrib/auth/backends.py
+++ b/django/contrib/auth/backends.py
@@ -11,10 +11,12 @@ UserModel = get_user_model()
class BaseBackend:
def authenticate(self, request, **kwargs):
- return None
+ raise NotImplementedError(
+ "This .authenticate(self, request, **kwargs) must be
implemented."
+ )
def get_user(self, user_id):
- return None
+ raise NotImplementedError("This .get_user(self, ...) must be
implemented.")
def get_user_permissions(self, user_obj, obj=None):
return set()
}}}
I am unsure if this is what is required.
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:3>
Comment (by Vishal):
Replying to [comment:3 piyushdivyankar1994]:
> I have made the following changes
>
> {{{
> diff --git a/django/contrib/auth/backends.py
b/django/contrib/auth/backends.py
> index 4adcf35051..b73d1f9f57 100644
> --- a/django/contrib/auth/backends.py
> +++ b/django/contrib/auth/backends.py
> @@ -11,10 +11,12 @@ UserModel = get_user_model()
>
> class BaseBackend:
> def authenticate(self, request, **kwargs):
> - return None
> + raise NotImplementedError(
> + "This .authenticate(self, request, **kwargs) must be
implemented."
> + )
>
> def get_user(self, user_id):
> - return None
> + raise NotImplementedError("This .get_user(self, ...) must be
implemented.")
>
> def get_user_permissions(self, user_obj, obj=None):
> return set()
> }}}
>
> I am unsure if this is what is required.
Raised P.R https://github.com/django/django/pull/16086/
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:4>
* cc: Vishal (added)
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:5>
* status: assigned => closed
* type: Uncategorized => Cleanup/optimization
* has_patch: 1 => 0
* resolution: => wontfix
* easy: 1 => 0
Comment:
Thanks for the ticket, however `BaseBackend` methods return `None` as it's
the proper value for invalid credentials, see
[https://docs.djangoproject.com/en/stable/ref/contrib/auth/#django.contrib.auth.backends.BaseBackend
docs]:
> ''"A base class that provides default implementations for all required
methods. By default, it will reject any user and provide no
permissions."''
Unfortunately, both your propositions are backward incompatible and
against the current docs. Hope it makes sense.
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:6>
Comment (by Dre Westcook):
Replying to [comment:6 Mariusz Felisiak]:
> Thanks for the ticket, however `BaseBackend` methods return `None` as
it's the proper value for invalid credentials, see
[https://docs.djangoproject.com/en/stable/ref/contrib/auth/#django.contrib.auth.backends.BaseBackend
docs]:
>
> > ''"A base class that provides default implementations for all required
methods. By default, it will reject any user and provide no
permissions."''
>
> Unfortunately, both your propositions are backward incompatible and
against the current docs. Hope it makes sense.
Hey thanks for the response. I wanted to have more of a discussion, I
wasn't ready to put it in code or even if that was the solution.
my point is the docs seem very unclear, and **rely on 3 small words**''.
Here's some better suggestions, to improve documentation rather than
changing code:
* in the docs that you link: `BaseBackend` should mention explicitly that
`authenticate()` and `get_user()` return none, in the same "list" style as
`get_user_permissions()` and `get_group_permissions` is described.
https://docs.djangoproject.com/en/4.1/ref/contrib/auth/#django.contrib.auth.backends.BaseBackend
* in the **Writing an authentication backend** , the code example should
include the `get_user` definition.
[https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#writing-
an-authentication-backend].
The point is that the only time it's mentioned that a backend needs to
implement both methods is in that first sentence. Every other code example
and explanation does not indicate that both are needed.
Hope that makes sense.
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:7>
Comment (by Mariusz Felisiak):
Dre, thanks. Docs improvements are always welcome.
--
Ticket URL: <https://code.djangoproject.com/ticket/34032#comment:8>