I hope you can help me figure the best way to implement a manual (server-side initiated) login without using the password. Let me explain the workflow:
- User registers
- Thank you! An email with an activation link has been sent blablabla
- (Account now exists but is marked not enabled)
- User opens email, clicks link
- (Account is enabled)
- Thank you! You can now use the site
What I'm trying to do is log in the user after he has clicked the email link so he can start using the website right away.
I can't use his password since it's encrypted in the DB, is the only option writing a custom authentication backend?
Daniel's answer is very good.
Another way to do it is to create a HashModelBackend following the Custom Authorization backends https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend like this:
And then install this in your settings:
Then your view would be something like this:
Response to dan's answer.
A way to write your backend:
Answer is based on django.contrib.auth.backends.ModelBackend source code. It's actual for django 1.9
And I would rather place custom backend below django's default:
because account activation is less possible than login itself. According to https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#specifying-authentication-backends:
Be careful this code will authenticate your users even with incorrect passwords.
You can use
ska
package, which has password-less login to Django implemented.ska
works with authentication tokens and its security is based on SHARED_KEY which should be equal for all parties (servers) involved.On client side (party that requests a password-less login), you generate a URL and sign it, using
ska
. Example:Default lifetime of the token is 600 seconds. You can customise that by proving a
lifetime
argument.On the server side (site to which users' log in), having in mind that you have installed
ska
properly, the user is logged in upon visiting the URL if they existed (username match), or otherwise - created. There are 3 callbacks that you can customise in your project's Django settings.USER_GET_CALLBACK
(string): Fired if user was successfully fetched from database (existing user).USER_CREATE_CALLBACK
(string): Fired right after user has been created (user didn't exist).USER_INFO_CALLBACK
(string): Fired upon successful authentication.See the documentation (http://pythonhosted.org/ska/) for more.
As of Django 1.10, the process has been simplified.
In all versions of Django, in order for a user to be logged in, they must be authenticated by one of your app's backends (controlled by the
AUTHENTICATION_BACKENDS
setting).If you simply want to force a login, you can just claim that the user was authenticated by the first backend from that list:
You don't need a password to log a user in. The
auth.login
function just takes aUser
object, which you are presumably already getting from the database when you enable the account. So you can pass that straight tologin
.Of course, you'll need to be very careful that there's no way a user can spoof a link to an existing already-enabled account, which would then automatically log them in as that user.
... etc.
Edited:
Hmm, didn't notice that requirement to use
authenticate
because of the extra property it adds. Looking at the code, all it does is abackend
attribute equivalent to the module path of the authenticating backend. So you could just fake it - before the login call above, do this: