transfer from private server/git server
This commit is contained in:
86
web/routes/auth.py
Normal file
86
web/routes/auth.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""
|
||||
routes/auth.py — Flux d'authentification OIDC (Authorization Code Flow) avec
|
||||
Keycloak, via Authlib. 7 étapes documentées dans la rétro-documentation
|
||||
(RD-3) et le journal de bord :
|
||||
|
||||
1. Détection de l'absence de session (décorateur / vérification session)
|
||||
2. Génération de l'URL d'autorisation (+ nonce anti-rejeu)
|
||||
3. Redirection de l'utilisateur vers Keycloak
|
||||
4. Retour sur /auth avec le code d'autorisation
|
||||
5. Échange du code contre un token (authorize_access_token)
|
||||
6. Vérification du token (signature, nonce, expiration — parse_id_token)
|
||||
7. Création de la session applicative Flask
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
from flask import Blueprint, redirect, url_for, session
|
||||
|
||||
from config import Config
|
||||
from services import audit_service
|
||||
from services.auth_service import client_ip
|
||||
|
||||
|
||||
def register_oauth(oauth):
|
||||
"""Enregistre le client OIDC Keycloak auprès d'Authlib (appelé depuis app.py)."""
|
||||
return oauth.register(
|
||||
name="keycloak",
|
||||
client_id=Config.OIDC_CLIENT_ID,
|
||||
client_secret=Config.OIDC_CLIENT_SECRET,
|
||||
server_metadata_url=Config.OIDC_DISCOVERY_URL,
|
||||
client_kwargs={"scope": "openid profile email"},
|
||||
)
|
||||
|
||||
|
||||
def init_auth_routes(oauth):
|
||||
# Le blueprint est créé ICI (et non au niveau du module) afin que
|
||||
# chaque appel de create_app() (production, ou une fois par test en
|
||||
# environnement pytest) produise un blueprint neuf. Un blueprint créé
|
||||
# au niveau module serait décoré une seule fois puis réutilisé tel
|
||||
# quel, ce que Flask refuse dès le second register_blueprint().
|
||||
bp = Blueprint("auth", __name__)
|
||||
keycloak = register_oauth(oauth)
|
||||
|
||||
@bp.route("/login")
|
||||
def login():
|
||||
# Étape 2 : génération de l'URL d'autorisation avec un nonce
|
||||
nonce = uuid.uuid4().hex
|
||||
session["nonce"] = nonce
|
||||
redirect_uri = url_for("auth.auth_callback", _external=True, _scheme="https")
|
||||
# Étape 3 : redirection vers Keycloak
|
||||
return keycloak.authorize_redirect(redirect_uri, nonce=nonce)
|
||||
|
||||
@bp.route("/auth")
|
||||
def auth_callback():
|
||||
db = _get_db()
|
||||
# Étape 5 : échange du code contre un token
|
||||
token = keycloak.authorize_access_token()
|
||||
nonce = session.pop("nonce", None)
|
||||
# Étape 6 : vérification (signature + nonce) du token
|
||||
userinfo = keycloak.parse_id_token(token, nonce=nonce)
|
||||
# Étape 7 : création de la session applicative
|
||||
session["user"] = userinfo
|
||||
session["id_token"] = token.get("id_token")
|
||||
audit_service.log(db, "LOGIN", None, "SUCCESS", userinfo.get("sub"), client_ip())
|
||||
return redirect("/")
|
||||
|
||||
@bp.route("/logout")
|
||||
def logout():
|
||||
db = _get_db()
|
||||
user = session.get("user") or {}
|
||||
id_token = session.get("id_token")
|
||||
audit_service.log(db, "LOGOUT", None, "SUCCESS", user.get("sub"), client_ip())
|
||||
session.clear()
|
||||
return redirect(
|
||||
f"{Config.OIDC_LOGOUT_URL}"
|
||||
f"?post_logout_redirect_uri={Config.PUBLIC_BASE_URL}"
|
||||
f"&id_token_hint={id_token}"
|
||||
)
|
||||
|
||||
return bp
|
||||
|
||||
|
||||
def _get_db():
|
||||
from flask import current_app
|
||||
|
||||
return current_app.config["DB"]
|
||||
Reference in New Issue
Block a user