Aller au contenu principal

Authentification OAuth2

Vue d'ensemble​

Le mode serveur utilise deux niveaux d'authentification :

  1. JWT : Authentification utilisateur sur la plateforme
  2. OAuth2 Enedis : Autorisation d'accès aux données Linky
┌─────────────────────────────────────────────────────────────────────────────┐
│ FLUX D'AUTHENTIFICATION │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 1. AUTHENTIFICATION JWT │ │
│ │ │ │
│ │ Utilisateur Backend │ │
│ │ │ │ │ │
│ │ │ POST /signup │ │ │
│ │ │ {email, password} │ │ │
│ │ │───────────────────────────>│ │ │
│ │ │ │ Créer compte │ │
│ │ │ │ Générer client_id/secret │ │
│ │ │ 200 OK │ │ │
│ │ │<───────────────────────────│ │ │
│ │ │ │ │ │
│ │ │ POST /login │ │ │
│ │ │ {email, password} │ │ │
│ │ │───────────────────────────>│ │ │
│ │ │ │ Vérifier credentials │ │
│ │ │ 200 {access_token} │ Générer JWT (30 jours) │ │
│ │ │<───────────────────────────│ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 2. CONSENTEMENT OAUTH2 ENEDIS │ │
│ │ │ │
│ │ Utilisateur Backend Enedis │ │
│ │ │ │ │ │ │
│ │ │ GET /consent │ │ │ │
│ │ │─────────────────>│ │ │ │
│ │ │ │ Construire URL │ │ │
│ │ │ 302 Redirect │ OAuth2 │ │ │
│ │ │<─────────────────│ │ │ │
│ │ │ │ │ │ │
│ │ │ Enedis Login │ │ │ │
│ │ │─────────────────────────────────────>│ │ │
│ │ │ │ │ │ │
│ │ │ Autoriser app │ │ │ │
│ │ │─────────────────────────────────────>│ │ │
│ │ │ │ │ │ │
│ │ │ Redirect + code │ │ │ │
│ │ │<─────────────────────────────────────│ │ │
│ │ │ │ │ │ │
│ │ │ /callback?code= │ │ │ │
│ │ │─────────────────>│ POST /oauth/token │ │ │
│ │ │ │───────────────────>│ │ │
│ │ │ │<───────────────────│ │ │
│ │ │ │ access_token │ │ │
│ │ │ │ refresh_token │ │ │
│ │ │ │ │ │ │
│ │ │ │ Stocker tokens │ │ │
│ │ │ 302 /dashboard │ Détecter PDLs │ │ │
│ │ │<─────────────────│ │ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 3. APPELS API AUTHENTIFIÉS │ │
│ │ │ │
│ │ Frontend Backend Enedis │ │
│ │ │ │ │ │ │
│ │ │ GET /consumption │ │ │ │
│ │ │ Authorization: Bearer JWT │ │ │ │
│ │ │───────────────────────────>│ │ │ │
│ │ │ │ Vérifier JWT │ │ │
│ │ │ │ Récupérer token │ │ │
│ │ │ │ Enedis │ │ │
│ │ │ │ │ │ │
│ │ │ │ GET /metering │ │ │
│ │ │ │ Authorization: │ │ │
│ │ │ │ Bearer Enedis │ │ │
│ │ │ │───────────────────>│ │ │
│ │ │ │<───────────────────│ │ │
│ │ │ 200 {data} │ │ │ │
│ │ │<───────────────────────────│ │ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

JWT (JSON Web Token)​

Structure du token​

{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"sub": "user-uuid",
"email": "user@example.com",
"is_admin": false,
"exp": 1704067200,
"iat": 1701475200
}
}

Configuration​

# Clé secrète pour signer les tokens
SECRET_KEY=your-super-secret-key-minimum-32-characters

# Durée de validité (en jours)
ACCESS_TOKEN_EXPIRE_DAYS=30

Utilisation dans les requêtes​

# Header Authorization
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

# Ou query parameter (déconseillé)
GET /api/consumption?token=eyJhbGciOiJIUzI1NiIs...

OAuth2 Enedis​

Scopes demandés​

ScopeDescription
metering_data:consumption_dailyConsommation journalière
metering_data:consumption_load_curveCourbe de charge (30 min)
metering_data:production_dailyProduction journalière
metering_data:production_load_curveCourbe de charge production
customers:contact_dataInformations de contact
customers:contract_dataInformations de contrat

URL d'autorisation​

Version v1 par défaut ; ENEDIS_AUTHORIZE_VERSION=v2 pour la page Data Connect 2026 (retour autorisation_id au lieu de usage_point_id), ENEDIS_AUTHORIZE_URL pour une surcharge complète.

https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/authorize
?response_type=code
&client_id={ENEDIS_CLIENT_ID}
&redirect_uri={CALLBACK_URL}
&scope=metering_data:consumption_daily+...
&state={random_state}
&duration=P3Y

Durée du consentement​

Le paramètre duration=P3Y demande un consentement de 3 ans.

Gestion des tokens​

# Stockage en base (chiffré)
class Token(Base):
id = Column(UUID)
user_id = Column(UUID, ForeignKey('users.id'))
pdl_id = Column(UUID, ForeignKey('pdls.id'))
access_token = Column(Text) # Chiffré
refresh_token = Column(Text) # Chiffré
expires_at = Column(DateTime)

Refresh automatique​

Un job planifié rafraîchit les tokens avant expiration :

# Exécuté toutes les heures
async def refresh_expiring_tokens():
# Tokens qui expirent dans les 2 heures
expiring = await get_expiring_tokens(hours=2)
for token in expiring:
await refresh_token(token)

Client Credentials​

Chaque utilisateur reçoit un client_id et client_secret uniques :

{
"client_id": "cli_a1b2c3d4e5f6g7h8",
"client_secret": "sec_xxxxxxxxxxxxxxxxxxxx"
}

Utilisation​

# Authentification API (alternative au JWT)
curl -X GET /api/consumption \
-u "cli_xxx:sec_xxx"

# Ou via header
Authorization: Basic base64(client_id:client_secret)

Régénération​

Paramètres → API → Régénérer les credentials

⚠️ L'ancienne clé est immédiatement invalidée.


Sécurité​

Bonnes pratiques implémentées​

MesureDescription
HTTPS obligatoireRedirection automatique HTTP → HTTPS
Tokens signésJWT signé avec HS256
Rate limiting5 req/s par utilisateur
Brute force protectionBlocage après 5 échecs
Token rotationRefresh token unique par session
Secure cookiesHttpOnly, Secure, SameSite=Strict

Protection CSRF​

# Vérification du state dans le callback OAuth2
if state != session['oauth_state']:
raise HTTPException(403, "Invalid state")

Révocation de consentement​

Un utilisateur peut révoquer son consentement :

  1. Via l'interface MyElectricalData : Paramètres → Consentement → Révoquer
  2. Via le portail Enedis : Mon compte → Mes données

La révocation côté Enedis est détectée lors du prochain appel API (erreur 403).


Dépannage​

Token JWT expiré​

{
"detail": "Token has expired"
}

Solution : Se reconnecter via /login

Token Enedis expiré​

{
"detail": "Enedis token expired, please renew consent"
}

Solution : Relancer le consentement via Paramètres → Consentement

Consentement révoqué​

{
"detail": "Access denied - consent may have been revoked"
}

Solution : Redonner le consentement sur le portail Enedis

Rate limit atteint​

{
"detail": "Rate limit exceeded. Try again in 60 seconds"
}

Solution : Attendre ou optimiser les appels (utiliser le cache)