Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🔐
Intermediaire 25 min Flask

Authentification dans Flask

Authentification dans Flask

Pourquoi Authentification dans Flask ?

L'authentification est un élément crucial de toute application Web, que ce soit une simple plateforme de gestion des tâches ou une grande entreprise. Elle permet de contrôler qui accède aux ressources et les fonctionnalités de l'application. Dans le contexte professionnel, un développeur a besoin d'authentification pour :

  • Protéger les données sensibles
  • Assurer la sécurité des utilisateurs
  • Gérer les droits d'accès et les permissions

Un cas concret est une application de gestion de projet où chaque utilisateur doit être authentifié avant de pouvoir consulter, modifier ou ajouter des tâches. L'authentification permet de s'assurer que seuls les membres autorisés peuvent effectuer ces opérations.

Prerequis

  • Connaissance de base de Flask
  • Connaissances en Python
  • Installation d'un environnement de développement (Python, pip)
  • Un éditeur de texte ou un IDE

Concepts fondamentaux

1. Utilisateurs et Mot de Passe

Chaque utilisateur a besoin d'un nom d'utilisateur et d'un mot de passe pour s'authentifier. Flask utilise des extensions comme flask-security qui gèrent automatiquement les utilisateurs et les mots de passe.

from flask import Flask
from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SECURITY_PASSWORD_SALT'] = 'your_secret_salt'

db = SQLAlchemy(app)

class Role(db.Model, RoleMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    roles = db.relationship('Role', secondary='roles_users',
                            backref=db.backref('users', lazy='dynamic'))

roles_users = db.Table('roles_users',
                       db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
                       db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)

2. Gestion des Sessions

Flask utilise les sessions pour stocker les informations de l'utilisateur une fois qu'il est connecté. Les sessions sont sécurisées avec un secret clé.

app.secret_key = 'your_secret_key'

3. Routes Protégées

Pour certaines routes, il faut s'assurer que l'utilisateur est connecté avant d'accéder à ces ressources. Flask-Login facilite ce processus.

from flask_login import LoginManager, login_required, current_user

login_manager = LoginManager()
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.route('/dashboard')
@login_required
def dashboard():
    return f"Welcome {current_user.email}"

Mise en pratique : projet fil rouge

Nous allons créer un mini-projet complet : une application de gestion des tâches. L'application permettra aux utilisateurs de s'enregistrer, se connecter et ajouter/modifier/supprimer des tâches.

Étape 1 : Installation des dépendances

pip install Flask Flask-SQLAlchemy Flask-Login Flask-Security

Étape 2 : Structure du projet

task_manager/
├── app.py
├── models.py
├── templates/
│   ├── base.html
│   ├── register.html
│   ├── login.html
│   └── dashboard.html
└── static/
    └── style.css

Étape 3 : Configuration de l'application

## app.py
from flask import Flask, render_template, redirect, url_for, request
from models import db, User, Role, user_datastore
from flask_security import Security, login_required, current_user

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SECURITY_PASSWORD_SALT'] = 'your_secret_salt'

db.init_app(app)
security = Security(app, user_datastore)

@app.route('/')
def index():
    return redirect(url_for('dashboard'))

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def login():
    if request.method == 'POST':
        email = request.form['email']
        password = request.form['password']
        user = User.query.filter_by(email=email).first()
        if user and user.password == password:
            return redirect(url_for('dashboard'))
        else:
            return "Invalid credentials"
    return render_template('login.html')

@app.route('/dashboard')
@login_required
def dashboard():
    return render_template('dashboard.html', user=current_user)

Étape 4 : Création des modèles

## models.py
from flask_sqlalchemy import SQLAlchemy
from flask_security import UserMixin, RoleMixin

db = SQLAlchemy()

class Role(db.Model, RoleMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    roles = db.relationship('Role', secondary='roles_users',
                            backref=db.backref('users', lazy='dynamic'))

roles_users = db.Table('roles_users',
                       db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
                       db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

Étape 5 : Création des templates

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Task Manager</title>
    <link rel="stylesheet" href="url_for('static', filename='style.css')">
</head>
<body>
    {% block content %}{% endblock %}
</body>
</html>

<!-- templates/register.html -->
{% extends 'base.html' %}
{% block content %}
<h2>Register</h2>
<form method="post">
    <input type="email" name="email" placeholder="Email" required>
    <input type="password" name="password" placeholder="Password" required>
    <button type="submit">Register</button>
</form>
{% endblock %}

<!-- templates/login.html -->
{% extends 'base.html' %}
{% block content %}
<h2>Login</h2>
<form method="post">
    <input type="email" name="email" placeholder="Email" required>
    <input type="password" name="password" placeholder="Password" required>
    <button type="submit">Login</button>
</form>
{% endblock %}

<!-- templates/dashboard.html -->
{% extends 'base.html' %}
{% block content %}
<h2>Welcome user.email</h2>
<a href="/logout">Logout</a>
{% endblock %}

Erreurs frequentes et debugging

1. Erreur : UnboundLocalError: local variable 'user_datastore' referenced before assignment

Code incorrect :

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

Code correct :

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

2. Erreur : AttributeError: 'User' object has no attribute 'password_hash'

Code incorrect :

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

Code correct :

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

3. Erreur : TypeError: create_user() missing 1 required positional argument: 'email'

Code incorrect :

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

Code correct :

@app.route('/register', methods=['GET', 'POST'])
@security.auth_required('anonymous')
def register():
    if request.method == 'POST':
        user_data = {
            'email': request.form['email'],
            'password': request.form['password']
        }
        try:
            user_datastore.create_user(**user_data)
            db.session.commit()
            return redirect(url_for('login'))
        except Exception as e:
            print(e)
    return render_template('register.html')

Pour aller plus loin

  1. Gestion des rôles et permissions : Ajoutez différents rôles (admin, user) avec différentes permissions.
  2. Intégration d'OAuth : Permettez à vos utilisateurs de s'authentifier avec leurs comptes Google, Facebook, etc.
  3. Email confirmation : Demandez une confirmation email lors de l'enregistrement pour vérifier que l'email est bien le sien.

Défi pratique

Créez un petit script CLI qui permet d'ajouter un utilisateur et de lui attribuer un rôle. Utilisez flask-script ou click.

## cli.py
from flask import Flask, render_template, redirect, url_for, request
from models import db, User, Role, user_datastore

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SECURITY_PASSWORD_SALT'] = 'your_secret_salt'

db.init_app(app)

@app.route('/add_user', methods=['GET', 'POST'])
def add_user():
    if request.method == 'POST':
        email = request.form['email']
        password = request.form['password']
        role_name = request.form['role']
        user_datastore.create_user(email=email, password=password)
        role = Role.query.filter_by(name=role_name).first()
        if role:
            user_datastore.add_role_to_user(user_datastore.find_user(email=email), role)
        db.session.commit()
        return redirect(url_for('index'))
    return render_template('add_user.html')

if __name__ == '__main__':
    app.run(debug=True)

Ajoutez un template templates/add_user.html pour le formulaire.

<!-- templates/add_user.html -->
{% extends 'base.html' %}
{% block content %}
<h2>Add User</h2>
<form method="post">
    <input type="email" name="email" placeholder="Email" required>
    <input type="password" name="password" placeholder="Password" required>
    <select name="role">
        {% for role in roles %}
        <option value="role.name">role.name</option>
        {% endfor %}
    </select>
    <button type="submit">Add User</button>
</form>
{% endblock %}

Ajoutez une route pour afficher la liste des utilisateurs et leurs rôles.

@app.route('/users')
@login_required
def users():
    users = user_datastore.find_users()
    return render_template('users.html', users=users)

Créez un template templates/users.html.

<!-- templates/users.html -->
{% extends 'base.html' %}
{% block content %}
<h2>Users</h2>
<table>
    <tr>
        <th>Email</th>
        <th>Roles</th>
    </tr>
    {% for user in users %}
    <tr>
        <td>user.email</td>
        <td>', '.join(role.name for role in user.roles)</td>
    </tr>
    {% endfor %}
</table>
{% endblock %}

Ensuite, vous pouvez exécuter le script CLI avec :

python cli.py add_user

Et ajouter des utilisateurs en spécifiant leur email, mot de passe et rôle.

Ce tutoriel vous a permis d'acquérir une compréhension approfondie de l'authentification dans Flask. Vous avez appris à configurer l'environnement, à créer des modèles de données, à gérer les sessions et à protéger certaines routes. N'hésitez pas à explorer davantage pour approfondir vos connaissances en sécurité Web avec Flask.

Besoin d'aide sur Flask ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Comment installer Flask pour l'authentification ?
Pour installer Flask, vous pouvez utiliser pip avec la commande 'pip install Flask'. Pour ajouter des fonctionnalités d'authentification, installez également Flask-Login avec 'pip install flask-login'.
Quelle est la différence entre authentification et authorization dans Flask ?
L'authentification vérifie l'identité de l'utilisateur (par exemple en demandant son nom d'utilisateur et mot de passe). L'authorization détermine ce que l'utilisateur peut faire une fois connecté, en fonction des permissions qu'il a.
Comment gérer les sessions utilisateur en Flask ?
En utilisant Flask-Login, vous pouvez facilement gérer les sessions utilisateur. L'extension offre des fonctions pour login_user(), logout_user() et qui permettent de vérifier si un utilisateur est connecté avec is_authenticated.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.