feat(auth): init Authlib + provider LinkedIn (config env) (#88)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Colin Maudry
2026-06-24 23:11:26 +02:00
parent d72cad90ec
commit d79e990c04
5 changed files with 40 additions and 0 deletions
+6
View File
@@ -33,6 +33,12 @@ USERS_DB_PATH=./users.sqlite
SECRET_KEY= # à générer : python -c "import secrets; print(secrets.token_hex(32))" SECRET_KEY= # à générer : python -c "import secrets; print(secrets.token_hex(32))"
APP_BASE_URL=http://localhost:8050 APP_BASE_URL=http://localhost:8050
# Connexion LinkedIn (OpenID Connect) — créer une app sur le LinkedIn Developer Portal,
# activer "Sign In with LinkedIn using OpenID Connect", déclarer le redirect URI
# {APP_BASE_URL}/auth/linkedin/callback
LINKEDIN_CLIENT_ID=
LINKEDIN_CLIENT_SECRET=
# Brevo — envoi des emails transactionnels (vérification email, reset mot de passe) # Brevo — envoi des emails transactionnels (vérification email, reset mot de passe)
BREVO_API_KEY= # clé API transactionnelle Brevo BREVO_API_KEY= # clé API transactionnelle Brevo
BREVO_TEMPLATE_VERIFY_ID= # ID numérique du template "vérification d'adresse" BREVO_TEMPLATE_VERIFY_ID= # ID numérique du template "vérification d'adresse"
+1
View File
@@ -25,6 +25,7 @@ dependencies = [
"brevo-python==5.0.0rc1", "brevo-python==5.0.0rc1",
"flask-wtf", "flask-wtf",
"email-validator", "email-validator",
"authlib",
"flask-cors>=6.0.2", "flask-cors>=6.0.2",
"flask-smorest>=0.46.0", "flask-smorest>=0.46.0",
"marshmallow>=3.20.0", "marshmallow>=3.20.0",
+21
View File
@@ -0,0 +1,21 @@
import os
from authlib.integrations.flask_client import OAuth
from flask import Flask
LINKEDIN_DISCOVERY_URL = (
"https://www.linkedin.com/oauth/.well-known/openid-configuration"
)
oauth = OAuth()
def init_oauth(app: Flask) -> None:
oauth.init_app(app)
oauth.register(
name="linkedin",
client_id=os.getenv("LINKEDIN_CLIENT_ID"),
client_secret=os.getenv("LINKEDIN_CLIENT_SECRET"),
server_metadata_url=LINKEDIN_DISCOVERY_URL,
client_kwargs={"scope": "openid profile email"},
)
+9
View File
@@ -6,6 +6,7 @@ from flask_wtf.csrf import CSRFProtect
from src.auth import db, mailer from src.auth import db, mailer
from src.auth.models import load_user from src.auth.models import load_user
from src.auth.oauth import init_oauth
from src.utils import DEVELOPMENT, logger from src.utils import DEVELOPMENT, logger
_csrf: CSRFProtect | None = None _csrf: CSRFProtect | None = None
@@ -47,6 +48,8 @@ def init_auth(app: Flask) -> None:
app.register_blueprint(auth_bp) app.register_blueprint(auth_bp)
init_oauth(app)
_csrf = CSRFProtect(app) _csrf = CSRFProtect(app)
if not os.getenv("BREVO_API_KEY"): if not os.getenv("BREVO_API_KEY"):
@@ -54,3 +57,9 @@ def init_auth(app: Flask) -> None:
"BREVO_API_KEY non défini : les emails d'auth échoueront. " "BREVO_API_KEY non défini : les emails d'auth échoueront. "
"Définissez les variables BREVO_* dans .env pour envoyer des emails." "Définissez les variables BREVO_* dans .env pour envoyer des emails."
) )
if not os.getenv("LINKEDIN_CLIENT_ID"):
logger.warning(
"LINKEDIN_CLIENT_ID non défini : la connexion LinkedIn échouera. "
"Définissez LINKEDIN_CLIENT_ID / LINKEDIN_CLIENT_SECRET dans .env."
)
+3
View File
@@ -19,6 +19,9 @@ def app(users_db_path, monkeypatch):
from src.auth.setup import init_auth from src.auth.setup import init_auth
monkeypatch.setenv("SECRET_KEY", "test-secret-key") monkeypatch.setenv("SECRET_KEY", "test-secret-key")
monkeypatch.setenv("LINKEDIN_CLIENT_ID", "test-client-id")
monkeypatch.setenv("LINKEDIN_CLIENT_SECRET", "test-client-secret")
monkeypatch.setenv("APP_BASE_URL", "http://localhost:8050")
app = Flask(__name__) app = Flask(__name__)
app.config["WTF_CSRF_ENABLED"] = False app.config["WTF_CSRF_ENABLED"] = False
init_auth(app) init_auth(app)