29

I'm using the default authentication system with django, but I've added on an OpenID library, where I can authenticate users via OpenID. What I'd like to do is log them in, but it seems using the default django auth system, I need their password to authenticate the user. Is there a way to get around this without actually using their password?

I'd like to do something like this...

user = ... # queried the user based on the OpenID response
user = authenticate(user) # function actually requires a username and password
login(user)

I sooner just leave off the authenticate function, but it attaches a backend field, which is required by login.

voodoogiant
  • 2,118
  • 6
  • 29
  • 49
  • Possible duplicate of [Manually logging in a user without password](http://stackoverflow.com/questions/2787650/manually-logging-in-a-user-without-password). – easoncxz Jul 10 '14 at 06:02

4 Answers4

28

It's straightforward to write a custom authentication backend for this. If you create yourapp/auth_backend.py with the following contents:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User


class PasswordlessAuthBackend(ModelBackend):
    """Log in to Django without providing a password.

    """
    def authenticate(self, username=None):
        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Then add to your settings.py:

AUTHENTICATION_BACKENDS = (
    # ... your other backends
    'yourapp.auth_backend.PasswordlessAuthBackend',
)

In your view, you can now call authenticate without a password:

user = authenticate(username=user.username)
login(request, user)
Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
  • 13
    This works great but beware - if you want to use regular auth in some situations, as well as no-password auth in others, be sure to prevent your new backend causing every attempt with a valid username to succeed - remember that all backends are tried when authenticate() is called. In mine I require a special token argument to be included to ensure that the caller of authenticate() really wanted no-password auth to work. – Richard May 12 '14 at 09:46
  • @Richard How you implemented regular auth and no password auth. I am trying to implement that but it is calling regular auth method. – Sanket Patel Mar 08 '19 at 06:02
  • Above code is not working with Django 2.1. It is not calling authenticate method of PasswordlessAuthBackend class. But it worls with Django 2.0 – Sanket Patel Mar 08 '19 at 07:41
  • @SanketPatel I suspect that in newer Django versions the `def authenticate` must take a `request` parameter, as in [another answer for this question](https://stackoverflow.com/a/55217797/263061). – nh2 Dec 26 '20 at 03:06
  • @SanketPatel More info in https://stackoverflow.com/a/54370236/263061. – nh2 Dec 26 '20 at 03:19
8

This is a bit of a hack but if you don't want to rewrite a bunch of stuff remove the authenticate

user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)

user would be your User object

Mike
  • 7,769
  • 13
  • 57
  • 81
  • 4
    That is one possible solution, but that doesn't get stored in the session, so if you open a new tab and go to the site, you have to log in again. – voodoogiant Jul 06 '11 at 05:24
  • 1
    This WILL be saved in the session, and work in subsequent views. The only gotcha is that the backend you set must be included in the AUTHENTICATION_BACKENDS setting. I wish I could undo my downvote, but I can't. – Emil Stenström Apr 29 '15 at 14:13
  • @voodoogiant, I met exactly the same issue, that is the session is not stored. But weird enough, the first 2 XHR requests could get session data from session-id correctly. But the later XHR request does not, and Django server set session-id in cookie to blank string in response. Do you know why? and how to solve that? – Jcyrss Jun 04 '16 at 14:02
7

In order to do authenticate without password, in your settings.py:

AUTHENTICATION_BACKENDS = [
# auth_backend.py implementing Class YourAuth inside yourapp folder
    'yourapp.auth_backend.YourAuth', 
# Default authentication of Django
    'django.contrib.auth.backends.ModelBackend',
]

In your auth_backend.py:

NOTE: If you have custom model for your app then import from .models CustomUser

from .models import User 
from django.conf import settings

# requires to define two functions authenticate and get_user

class YourAuth:  

    def authenticate(self, request, username=None):
        try:
            user = User.objects.get(username=username)
            return user
        except User.DoesNotExist:
            return None
        
    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

In your Views for custom login request:

# Your Logic to login user
userName = authenticate(request, username=uid)
login(request, userName)

For further reference, use the django documentation here.

Olivia Stork
  • 4,660
  • 5
  • 27
  • 40
2

You can easily fix this by creating your own authentication backend and adding it to the AUTHENTICATION_BACKENDS setting.

There are some OpenID backends available already, so with a bit of searching you could save yourself the trouble of writing one.

Wolph
  • 78,177
  • 11
  • 137
  • 148
  • I've had problems with django OpenID backends as they weren't compatible with Google's unique way of doing OpenID. Anyway, like I said I already have a password-based backend and I just want to use OpenID in some cases--not switch to a strict OpenID backend. – voodoogiant Jul 04 '11 at 04:20
  • @voodoogiant: depending on the keyword arguments the authentication backend is chosen. So if you use `openid_token` for your OpenID backend and `username` with `password` for your normal authentication system than they will both work. – Wolph Jul 05 '11 at 14:41