web-dev-qa-db-fra.com

Connexion manuelle d'un utilisateur sans mot de passe

J'espère que vous pourrez m'aider à trouver la meilleure façon d'implémenter une connexion manuelle (initiée côté serveur) sans en utilisant le mot de passe. Permettez-moi d'expliquer le flux de travail:

  • Registres d'utilisateurs
  • Je vous remercie! Un e-mail avec un lien d'activation a été envoyé blablabla
  • (Le compte existe maintenant mais est marqué comme non activé)
  • L'utilisateur ouvre un e-mail, clique sur le lien
  • (Le compte est activé)
  • Je vous remercie! Vous pouvez maintenant utiliser le site

Ce que j'essaie de faire, c'est de me connecter à l'utilisateur après avoir cliqué sur le lien de messagerie afin qu'il puisse commencer à utiliser le site Web immédiatement.

Je ne peux pas utiliser son mot de passe car il est crypté dans la base de données, la seule option est-elle d'écrire un backend d'authentification personnalisé?

65
Agos

Vous n'avez pas besoin d'un mot de passe pour vous connecter. Le auth.login function prend simplement un objet User, que vous obtenez probablement déjà de la base de données lorsque vous activez le compte. Vous pouvez donc le transmettre directement à login.

Bien sûr, vous devrez faire très attention à ce qu'aucun utilisateur ne puisse usurper un lien vers un compte existant déjà activé, qui serait alors connectez-les automatiquement en tant qu'utilisateur.

from Django.contrib.auth import login

def activate_account(request, hash):
    account = get_account_from_hash(hash)
    if not account.is_active:
        account.activate()
        account.save()
        user = account.user
        login(request, user)

... etc.

Modifié:

Hmm, je n'ai pas remarqué cette exigence d'utiliser authenticate en raison de la propriété supplémentaire qu'il ajoute. En regardant le code, tout ce qu'il fait est un attribut backend équivalent au chemin du module du backend d'authentification. Donc, vous pouvez simplement le simuler - avant l'appel de connexion ci-dessus, procédez comme suit:

user.backend = 'Django.contrib.auth.backends.ModelBackend'
88
Daniel Roseman

Depuis Django 1.10, le processus a été simplifié.

Dans toutes les versions de Django, pour qu'un utilisateur soit connecté, il doit être authentifié par l'un des backends de votre application (contrôlé par le AUTHENTICATION_BACKENDS réglage).

Si vous souhaitez simplement forcer une connexion, vous pouvez simplement affirmer que l'utilisateur a été authentifié par le premier backend de cette liste:

from Django.conf import settings
from Django.contrib.auth import login


# Django 1.10+
login(request, user, backend=settings.AUTHENTICATION_BACKENDS[0])

# Django <1.10 -  fake the authenticate() call
user.backend = settings.AUTHENTICATION_BACKENDS[0]
login(request, user)
25
Ian Clark

La réponse de Daniel est très bonne.

Une autre façon de le faire est de créer un HashModelBackend en suivant les backends d'autorisation personnalisée https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend comme ça:

class HashModelBackend(object):
    def authenticate(self, hash=None):
        user = get_user_from_hash(hash)
        return user

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

Et puis installez ceci dans vos paramètres:

AUTHENTICATION_BACKENDS = (
    'myproject.backends.HashModelBackend',
    'Django.contrib.auth.backends.ModelBackend',
)

Ensuite, votre vue serait quelque chose comme ceci:

def activate_account(request, hash):
    user = authenticate(hash=hash)
    if user:
        # check if user is_active, and any other checks
        login(request, user)
    else:
        return user_not_found_bad_hash_message
25
dar

Vous pouvez utiliser le package ska, qui a une connexion sans mot de passe à Django implémenté. ska fonctionne avec des jetons d'authentification et sa sécurité est basée sur SHARED_KEY qui devrait être égal pour toutes les parties (serveurs) impliquées.

Côté client (partie qui demande une connexion sans mot de passe), vous générez une URL et la signez, à l'aide de ska. Exemple:

from ska import sign_url
from ska.contrib.Django.ska.settings import SECRET_KEY

server_ska_login_url = 'https://server-url.com/ska/login/'

signed_url = sign_url(
    auth_user='test_ska_user_0',
    secret_key=SECRET_KEY,
    url=server_ska_login_url
    extra={
        'email': '[email protected]',
        'first_name': 'John',
        'last_name': 'Doe',
    }
)

La durée de vie par défaut du jeton est de 600 secondes. Vous pouvez personnaliser cela en prouvant un argument lifetime.

Côté serveur (site auquel les utilisateurs se connectent), sachant que vous avez correctement installé ska, l'utilisateur est connecté en visitant l'URL si elle existait (correspondance de nom d'utilisateur), ou autrement - créée . Il existe 3 rappels que vous pouvez personnaliser dans les paramètres Django de Django).

  • USER_GET_CALLBACK (chaîne): déclenché si l'utilisateur a été récupéré avec succès à partir de la base de données (utilisateur existant).
  • USER_CREATE_CALLBACK (chaîne): tiré juste après la création de l'utilisateur (l'utilisateur n'existait pas).
  • USER_INFO_CALLBACK (chaîne): déclenché en cas d'authentification réussie.

Voir la documentation ( http://pythonhosted.org/ska/ ) pour plus.

2
Artur Barseghyan

Réponse à la réponse de dan .

Une façon d'écrire votre backend:

from Django.contrib.auth import get_user_model
from Django.contrib.auth.backends import ModelBackend

class HashModelBackend(ModelBackend):

def authenticate(self, username=None, **kwargs):
    UserModel = get_user_model()
    if username is None:
        username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
        user = UserModel._default_manager.get_by_natural_key(username)
        return user
    except UserModel.DoesNotExist:
        return None

La réponse est basée sur le code source de Django.contrib.auth.backends.ModelBackend . C'est réel pour Django 1.9

Et je préfère placer un backend personnalisé en dessous de la valeur par défaut de Django:

AUTHENTICATION_BACKENDS = [
    'Django.contrib.auth.backends.ModelBackend',
    'yours.HashModelBackend',
]

car l'activation du compte est moins possible que la connexion elle-même. Selon https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#specifying-authentication-backends :

L'ordre des AUTHENTICATION_BACKENDS est important, donc si le même nom d'utilisateur et le même mot de passe sont valides dans plusieurs backends, Django arrêtera le traitement lors de la première correspondance positive.

Attention ce code authentifiera vos utilisateurs même avec des mots de passe incorrects.