Manejo de sesiones con Flask-Login

Publicado por Andrea Navarro en

En este artículo veremos como manejar sesiones en una aplicación Flask utilizando la extensión Flask-Login. También exploraremos los pasos necesarios para proteger rutas en nuestra aplicación.

Flask-Login es una extensión de Flask que permite el manejo de sesiones. Se adapta a diferentes tipos de autenticación y no está restringido al uso de usuario-contraseña haciéndolo lo suficientemente flexible para adaptarse a distintos tipos de proyectos incluyendo aquellos que utilicen métodos de autenticación externos.

Flask-Login cuenta con las siguientes características:

  • Manejo de Login: La ID del usuario es almacenada en la sesión permitiendo la identificación del usuario para la restricción de acceso a rutas
  • Manejo de Logout
  • Restricción de rutas: Permite la restricción de rutas dependiendo de la identidad del usuario y si tiene una sesión iniciada
  • Almacenamiento no predeterminado: Es posible configurar cómo se obtiene la identidad del usuario (Base de datos, Token, etc)
  • Método de autenticación no predeterminado: Es posible configurar cualquier tipo de autenticación (usuario-contraseña, JWT, OpenID, etc)

Instalación de Flask-Login

Es posible instalar Flask-Login utilizando pip

pip3 install flask-login

Configuración

Para utilizar Flask-Login es necesario inicializar la clase LoginManager que será encargada de
manejar la funciones de sesión. Al inicializarla se pasa como argumento la variable que contiene la aplicación de Flask.

app = Flask(__name_)  
login_manager = LoginManager(app)

Para que el manejo de sesiones funcione correctamente es necesario que la aplicación tenga cargada
la clave secreta.

app.secret_key = “clave_secreta”

Clase de usuario

Para poder manejar la sesión de usuario debe crearse una clase que representará al usuario. Flask-Login requiere que la clase de usuario implemente los siguientes métodos y propiedades.

is_authenticatedEsta propiedad debe devolver True si el usuario está autenticado y False de lo contrario
is_activeEsta propiedad debe devolver True si el usuario está activo. Esto dependerá de las condiciones programadas en la aplicación (activación de cuenta, suspensión de cuenta, etc)
is_anonymousEsta propiedad debe devolver True si el usuario en anónimo. Los usuarios reales siempre devolverán False.
get_id()Este método debe devolver un valor unicode que identifique al usuario.

En vez de implementar manualmente estos métodos es posible hacer que la clase de usuario herede de la clase UserMixin que tiene estos métodos definidos.

class Usuario(UserMixin):

Carga de usuario

Para indicar a LoginManager cómo obtener el usuario a partir del ID guardado en la sesión debe crearse la función load_user. Existen dos decoradores que se utilizarán dependiendo del método de autenticación.

Se utiliza el decorador user_loader para recargar el usuario desde la sesión. La función tomará el ID de guardado en la sesión y devolverá el objeto Usuario o None si el usuario no existe. En el siguiente ejemplo se busca el usuario en la base de datos a partir del ID de la sesión.

@login_manager.user_loader
def load_user(usuario_id):
   #Buscar en BD y cargar atributos
    return Usuario.get(usuario_id)

Si en cambio los datos del usuario utilizando una request de Flask se utiliza el decorador request_loader. La función debe tomar como entrada una request y devolverá el objeto Usuario o None si el usuario no existe. En el siguiente caso se obtienen los datos del usuario de un JWT.

@login_manager.request_loader
def load_user(request):
    if 'access_token' in request.cookies:
           #Decodificar el token
            decoded = jwt.decode(request.cookies['access_token'], current_app.config["SECRET_KEY"])
            #Cargar datos del usuario
            user = Usuario(decoded["email"],decoded["nombre"])
            #Devolver usuario 
            return user
     return None

Inicio de sesión

Para realizar el login de un usuario se debe pasar a la función login_user el objeto de la clase de usuario que se quiere cargar y como segundo parámetro si se quiere que se recuerde la sesión. Previo a este paso se debe haber realizado el proceso de autenticación según el método elegido (verificar usuario y contraseña, verificar token, obtener confirmación de método de autenticación externo, etc)

usuario = Usuario(id =5, nombre ="Pedro" )
login_user(usuario)

Una vez realizado este proceso la sesión se iniciará con el ID del usuario cargado junto con todos los datos de la clase.

Cierre de sesión

Para cerrar la sesión abierta se utiliza la función logout_user.

logout_user()

Obtener datos de sesión

Para obtener los datos de una sesión o verificar si la sesión ha sido creada se puede utilizar el proxy current_user. Dentro de un template es posible mostrar los atributos correspondientes al usuario que ha iniciado sesión o acceder a sus métodos.

Hola{{ current_user.nombre }}!

Ya que la clase usuario hereda de UserMixin es posible acceder a los métodos para verificar que el usuario no sea anónimo y restringir el código renderizado si no lo está. De esta manera es posible mostrar solo aquellos enlaces, botones o contenido requerido para cada tipo de usuario.

{% if current_user.is_authenticated %}
           Hola{{ current_user.nombre }}!
{% endif %}

Cuando ningún usuario ha iniciado sesión current_user contiene un objeto de tipo
AnonymousUserMixin. Este objeto tiene implementados los mismos métodos y propiedades que la
clase UserMixin por lo que el método is_authenticated está implementado y tendrá un valor de False.

Un caso típico es mostrar el enlace de login o formulario de login a usuarios no logueados y el enlace de logout a los que han iniciado sesión.

{% if current_user.is_authenticated %}
					<a href="{{ url_for('main.logout') }}">Logout</a>
{% else %}	
		           <a href="{{ url_for('main.login') }}">Login</a>
{% endif %}

Restricción de rutas

Para restringir el acceso a las rutas que requieren que el usuario haya iniciado sesión para accederlas se utiliza el decorador login_required sobre la ruta.

@app.route('/usuario')
@login_required
def usuario():
    return render_template('usuario.html')

Cuando un usuario intenta ingresar a una vista que tiene el decorador login_required sin haber
iniciado sesión por defecto Flask mostrará un mensaje de error y abortará con un error 401. Para
personalizar este comportamiento puede utilizarse el decorador unauthorized_handler.

El el siguiente ejemplo el usuario será redireccionado a la página que contiene el formulario de login y un mensaje de advertencia se mostrará en su pantalla.

@login_manager.unauthorized_handler
def unauthorized_callback():
    flash('Debe iniciar sesión para continuar.')
    return redirect(url_for('login'))

En este artículo hemos visto como crear, cerrar y acceder a datos de sesiones en Flask utilizando la extensión Flask-Login. Espero que les sea de utilidad!


¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!


Andrea Navarro

- Ingeniera en Informática - Docente universitaria - Investigadora