Skip to content

Latest commit

 

History

History
1413 lines (1101 loc) · 44.6 KB

File metadata and controls

1413 lines (1101 loc) · 44.6 KB

🔝 Retour au Sommaire

16.2.2. Nouveauté PostgreSQL 18 : Authentification OAuth 2.0

Introduction

PostgreSQL 18, publié en septembre 2025, introduit une fonctionnalité majeure attendue depuis longtemps : le support natif d'OAuth 2.0 pour l'authentification des utilisateurs. Cette nouveauté révolutionnaire permet à PostgreSQL de s'intégrer dans les architectures modernes de gestion d'identité (IAM - Identity and Access Management).

Pour les débutants : OAuth 2.0 est le système utilisé par Google, Microsoft, Facebook, etc. pour vous permettre de vous connecter à des applications sans partager votre mot de passe. PostgreSQL 18 adopte enfin ce standard moderne !


Qu'est-ce qu'OAuth 2.0 ? (Explication pour Débutants)

L'analogie du valet

Imaginez que vous séjournez dans un hôtel (le serveur d'authentification) et que vous souhaitez utiliser la salle de sport (PostgreSQL). Au lieu de donner votre clé de chambre à l'accueil de la salle de sport (ce qui serait dangereux), l'hôtel vous délivre un badge temporaire qui :

  • ✅ Permet d'accéder uniquement à la salle de sport (pas à votre chambre)
  • ✅ Expire après quelques heures
  • ✅ Peut être révoqué à tout moment
  • ✅ Ne contient pas vos informations sensibles

C'est exactement ce qu'OAuth 2.0 fait : il vous donne un "badge d'accès temporaire" (appelé token) au lieu de partager votre mot de passe.

Le problème résolu par OAuth 2.0

Avant OAuth (l'ancien monde) ❌

Application Web → PostgreSQL
    "Voici le mot de passe de l'utilisateur : SuperSecret123"

Problèmes :
- Le mot de passe transite partout
- L'application connaît le mot de passe
- Impossible de révoquer l'accès sans changer le mot de passe
- Pas de granularité (tout ou rien)

Avec OAuth 2.0 (le monde moderne) ✅

Utilisateur → Serveur OAuth (Google, Azure AD, Okta)
    "Je veux accéder à PostgreSQL"

Serveur OAuth → Utilisateur
    "Voici un token temporaire : eyJhbGc..."

Utilisateur → PostgreSQL
    "Voici mon token"

PostgreSQL → Serveur OAuth
    "Ce token est-il valide ?"

Serveur OAuth → PostgreSQL
    "Oui, c'est Alice, elle peut se connecter"

Concepts Fondamentaux d'OAuth 2.0

1. Les Acteurs (Rôles)

┌──────────────┐
│  Resource    │  Vous, l'utilisateur qui veut accéder à PostgreSQL
│   Owner      │  (Alice)
└──────────────┘
       │
       │ 1. Demande d'accès
       ▼
┌──────────────┐
│ Authorization│  Le serveur qui gère les identités
│   Server     │  (Google, Azure AD, Keycloak, Okta)
│   (OAuth)    │
└──────────────┘
       │
       │ 2. Délivre un token
       ▼
┌──────────────┐
│    Client    │  Votre application ou client PostgreSQL
│ (Application)│  (psql, pgAdmin, votre app web)
└──────────────┘
       │
       │ 3. Utilise le token
       ▼
┌──────────────┐
│   Resource   │  PostgreSQL (la ressource protégée)
│    Server    │
│ (PostgreSQL) │
└──────────────┘
       │
       │ 4. Valide le token
       ▼
┌──────────────┐
│ Authorization│  Retour au serveur OAuth pour validation
│   Server     │
└──────────────┘

2. Les Tokens

Access Token (Token d'Accès)

Le "badge" qui donne accès à PostgreSQL.

Caractéristiques :

  • Éphémère : Expire rapidement (15 min - 1 heure)
  • Opaque ou JWT : Peut contenir des informations ou être une référence
  • Limité en scope : N'accorde que certaines permissions

Exemple de token JWT :

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhbGljZSIsIm5hbWUiOiJBbGljZSBTbWl0aCIsImlhdCI6MTYzNjM3ODg5OCwiZXhwIjoxNjM2MzgyNDk4LCJhdWQiOiJwb3N0Z3Jlc3FsLWNsdXN0ZXIifQ.signature

Décodé, il contient :

{
  "sub": "alice",           // Sujet (identité)
  "name": "Alice Smith",    // Nom complet
  "email": "alice@company.com",
  "iat": 1636378898,        // Émis le (timestamp)
  "exp": 1636382498,        // Expire le (timestamp)
  "aud": "postgresql-cluster", // Audience (pour qui)
  "scope": "openid profile db:read" // Permissions
}

Refresh Token (Token de Rafraîchissement)

Un "badge spécial" permettant d'obtenir de nouveaux access tokens.

Caractéristiques :

  • Longue durée : Jours, semaines, voire mois
  • Stocké de manière ultra-sécurisée
  • Utilisé uniquement avec le serveur OAuth
  • Peut être révoqué

3. Les Scopes (Portées)

Les scopes définissent ce que le token autorise.

Exemples de scopes :

openid              → Authentification basique  
profile             → Accès au profil utilisateur  
email               → Accès à l'email  
db:read             → Lecture seule sur la base  
db:write            → Écriture dans la base  
db:admin            → Administration complète  

Exemple de demande :

"Je demande un token avec les scopes : openid, profile, db:read"

Le serveur OAuth demandera alors à l'utilisateur :

L'application "MyApp" demande à :
✓ Connaître votre identité (openid)
✓ Voir votre profil (profile)
✓ Lire la base de données PostgreSQL (db:read)

Accepter | Refuser

Architecture OAuth 2.0 avec PostgreSQL 18

Flux Complet (Authorization Code Flow)

C'est le flux le plus sécurisé et recommandé pour les applications web.

┌─────────┐                                         ┌──────────┐
│  User   │                                         │  OAuth   │
│ (Alice) │                                         │  Server  │
└────┬────┘                                         │ (Okta)   │
     │                                              └─────┬────┘
     │ 1. "Je veux accéder à MyApp"                       │
     ├──────────────────────────────►┌─────────┐          │
     │                               │  MyApp  │          │
     │                               │ (Client)│          │
     │                               └────┬────┘          │
     │                                    │               │
     │ 2. Redirection vers OAuth          │               │
     │◄───────────────────────────────────┤               │
     │                                    │               │
     │ 3. "Alice, connecte-toi"           │               │
     ├────────────────────────────────────┼─────────────► │
     │    Email: alice@company.com        │               │
     │    Password: ********              │               │
     │                                    │               │
     │ 4. "Autorises-tu MyApp ?"          │               │
     │◄───────────────────────────────────┼───────────────┤
     │    [Accepter] [Refuser]            │               │
     │                                    │               │
     │ 5. "Oui, j'accepte"                │               │
     ├────────────────────────────────────┼─────────────► │
     │                                    │               │
     │ 6. Redirection avec code           │               │
     │    (http://myapp.com?code=ABC123)  │               │
     │◄───────────────────────────────────┼───────────────┤
     │                                    │               │
     │ 7. Retour sur MyApp                │               │
     ├──────────────────────────────►     │               │
     │                               │    │               │
     │                               │ 8. Échange code→token
     │                               ├──────────────────► │
     │                               │    │               │
     │                               │ 9. Access Token    │
     │                               │◄────────────────── ┤
     │                               │    │               │
     │                               │ 10. Connexion PG   │
     │                               │    │  avec token   │
     │                               │    ▼               │
     │                               │ ┌──────────┐       │
     │                               │ │PostgreSQL│       │
     │                               │ │    18    │       │
     │                               │ └────┬─────┘       │
     │                               │      │             │
     │                               │ 11. Validation     │
     │                               │      │  du token   │
     │                               │      ├───────────► │
     │                               │      │             │
     │                               │ 12. Token valide   │
     │                               │      │◄────────────┤
     │                               │      │             │
     │ 13. Données de la requête     │ ✅ Connexion       │
     │◄──────────────────────────────┤    établie         │
     │                               │                    │

Explication étape par étape :

  1. Initiation : Alice clique sur "Se connecter" dans MyApp
  2. Redirection : MyApp redirige vers le serveur OAuth (Okta)
  3. Authentification : Alice s'authentifie sur Okta (login/password, MFA...)
  4. Consentement : Okta demande si Alice autorise MyApp à accéder à ses ressources
  5. Accord : Alice accepte
  6. Code d'autorisation : Okta redirige vers MyApp avec un code temporaire
  7. Retour : MyApp reçoit le code
  8. Échange : MyApp échange le code contre un access token (en backend, sécurisé)
  9. Token reçu : Okta envoie l'access token (+ refresh token optionnel)
  10. Connexion PostgreSQL : MyApp utilise le token pour se connecter à PostgreSQL
  11. Validation : PostgreSQL valide le token auprès d'Okta
  12. Confirmation : Okta confirme que le token est valide
  13. Accès : PostgreSQL accorde l'accès, MyApp peut exécuter des requêtes

Configuration d'OAuth 2.0 dans PostgreSQL 18

Prérequis

Avant de configurer PostgreSQL, vous devez avoir :

  1. Un serveur OAuth 2.0 configuré :

    • Google Identity Platform
    • Microsoft Azure AD (Entra ID)
    • Okta
    • Auth0
    • Keycloak (open-source)
    • AWS Cognito
  2. Une application OAuth enregistrée avec :

    • Client ID
    • Client Secret (pour certains flux)
    • Redirect URIs
    • Scopes autorisés
  3. Les URLs du serveur OAuth :

    • Issuer URL (base)
    • Authorization endpoint
    • Token endpoint
    • JWKS URI (pour validation des tokens)

Configuration PostgreSQL (pg_hba.conf)

Configuration Basique

# TYPE  DATABASE  USER    ADDRESS       METHOD    OPTIONS
host    all       all     0.0.0.0/0     oauth     oauth_issuer_url="https://oauth.company.com" \
                                                  oauth_audience="postgresql-prod-cluster"

Paramètres obligatoires :

Paramètre Description Exemple
oauth_issuer_url URL de base du serveur OAuth https://oauth.company.com
oauth_audience Identifiant de votre cluster PostgreSQL postgresql-prod-cluster

Configuration Complète (Recommandée)

# Authentification OAuth 2.0 pour tous les utilisateurs
host    all    all    0.0.0.0/0    oauth \
    oauth_issuer_url="https://login.microsoftonline.com/tenant-id/v2.0" \
    oauth_audience="api://postgresql-cluster" \
    oauth_scope="openid profile email db:connect" \
    oauth_claim_username="preferred_username" \
    oauth_claim_groups="groups" \
    oauth_jwks_uri="https://login.microsoftonline.com/tenant-id/discovery/v2.0/keys" \
    oauth_token_validation="introspection" \
    oauth_introspection_endpoint="https://login.microsoftonline.com/oauth2/v2.0/introspect" \
    oauth_client_id="postgresql-client-id" \
    oauth_client_secret="super-secret-key"

Paramètres avancés :

Paramètre Description Valeur par défaut
oauth_scope Scopes requis (séparés par espaces) openid
oauth_claim_username Claim JWT contenant le username sub
oauth_claim_groups Claim JWT contenant les groupes (aucun)
oauth_jwks_uri URL des clés publiques pour validation Auto-découvert
oauth_token_validation Méthode de validation (local ou introspection) local
oauth_introspection_endpoint URL d'introspection du token (aucun)
oauth_client_id ID client pour introspection (aucun)
oauth_client_secret Secret client pour introspection (aucun)

Méthodes de Validation de Token

1. Validation Locale (Recommandée)

PostgreSQL vérifie le token JWT localement en utilisant les clés publiques du serveur OAuth.

Avantages :

  • ✅ Rapide (pas d'appel réseau)
  • ✅ Pas de surcharge sur le serveur OAuth
  • ✅ Fonctionne même si le serveur OAuth est temporairement indisponible

Configuration :

oauth_token_validation="local"  
oauth_jwks_uri="https://oauth.company.com/.well-known/jwks.json"  

Processus :

  1. PostgreSQL télécharge les clés publiques depuis jwks_uri (mise en cache)
  2. Pour chaque connexion, vérifie la signature du token avec ces clés
  3. Valide les claims (exp, aud, iss, etc.)
  4. Extrait l'identité de l'utilisateur

2. Validation par Introspection

PostgreSQL demande au serveur OAuth si le token est valide.

Avantages :

  • ✅ Gère les tokens opaques (non-JWT)
  • ✅ Vérifie la révocation en temps réel

Inconvénients :

  • ⚠️ Requête réseau à chaque connexion
  • ⚠️ Point de défaillance unique

Configuration :

oauth_token_validation="introspection"  
oauth_introspection_endpoint="https://oauth.company.com/oauth/introspect"  
oauth_client_id="postgresql-service"  
oauth_client_secret="secret-key"  

Processus :

  1. PostgreSQL envoie le token au endpoint d'introspection
  2. Le serveur OAuth répond avec les détails (actif/inactif, username, scopes)
  3. PostgreSQL autorise ou refuse la connexion

Exemples de Configuration par Fournisseur OAuth

1. Google Identity Platform

# pg_hba.conf
host    all    all    0.0.0.0/0    oauth \
    oauth_issuer_url="https://accounts.google.com" \
    oauth_audience="123456789-abc.apps.googleusercontent.com" \
    oauth_scope="openid email profile" \
    oauth_claim_username="email"

Étapes préalables :

  1. Créer un projet dans Google Cloud Console
  2. Activer Google Identity API
  3. Créer des credentials OAuth 2.0
  4. Configurer les redirect URIs

2. Microsoft Azure AD (Entra ID)

# pg_hba.conf
host    all    all    0.0.0.0/0    oauth \
    oauth_issuer_url="https://login.microsoftonline.com/{tenant-id}/v2.0" \
    oauth_audience="api://postgresql-cluster" \
    oauth_scope="openid profile User.Read" \
    oauth_claim_username="preferred_username" \
    oauth_claim_groups="groups"

Remplacer {tenant-id} par votre Tenant ID Azure.

Configuration Azure :

  1. Créer une App Registration dans Azure AD
  2. Ajouter l'API "PostgreSQL Cluster" dans "Expose an API"
  3. Définir les scopes (db:read, db:write)
  4. Configurer les claims optionnels (groups, preferred_username)

3. Okta

# pg_hba.conf
host    all    all    0.0.0.0/0    oauth \
    oauth_issuer_url="https://dev-123456.okta.com/oauth2/default" \
    oauth_audience="api://postgresql" \
    oauth_scope="openid profile email groups" \
    oauth_claim_username="preferred_username" \
    oauth_claim_groups="groups"

Configuration Okta :

  1. Créer une Application (Type: Web)
  2. Configurer l'Authorization Server (custom ou default)
  3. Créer des scopes custom pour PostgreSQL
  4. Mapper les groupes Okta dans les tokens

4. Keycloak (Open Source)

# pg_hba.conf
host    all    all    0.0.0.0/0    oauth \
    oauth_issuer_url="https://keycloak.company.com/realms/master" \
    oauth_audience="postgresql-client" \
    oauth_scope="openid profile email" \
    oauth_claim_username="preferred_username" \
    oauth_claim_groups="realm_roles"

Configuration Keycloak :

  1. Créer un Realm (ou utiliser master)
  2. Créer un Client "postgresql-client" (Public ou Confidential)
  3. Configurer les Mappers pour inclure les rôles dans le token
  4. Activer "Include in Token Scope" pour les rôles

5. Auth0

# pg_hba.conf
host    all    all    0.0.0.0/0    oauth \
    oauth_issuer_url="https://company.auth0.com/" \
    oauth_audience="https://api.company.com/postgresql" \
    oauth_scope="openid profile email" \
    oauth_claim_username="email"

Configuration Auth0 :

  1. Créer une API "PostgreSQL" dans Auth0
  2. Définir l'identifier (audience)
  3. Créer des permissions (scopes)
  4. Créer une Application et autoriser l'API

Mapping Utilisateurs et Rôles

Principe de Base

⚠️ Important : OAuth 2.0 authentifie l'utilisateur mais ne crée PAS automatiquement de compte PostgreSQL. Vous devez mapper les identités OAuth aux utilisateurs PostgreSQL.

Stratégie 1 : Mapping Manuel (Petites Équipes)

Étape 1 : Créer les utilisateurs PostgreSQL

-- Créer un utilisateur correspondant à l'email OAuth
CREATE USER "alice@company.com" WITH NOLOGIN;  
GRANT CONNECT ON DATABASE mydb TO "alice@company.com";  
GRANT pg_read_all_data TO "alice@company.com";  

CREATE USER "bob@company.com" WITH NOLOGIN;  
GRANT CONNECT ON DATABASE mydb TO "bob@company.com";  
GRANT pg_write_all_data TO "bob@company.com";  

Note : NOLOGIN empêche la connexion par mot de passe, seul OAuth fonctionnera.

Étape 2 : Configuration du claim

# pg_hba.conf
oauth_claim_username="email"

Quand Alice se connecte avec son token OAuth contenant "email": "alice@company.com", PostgreSQL cherchera l'utilisateur alice@company.com.

Stratégie 2 : Mapping par Groupes (Entreprises)

Étape 1 : Créer des rôles PostgreSQL correspondant aux groupes OAuth

-- Créer des rôles basés sur les groupes OAuth/AD
CREATE ROLE developers;  
GRANT CONNECT ON DATABASE mydb TO developers;  
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO developers;  

CREATE ROLE data_analysts;  
GRANT CONNECT ON DATABASE mydb TO data_analysts;  
GRANT SELECT ON ALL TABLES IN SCHEMA public TO data_analysts;  

CREATE ROLE admins;  
GRANT ALL PRIVILEGES ON DATABASE mydb TO admins;  

-- Créer des utilisateurs et les ajouter aux groupes
CREATE USER "alice@company.com" WITH NOLOGIN;  
GRANT developers TO "alice@company.com";  

CREATE USER "charlie@company.com" WITH NOLOGIN;  
GRANT data_analysts TO "charlie@company.com";  

Étape 2 : Configuration des claims de groupes

# pg_hba.conf
oauth_claim_username="email"  
oauth_claim_groups="groups"  

Étape 3 : Configurer le serveur OAuth pour inclure les groupes

Exemple avec Azure AD :

// Token JWT contient
{
  "email": "alice@company.com",
  "groups": [
    "Developers",
    "DatabaseUsers"
  ]
}

Étape 4 : Script de synchronisation automatique (optionnel)

# sync_oauth_users.py
import psycopg  
import requests  

# Récupérer les utilisateurs du serveur OAuth
oauth_users = get_users_from_oauth_api()

# Connexion PostgreSQL (avec compte admin)
conn = psycopg.connect("host=localhost user=postgres")

for user in oauth_users:
    email = user['email']
    groups = user['groups']

    # Créer l'utilisateur s'il n'existe pas
    conn.execute(f"""
        DO $$
        BEGIN
            IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '{email}') THEN
                CREATE USER "{email}" WITH NOLOGIN;
            END IF;
        END $$;
    """)

    # Ajouter aux groupes
    for group in groups:
        if group in ['Developers', 'Admins', 'DataAnalysts']:
            conn.execute(f'GRANT {group.lower()} TO "{email}"')

conn.commit()

Stratégie 3 : Auto-Provisioning (Avancé)

PostgreSQL 18 ne supporte pas nativement l'auto-provisioning, mais vous pouvez utiliser un Event Trigger combiné à une extension personnalisée.

Concept :

-- Pseudo-code (nécessite une extension custom)
CREATE EVENT TRIGGER auto_provision_user  
ON oauth_login  
EXECUTE FUNCTION auto_create_user_from_token();  

Alternative : Utiliser un Proxy

Un reverse proxy (comme Supabase GoTrue, ou custom) intercepte les connexions, crée les utilisateurs à la volée, puis transmet la connexion à PostgreSQL.


Connexion Côté Client

1. Avec psql (PostgreSQL 18)

# Obtenir un token OAuth d'abord (via votre fournisseur)
TOKEN=$(curl -X POST https://oauth.company.com/token \
  -d "grant_type=password" \
  -d "username=alice@company.com" \
  -d "password=MyPassword" \
  -d "client_id=postgresql-client" \
  -d "scope=openid profile db:connect" | jq -r .access_token)

# Connexion avec le token
PGOAUTHTOKEN=$TOKEN psql -h pg.example.com -U alice@company.com -d mydb

# Ou via la variable d'environnement
export PGOAUTHTOKEN=$TOKEN  
psql -h pg.example.com -U alice@company.com -d mydb  

Alternative : Connexion interactive

# Si le client supporte le flux interactif (Device Flow)
psql -h pg.example.com -U alice@company.com -d mydb --oauth-interactive

# Affichera :
# Please visit: https://oauth.company.com/device?code=ABC123
# Et entrez le code: ABC123

2. Avec un Driver Python (psycopg3)

import psycopg  
import requests  

# 1. Obtenir un token OAuth
def get_oauth_token():
    response = requests.post(
        'https://oauth.company.com/token',
        data={
            'grant_type': 'client_credentials',
            'client_id': 'my-app',
            'client_secret': 'my-secret',
            'scope': 'openid db:connect'
        }
    )
    return response.json()['access_token']

# 2. Connexion PostgreSQL avec le token
token = get_oauth_token()

conn = psycopg.connect(
    host='pg.example.com',
    user='alice@company.com',
    password=token,  # Le token est passé comme "password"
    dbname='mydb',
    sslmode='require'
)

# 3. Utilisation normale
with conn.cursor() as cur:
    cur.execute("SELECT current_user;")
    print(cur.fetchone())

conn.close()

3. Avec Node.js (node-postgres)

const { Client } = require('pg');  
const axios = require('axios');  

// 1. Obtenir le token OAuth
async function getOAuthToken() {
  const response = await axios.post('https://oauth.company.com/token', {
    grant_type: 'client_credentials',
    client_id: 'my-app',
    client_secret: 'my-secret',
    scope: 'openid db:connect'
  });
  return response.data.access_token;
}

// 2. Connexion PostgreSQL
async function connectWithOAuth() {
  const token = await getOAuthToken();

  const client = new Client({
    host: 'pg.example.com',
    user: 'alice@company.com',
    password: token,  // Token passé comme password
    database: 'mydb',
    ssl: { rejectUnauthorized: true }
  });

  await client.connect();

  const res = await client.query('SELECT current_user');
  console.log('Connected as:', res.rows[0].current_user);

  await client.end();
}

connectWithOAuth();

4. Gestion du Refresh Token

Les access tokens expirant rapidement, il faut gérer le renouvellement :

import psycopg  
import requests  
import time  

class OAuthPostgresConnection:
    def __init__(self, oauth_config, pg_config):
        self.oauth_config = oauth_config
        self.pg_config = pg_config
        self.access_token = None
        self.refresh_token = None
        self.token_expires_at = 0
        self.conn = None

    def get_tokens(self):
        """Obtenir les tokens initiaux"""
        response = requests.post(
            self.oauth_config['token_url'],
            data={
                'grant_type': 'password',
                'username': self.oauth_config['username'],
                'password': self.oauth_config['password'],
                'client_id': self.oauth_config['client_id'],
                'scope': 'openid profile db:connect'
            }
        )
        data = response.json()
        self.access_token = data['access_token']
        self.refresh_token = data['refresh_token']
        # Token expire dans X secondes (avec marge de sécurité)
        self.token_expires_at = time.time() + data['expires_in'] - 60

    def refresh_access_token(self):
        """Renouveler le token avec le refresh token"""
        response = requests.post(
            self.oauth_config['token_url'],
            data={
                'grant_type': 'refresh_token',
                'refresh_token': self.refresh_token,
                'client_id': self.oauth_config['client_id']
            }
        )
        data = response.json()
        self.access_token = data['access_token']
        # Nouveau refresh token potentiellement fourni
        if 'refresh_token' in data:
            self.refresh_token = data['refresh_token']
        self.token_expires_at = time.time() + data['expires_in'] - 60

    def connect(self):
        """Se connecter à PostgreSQL"""
        if time.time() >= self.token_expires_at:
            if self.refresh_token:
                self.refresh_access_token()
            else:
                self.get_tokens()

        self.conn = psycopg.connect(
            host=self.pg_config['host'],
            user=self.pg_config['user'],
            password=self.access_token,
            dbname=self.pg_config['dbname'],
            sslmode='require'
        )
        return self.conn

    def execute(self, query):
        """Exécuter une requête (avec reconnexion si nécessaire)"""
        try:
            cur = self.conn.cursor()
            cur.execute(query)
            return cur.fetchall()
        except psycopg.OperationalError:
            # Token expiré, renouveler et réessayer
            self.connect()
            cur = self.conn.cursor()
            cur.execute(query)
            return cur.fetchall()

# Utilisation
oauth_config = {
    'token_url': 'https://oauth.company.com/token',
    'username': 'alice@company.com',
    'password': 'MyPassword',
    'client_id': 'my-app'
}

pg_config = {
    'host': 'pg.example.com',
    'user': 'alice@company.com',
    'dbname': 'mydb'
}

pg = OAuthPostgresConnection(oauth_config, pg_config)  
pg.connect()  

# Les requêtes géreront automatiquement le renouvellement
results = pg.execute("SELECT * FROM users LIMIT 10")

Sécurité et Bonnes Pratiques

1. Toujours Utiliser SSL/TLS ✅

# pg_hba.conf - Utiliser hostssl au lieu de host
hostssl    all    all    0.0.0.0/0    oauth    oauth_issuer_url="..."

Pourquoi ? Le token OAuth transite sur le réseau. Sans SSL, il peut être intercepté.

2. Validation Stricte de l'Audience

oauth_audience="api://postgresql-prod-cluster"

Évite : Qu'un token destiné à une autre API soit utilisé pour PostgreSQL.

3. Scopes Minimaux (Principe du Moindre Privilège)

oauth_scope="openid profile db:read"  # Pas db:admin !

Ne demandez que les scopes nécessaires.

4. Expiration Courte des Tokens

Configuration côté OAuth :

{
  "access_token_lifetime": 900,  // 15 minutes
  "refresh_token_lifetime": 2592000  // 30 jours
}

5. Révocation de Tokens

Processus :

  1. Détecter un token compromis
  2. Révoquer via le serveur OAuth
  3. PostgreSQL refusera le token à la prochaine validation

Avec introspection :

# Révoquer un token dans Keycloak
curl -X POST https://keycloak.company.com/realms/master/protocol/openid-connect/revoke \
  -d "token=$ACCESS_TOKEN" \
  -d "client_id=postgresql-client" \
  -d "client_secret=secret"

6. Monitoring et Audit

-- Voir les connexions OAuth actives
SELECT
    usename,
    application_name,
    client_addr,
    backend_start,
    state
FROM pg_stat_activity  
WHERE usename LIKE '%@%';  -- Emails OAuth  

-- Logs d'authentification
-- postgresql.conf
log_connections = on  
log_disconnections = on  

7. Rotation des Secrets

Si vous utilisez oauth_client_secret :

# 1. Créer un nouveau secret dans le serveur OAuth
# 2. Mettre à jour pg_hba.conf
oauth_client_secret="new-secret-key"
# 3. Recharger PostgreSQL
pg_ctl reload
# 4. Révoquer l'ancien secret dans OAuth

8. Connection Pooling avec OAuth

⚠️ Défi : Les tokens expirent, mais les pools maintiennent les connexions ouvertes.

Solution : PgBouncer en mode Transaction

# pgbouncer.ini
[databases]
mydb = host=pg.example.com

[pgbouncer]
pool_mode = transaction  
max_client_conn = 1000  
default_pool_size = 25  

# Réinitialiser les connexions régulièrement
server_lifetime = 3600  # 1 heure

Alternative : Middleware applicatif

# Renouveler le token avant chaque utilisation de la connexion du pool
def get_connection():
    conn = pool.get_connection()
    if token_expired():
        conn.close()
        refresh_token()
        conn = pool.new_connection(new_token)
    return conn

Cas d'Usage et Architecture

1. Application Web avec OAuth SSO

Architecture :

┌──────────┐       ┌──────────┐       ┌───────────┐       ┌──────────┐
│  User    │──────►│  WebApp  │──────►│   OAuth   │       │PostgreSQL│
│ (Browser)│◄──────│ (React)  │◄──────│  (Auth0)  │       │    18    │
└──────────┘       └─────┬────┘       └───────────┘       └─────┬────┘
                         │                                      │
                         │  1. User login → OAuth token         │
                         │  2. Store token in session/cookie    │
                         │  3. Backend uses token for DB        │
                         └──────────────────────────────────────┘

Flux :

  1. Utilisateur se connecte via OAuth (Google, Azure AD...)
  2. Le frontend reçoit un access token
  3. Le backend utilise ce token pour se connecter à PostgreSQL
  4. Chaque requête SQL utilise l'identité réelle de l'utilisateur (audit trail)

Avantage : Audit parfait - chaque requête est associée à un utilisateur identifié.

2. Microservices avec Service-to-Service Auth

Architecture :

┌─────────────┐       ┌─────────────┐       ┌──────────┐
│  Service A  │──────►│   OAuth     │       │PostgreSQL│
│  (Orders)   │       │  (Client    │       │    18    │
└─────────────┘       │ Credentials)│       └─────┬────┘
                      └──────┬──────┘             │
┌─────────────┐              │                    │
│  Service B  │──────────────┘                    │
│ (Inventory) │───────────────────────────────────┘
└─────────────┘

Chaque service obtient son propre token avec des scopes limités

Configuration OAuth :

{
  "service_orders": {
    "scopes": ["db:read", "db:write:orders"]
  },
  "service_inventory": {
    "scopes": ["db:read", "db:write:inventory"]
  }
}

3. Applications Mobiles

Architecture :

┌──────────┐       ┌──────────┐       ┌───────────┐
│  Mobile  │──────►│  OAuth   │       │ Backend   │
│   App    │◄──────│ (PKCE)   │       │   API     │
└──────────┘       └──────────┘       └──────┬────┘
                                             │
                                      ┌──────▼─────┐
                                      │PostgreSQL  │
                                      │     18     │
                                      └────────────┘

Flux PKCE (Proof Key for Code Exchange) :

  1. App mobile initie le flux OAuth avec PKCE (plus sécurisé)
  2. Utilisateur s'authentifie via navigateur mobile
  3. App reçoit un token
  4. App envoie requêtes au backend avec le token
  5. Backend utilise le token pour PostgreSQL

4. Data Science / BI Tools

Objectif : Permettre aux analystes d'accéder à PostgreSQL avec leurs credentials d'entreprise (SSO).

Tools supportant OAuth :

  • Tableau
  • Power BI
  • Looker
  • Metabase (via plugin)

Configuration Tableau :

Server: pg.example.com  
Port: 5432  
Database: analytics_db  
Authentication: OAuth 2.0  
OAuth Provider: Azure AD  

Avantage : Les analystes utilisent leur compte entreprise, pas de mots de passe PostgreSQL à gérer.


Migration et Coexistence

Stratégie de Migration Progressive

Phase 1 : Configuration Hybride

Permettre à la fois SCRAM et OAuth pendant la transition.

# pg_hba.conf
# Admins : SCRAM (existant)
hostssl all  postgres         10.0.1.0/24   scram-sha-256

# Applications critiques : Certificats (existant)
hostssl all  app_critical     10.0.2.0/24   cert

# Nouveaux utilisateurs : OAuth
hostssl all  all              10.0.3.0/24   oauth oauth_issuer_url="..."

# VPN users : OAuth ou SCRAM
hostssl all  all              192.168.0.0/16 oauth oauth_issuer_url="..."  
hostssl all  all              192.168.0.0/16 scram-sha-256  

Phase 2 : Migration Progressive par Équipe

# Équipe Dev (early adopters) : OAuth
hostssl all  @developers      0.0.0.0/0     oauth

# Équipe Data : SCRAM (legacy)
hostssl all  @data_team       0.0.0.0/0     scram-sha-256

# Nouveaux utilisateurs : OAuth uniquement
hostssl all  all              0.0.0.0/0     oauth

Phase 3 : OAuth Complet

Après validation complète (6-12 mois) :

# Tout le monde en OAuth
hostssl all  all              0.0.0.0/0     oauth

# Compte de secours local (peer)
local  all   postgres                        peer

Script de Validation de Migration

#!/bin/bash
# validate_oauth_migration.sh

echo "=== Test de Connexion OAuth ==="

# 1. Obtenir un token
TOKEN=$(curl -s -X POST https://oauth.company.com/token \
  -d "grant_type=client_credentials" \
  -d "client_id=test-client" \
  -d "client_secret=test-secret" \
  -d "scope=openid db:connect" | jq -r .access_token)

if [ -z "$TOKEN" ]; then
  echo "❌ Échec obtention token OAuth"
  exit 1
fi

echo "✅ Token OAuth obtenu"

# 2. Tester la connexion
PGOAUTHTOKEN=$TOKEN psql -h pg.example.com -U test@company.com -d testdb -c "SELECT current_user;" > /dev/null 2>&1

if [ $? -eq 0 ]; then
  echo "✅ Connexion PostgreSQL avec OAuth réussie"
else
  echo "❌ Échec connexion PostgreSQL"
  exit 1
fi

# 3. Vérifier l'identité
CURRENT_USER=$(PGOAUTHTOKEN=$TOKEN psql -h pg.example.com -U test@company.com -d testdb -t -c "SELECT current_user;")

if [ "$CURRENT_USER" == "test@company.com" ]; then
  echo "✅ Identité correcte : $CURRENT_USER"
else
  echo "❌ Identité incorrecte : $CURRENT_USER"
  exit 1
fi

echo "=== ✅ Tous les tests réussis ==="

Troubleshooting (Dépannage)

Problème 1 : "FATAL: OAuth authentication failed"

Causes possibles :

  1. Token expiré
  2. Signature invalide
  3. Audience incorrecte
  4. Issuer non reconnu

Diagnostic :

# Décoder le token JWT (sans valider la signature)
echo $TOKEN | cut -d. -f2 | base64 -d | jq

# Vérifier :
# - "exp": timestamp d'expiration (convertir en date)
# - "aud": doit correspondre à oauth_audience
# - "iss": doit correspondre à oauth_issuer_url

Vérifier les logs PostgreSQL :

tail -f /var/log/postgresql/postgresql-18-main.log | grep -i oauth

Messages typiques :

ERROR:  OAuth token validation failed: invalid signature  
ERROR:  OAuth token expired  
ERROR:  OAuth audience mismatch: expected "pg-cluster", got "other-service"  

Problème 2 : "User does not exist"

Cause : L'utilisateur OAuth n'a pas de compte PostgreSQL.

Solution :

-- Créer l'utilisateur correspondant au claim username
CREATE USER "alice@company.com" WITH NOLOGIN;  
GRANT CONNECT ON DATABASE mydb TO "alice@company.com";  

Vérifier le mapping :

# Dans pg_hba.conf, vérifier :
oauth_claim_username="email"  # ou "preferred_username", "sub"...

Problème 3 : Impossible de récupérer JWKS

Erreur :

ERROR: Could not fetch JWKS from https://oauth.company.com/.well-known/jwks.json

Causes :

  1. URL incorrecte
  2. Firewall bloquant
  3. Certificat SSL invalide

Diagnostic :

# Tester manuellement
curl https://oauth.company.com/.well-known/jwks.json

# Vérifier la connectivité depuis le serveur PostgreSQL
sudo -u postgres curl -v https://oauth.company.com/.well-known/jwks.json

# Vérifier le certificat SSL
openssl s_client -connect oauth.company.com:443 -servername oauth.company.com

Solution temporaire (non recommandé en production) :

# Désactiver la vérification SSL (DANGER)
oauth_ssl_verify="false"

Problème 4 : Performance dégradée

Symptôme : Connexions lentes avec OAuth vs SCRAM.

Cause : Validation du token (introspection ou JWKS fetch).

Solutions :

  1. Utiliser la validation locale :

    oauth_token_validation="local"
    
  2. Cacher les clés JWKS : PostgreSQL 18 cache automatiquement, mais vérifier :

    -- Vérifier le cache
    SELECT * FROM pg_oauth_jwks_cache;
  3. Connection pooling :

    # PgBouncer
    pool_mode = transaction
    server_lifetime = 3600
  4. Monitoring :

    -- Temps d'authentification moyen
    SELECT
        usename,
        AVG(EXTRACT(EPOCH FROM (backend_start - query_start))) as avg_auth_time_sec
    FROM pg_stat_activity
    WHERE usename LIKE '%@%'
    GROUP BY usename;

Problème 5 : Token trop gros

Symptôme : Erreur "Token size exceeds maximum".

Cause : Token JWT avec beaucoup de claims (groupes, permissions...).

Solution :

  1. Filtrer les claims côté OAuth : Ne pas inclure tous les groupes dans le token.

  2. Utiliser des références :

    {
      "groups_ref": "https://api.company.com/users/alice/groups"
    }
  3. Introspection au lieu de JWT :

    oauth_token_validation="introspection"
    

Comparaison OAuth vs Autres Méthodes

Critère OAuth 2.0 SCRAM-SHA-256 LDAP Certificats
Sécurité ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Complexité ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
SSO ✅ Natif ✅ Possible
Révocation ✅ Temps réel ❌ (change password) ✅ Désactiver compte ⚠️ CRL/OCSP
MFA ✅ Délégué à OAuth ✅ Possible
Mobilité ✅ Tokens portables ⚠️ Password storage ⚠️ Password storage ❌ Certificat requis
Audit ✅ Centralisé ⚠️ Local ✅ Centralisé ✅ Bon
Performance ⚠️ Validation externe ✅ Rapide ⚠️ Requête LDAP ✅ Rapide
Cas d'usage Apps modernes, SSO Standard, simple Entreprise, AD M2M, haute sécurité

Avantages et Limitations d'OAuth dans PostgreSQL 18

✅ Avantages

  1. Single Sign-On (SSO) :

    • Un seul login pour tous les services
    • Meilleure expérience utilisateur
  2. Sécurité Moderne :

    • Pas de mots de passe stockés dans PostgreSQL
    • MFA intégré (via le fournisseur OAuth)
    • Tokens à courte durée de vie
  3. Gestion Centralisée :

    • Désactivation d'un compte = révocation immédiate partout
    • Politique de sécurité unifiée
    • Audit centralisé
  4. Intégration Cloud Native :

    • Compatible avec Kubernetes, microservices
    • Service-to-service authentication
    • Support des identités managées (Azure MI, AWS IAM Roles)
  5. Conformité :

    • Standards de l'industrie (OAuth 2.0, OpenID Connect)
    • Facilite les audits de sécurité
    • Traçabilité complète

⚠️ Limitations

  1. Dépendance Externe :

    • Si le serveur OAuth est down, pas d'authentification possible
    • Besoin de haute disponibilité du serveur OAuth
  2. Complexité Initiale :

    • Configuration plus lourde que SCRAM
    • Courbe d'apprentissage pour les équipes
  3. Performance :

    • Validation du token ajoute de la latence
    • Nécessite connection pooling agressif
  4. Compatibilité :

    • Tous les clients ne supportent pas OAuth (anciens outils)
    • Nécessite PostgreSQL 18+
  5. Mapping Utilisateurs :

    • Pas d'auto-provisioning natif
    • Nécessite synchronisation manuelle ou automatisée

Ressources Complémentaires

Documentation Officielle

Fournisseurs OAuth Recommandés

Outils et Bibliothèques

  • jwt.io : Décoder et inspecter les tokens JWT
  • oauth2-proxy : Reverse proxy OAuth 2.0
  • Supabase : PostgreSQL avec OAuth intégré
  • PostgREST : API REST avec support OAuth

Articles et Tutoriels

  • "Implementing OAuth 2.0 in PostgreSQL 18" (blog officiel)
  • "Zero Trust Architecture with PostgreSQL" (Crunchy Data)
  • "OAuth 2.0 Best Practices" (RFC 8252)

Conclusion

L'introduction d'OAuth 2.0 dans PostgreSQL 18 marque une évolution majeure vers une authentification moderne, sécurisée et cloud-native. Cette fonctionnalité permet à PostgreSQL de s'intégrer parfaitement dans les architectures contemporaines où l'identité est gérée de manière centralisée.

Points Clés à Retenir

  1. OAuth 2.0 = Authentification par tokens au lieu de mots de passe
  2. SSO natif : Les utilisateurs utilisent leur identité d'entreprise
  3. Sécurité renforcée : MFA, révocation instantanée, tokens temporaires
  4. Migration progressive : Coexistence possible avec SCRAM et autres méthodes
  5. Production-ready : Validation locale, performance acceptable avec pooling

Recommandations

✅ Adoptez OAuth 2.0 si :

  • Vous déployez dans le cloud (AWS, Azure, GCP)
  • Vous avez déjà un fournisseur OAuth (Azure AD, Okta...)
  • Vous construisez des microservices
  • La conformité exige un SSO

⚠️ Restez prudent si :

  • Environnement on-premise sans infrastructure OAuth
  • Petite équipe sans compétence IAM
  • Outils legacy ne supportant pas OAuth
  • Besoins de simplicité maximale (SCRAM suffit)

Prochaines Étapes

  1. Évaluer votre infrastructure OAuth actuelle
  2. Planifier une migration progressive (6-12 mois)
  3. Former les équipes sur OAuth et JWT
  4. Tester en environnement de dev/staging
  5. Déployer progressivement en production

OAuth 2.0 dans PostgreSQL 18 n'est pas qu'une fonctionnalité technique, c'est un pas vers l'avenir de la gestion d'identité dans les bases de données.


⏭️ Nouveauté PG 18 : SCRAM passthrough avec postgres_fdw et dblink