No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de Flask

Curso de Flask

Bernardo Cassina

Bernardo Cassina

Autenticación de usuarios: Logout

30/36
Recursos

¿Cómo se implementa el sistema de login y logout en una aplicación Flask?

Implementar un sistema de autenticación es crucial para gestionar la seguridad de los usuarios en cualquier aplicación web. ¿Te has preguntado cómo diseñar un sistema de login y logout desde cero con Flask? Aquí exploramos cómo lograrlo paso a paso, integrando verificaciones esenciales y datos específicos de usuario.

¿Cómo modificar la ruta de login?

Para construir una funcionalidad de login robusta, es vital asegurar que el usuario que intenta ingresar realmente existe en la base de datos y que la contraseña proporcionada es la correcta. A continuación, el procedimiento detallado:

  1. Recuperar y validar datos del usuario:

    • Extrae el username y password del formulario de inicio de sesión.
    • Busca el usuario en la base de datos utilizando un método importado (getUser desde Firestore Service).
    • Verifica que el documento del usuario no sea vacío y conviértelo en un diccionario.
  2. Verificar la contraseña del usuario:

    • Obtén la contraseña almacenada en la base de datos.
    • Compara la contraseña ingresada con la almacenada.
    • Si coinciden, procede a crear un userModel y a loggearlo utilizando el método loginUser.
# Pseudocódigo para el login
userDoc = getUser(username)
if userDoc.toDict() is not None:
    stored_password = userDoc.toDict()['password']
    if password == stored_password:
        userData = userData(username=username, password=password)
        userModel = userModel(userData)
        loginUser(userModel)
  1. Redireccionar y notificar al usuario:
    • Dirige al usuario a una página de bienvenida.
    • Usa flash para mostrar un mensaje de confirmación o error.

¿Qué hacer cuando el usuario no existe o la información es incorrecta?

El manejo de error es igualmente crucial en el proceso:

  • Usuario inexistente: Si no se encuentra al usuario en el sistema, flashea un mensaje de error indicando que el usuario no existe.
  • Contraseña incorrecta: Indica al usuario que la contraseña no coincide, ayudando a identificar errores específicos y garantizando una buena experiencia de usuario.

¿Cómo implementar la funcionalidad de logout?

El logout o cierre de sesión es necesario para mantener la seguridad de las cuentas. Aquí te mostramos cómo realizarlo en Flask:

  1. Crear una función de logout:

    • Utiliza flask_login para importar logout_user.
    • Crea una ruta protegida por login_required para asegurarse de que solo los usuarios autenticados puedan ejecutar el logout.
  2. Implementar el flujo de logout:

    • Llama al método logout_user sin parámetros. Flask automaticamente identificará al usuario en sesión.
    • Muestra un mensaje de “regresa pronto” y redirige a la página de login.
# Estructura básica de logout
@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash("Regresa pronto")
    return redirect(url_for('auth.login'))

¿Cómo personalizar la navegación para usuarios autenticados?

Para una interfaz amigable, es útil que la barra de navegación muestre opciones distintas según si un usuario está autenticado o no:

  • Usuarios autenticados: Ofrece un link directo a logout.
  • Usuarios no autenticados: Solo muestra la opción de login.
<nav>
  {% if current_user.is_authenticated %}
    <a href="{{ url_for('auth.logout') }}">Logout</a>
  {% else %}
    <a href="{{ url_for('auth.login') }}">Login</a>
  {% endif %}
</nav>

Con estos pasos, el sistema de autenticación estará más completo, seguro y fácil de usar. Ahora estás listo para afrontar el reto de implementar el registro de usuarios, un paso esencial para cualquier aplicación dinámica. Mantén el entusiasmo y sigue aprendiendo para optimizar tus habilidades de desarrollo en Flask.

Aportes 15

Preguntas 9

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Podemos agregar estas lineas en la funcion de login para devolver al usuario a hello si ya esta logueado para que no puede ingresar de nuevo a la pagina de login una vez logueado

    if current_user.is_authenticated:
        return redirect(url_for('index'))```

Cuesta llegar hasta aquí. La vaina funciona, no sé muy bien como, pero funciona.

Que tal cambiar

if user_dic.to_dict() is not None:

por

if user_dic.to_dict():

Sanes si tendría alguna implicación?

Tuve que actualizar dos tests.
El get de hello:

    def test_hello_get(self):
        response = self.client.get(url_for('hello'))

        self.assert200(response)

Lo cambié para que se aseguré que me devuelve un status_code de redirección:

  def test_hello_login_required(self):
        response = self.client.get(url_for('hello'))

        self.assertEquals(response.status_code, 302)

Y el de POST sobre la ruta del login:

    def test_auth_login_post(self):
        fake_form = {
            'username': 'fake',
            'password': 'fake-pass'
        }
        
        response = self.client.post(url_for('auth.login'), data=fake_form)
        self.assertRedirects(response, url_for('index'))

Lo cambié por este:

    def test_auth_login_post(self):
        fake_form = {
            'username': 'emmanuel',
            'password': '12345678'
        }
        
        response = self.client.post(url_for('auth.login'), data=fake_form)
        self.assertRedirects(response, url_for('hello'))
        response = self.client.get(url_for('hello'))        
        self.assert200(response)

Que se asegura que redirija a hello y que se puede entrar a hello una vez logeado. Lo malo es que ahora depende de que exista ese usuario en la base de datos que utilizo. Supongo que si se configura un ambiente de CI/CD también se tiene que crear una base de datos para desarrollo en la nube.

me encanta flask, de verdad jajaja

Ahora, al crear un usuario nuevo, hacer logout y poner la misma clave ya no te permite ingresar, esto es porque compruebas un password hasheado con el que mandas el cual no es hasheado, para solucionar esto importas donde werkzeug. security el check password y queda así

from werkzeug.security import generate_password_hash, check_password_hash #para comprobar los hashes

#En login cambiaría password == password_from_db
@auth.route('/login', methods=['GET', 'POST'])
def login():
    login_form = LoginForm()
    context = {
        'login_form': login_form
    }

    if login_form.validate_on_submit():
        username = login_form.username.data
        password = login_form.password.data

        user_doc = get_user(username)

        if user_doc.to_dict() is not None:
            password_from_db = user_doc.to_dict()['password']

            #if password == password_from_db:
	      if check_password_hash(password_from_db, password):
                user_data = UserData(username, password)
                user = UserModel(user_data)

                login_user(user)

                flash('Bienvenido de nuevo')

                redirect(url_for('hello'))
            else:
                flash('La informaición no coincide')
        else:
            flash('El usario no existe')

        return redirect(url_for('index'))

    return render_template('login.html', **context)

Y ahora si cuando el nuevo usuario quiere ingresar ya le permite, aunque los primeros que guardamos sin los hashes ya no serían accesibles

Hasta lo unico que no me ha gustado tanto de los entornos virtuales, es que a veces se hay que reiniciar el entorno para que funcione o se reflejen los cambios

por a alguien le sirve, ya que vi que muchos les pasa lo mismo, a mi me mandaba a la pagina de login después haber puesto el usuario y password, lo que estuve revisando es que tenia un código repetido en **init**.py de app, en concreto el @login\_manager.userloader, no sé en que momento lo hice dos veces, al quitarlo ya pude entrar sin problema

Despues de hacer el login no me redirige a Hello, simplemente me pone:
“Bienvenido de nuev”
“Please login in to access this page”

Y me aparece de nuevo el login_form

me ayudan por favor? mi codigo es diferente al del profesor porque estoy siguiendo con base de datos sql

@auth.route('/login', methods=['GET', 'POST'])
def login():
    login_form = Form()
    context = {
        'login_form' : login_form
    }
    if login_form.validate_on_submit():
        username = login_form.username.data
        password = login_form.password.data
        user_db = Users.query.filter_by(username=username).first()

        if user_db is not None:
            if password == user_db.password:
                session['id_user'] = user_db.id_user
                session['name'] = user_db.name

                flash('Bienvenido de nuevo!')

                redirect(url_for('hello'))
            else: 
Alguien tiene este error: Traceback (most recent call last): File "C:\Users\rguti\Desktop\Python\flask\main.py", line 5, in \<module> from flask\_login import login\_required, current\_user File "C:\Users\rguti\Desktop\Python\flask\venv\Lib\site-packages\flask\_login\\\_\_init\_\_.py", line 12, in \<module> from .login\_manager import LoginManager File "C:\Users\rguti\Desktop\Python\flask\venv\Lib\site-packages\flask\_login\login\_manager.py", line 33, in \<module> from .utils import \_create\_identifier File "C:\Users\rguti\Desktop\Python\flask\venv\Lib\site-packages\flask\_login\utils.py", line 14, in \<module> from werkzeug.urls import url\_decode ImportError: cannot import name 'url\_decode' from 'werkzeug.urls' (C:\Users\rguti\Desktop\Python\flask\venv\Lib\site-packages\werkzeug\urls.py) No encuentro la solución

Tuve problemas con la verificación del user en el login, por lo que busque en la documentación firebase y encontré otra sintaxis, al momento de escribir esto estoy usando Python 3.10, espero les sirva por si tienen el mismo problema. Este seria el código a modificar en views en el método login():

if login_form.validate_on_submit():
        username = login_form.username.data 
        password = login_form.password.data

        user_ref = get_user(username)
        user_doc = user_ref.get()
        if user_doc.exists:
            password_from_db = user_doc.to_dict()['password']
            
#el resto es igual
'Flask' object has no attribute 'login_manager'```

interesante😅

Dentro del modelo de usuario que creamos, el que usamos con el decorador @login_manager.user_loader para cargar el current_user, podemos guardar atributos y metodos para ser usados solamente cuando tenemos un usuario logueado correctamente.

Tuve muchos errores con import cíclicos de Python. Al fina lo solucioné inicializando mi base de datos en mi archivo db_service desde el init del módulo. Así no requería que se iniciara antes.