🔝 Retour au Sommaire
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 !
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.
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)
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"
┌──────────────┐
│ 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 │
└──────────────┘
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
}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é
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
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 :
- Initiation : Alice clique sur "Se connecter" dans MyApp
- Redirection : MyApp redirige vers le serveur OAuth (Okta)
- Authentification : Alice s'authentifie sur Okta (login/password, MFA...)
- Consentement : Okta demande si Alice autorise MyApp à accéder à ses ressources
- Accord : Alice accepte
- Code d'autorisation : Okta redirige vers MyApp avec un code temporaire
- Retour : MyApp reçoit le code
- Échange : MyApp échange le code contre un access token (en backend, sécurisé)
- Token reçu : Okta envoie l'access token (+ refresh token optionnel)
- Connexion PostgreSQL : MyApp utilise le token pour se connecter à PostgreSQL
- Validation : PostgreSQL valide le token auprès d'Okta
- Confirmation : Okta confirme que le token est valide
- Accès : PostgreSQL accorde l'accès, MyApp peut exécuter des requêtes
Avant de configurer PostgreSQL, vous devez avoir :
-
Un serveur OAuth 2.0 configuré :
- Google Identity Platform
- Microsoft Azure AD (Entra ID)
- Okta
- Auth0
- Keycloak (open-source)
- AWS Cognito
-
Une application OAuth enregistrée avec :
- Client ID
- Client Secret (pour certains flux)
- Redirect URIs
- Scopes autorisés
-
Les URLs du serveur OAuth :
- Issuer URL (base)
- Authorization endpoint
- Token endpoint
- JWKS URI (pour validation des tokens)
# 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 |
# 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) |
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 :
- PostgreSQL télécharge les clés publiques depuis
jwks_uri(mise en cache) - Pour chaque connexion, vérifie la signature du token avec ces clés
- Valide les claims (
exp,aud,iss, etc.) - Extrait l'identité de l'utilisateur
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 :
- PostgreSQL envoie le token au endpoint d'introspection
- Le serveur OAuth répond avec les détails (actif/inactif, username, scopes)
- PostgreSQL autorise ou refuse la connexion
# 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 :
- Créer un projet dans Google Cloud Console
- Activer Google Identity API
- Créer des credentials OAuth 2.0
- Configurer les redirect URIs
# 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 :
- Créer une App Registration dans Azure AD
- Ajouter l'API "PostgreSQL Cluster" dans "Expose an API"
- Définir les scopes (db:read, db:write)
- Configurer les claims optionnels (
groups,preferred_username)
# 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 :
- Créer une Application (Type: Web)
- Configurer l'Authorization Server (custom ou default)
- Créer des scopes custom pour PostgreSQL
- Mapper les groupes Okta dans les tokens
# 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 :
- Créer un Realm (ou utiliser master)
- Créer un Client "postgresql-client" (Public ou Confidential)
- Configurer les Mappers pour inclure les rôles dans le token
- Activer "Include in Token Scope" pour les rôles
# 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 :
- Créer une API "PostgreSQL" dans Auth0
- Définir l'identifier (audience)
- Créer des permissions (scopes)
- Créer une Application et autoriser l'API
É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.
É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()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.
# 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: ABC123import 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()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();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")# 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é.
oauth_audience="api://postgresql-prod-cluster"
Évite : Qu'un token destiné à une autre API soit utilisé pour PostgreSQL.
oauth_scope="openid profile db:read" # Pas db:admin !
Ne demandez que les scopes nécessaires.
Configuration côté OAuth :
{
"access_token_lifetime": 900, // 15 minutes
"refresh_token_lifetime": 2592000 // 30 jours
}Processus :
- Détecter un token compromis
- Révoquer via le serveur OAuth
- 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"-- 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 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 OAuthSolution : 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 heureAlternative : 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 connArchitecture :
┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐
│ 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 :
- Utilisateur se connecte via OAuth (Google, Azure AD...)
- Le frontend reçoit un access token
- Le backend utilise ce token pour se connecter à PostgreSQL
- 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é.
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"]
}
}Architecture :
┌──────────┐ ┌──────────┐ ┌───────────┐
│ Mobile │──────►│ OAuth │ │ Backend │
│ App │◄──────│ (PKCE) │ │ API │
└──────────┘ └──────────┘ └──────┬────┘
│
┌──────▼─────┐
│PostgreSQL │
│ 18 │
└────────────┘
Flux PKCE (Proof Key for Code Exchange) :
- App mobile initie le flux OAuth avec PKCE (plus sécurisé)
- Utilisateur s'authentifie via navigateur mobile
- App reçoit un token
- App envoie requêtes au backend avec le token
- Backend utilise le token pour PostgreSQL
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.
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
#!/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 ==="Causes possibles :
- Token expiré
- Signature invalide
- Audience incorrecte
- 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_urlVérifier les logs PostgreSQL :
tail -f /var/log/postgresql/postgresql-18-main.log | grep -i oauthMessages typiques :
ERROR: OAuth token validation failed: invalid signature
ERROR: OAuth token expired
ERROR: OAuth audience mismatch: expected "pg-cluster", got "other-service"
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"...
Erreur :
ERROR: Could not fetch JWKS from https://oauth.company.com/.well-known/jwks.json
Causes :
- URL incorrecte
- Firewall bloquant
- 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.comSolution temporaire (non recommandé en production) :
# Désactiver la vérification SSL (DANGER)
oauth_ssl_verify="false"
Symptôme : Connexions lentes avec OAuth vs SCRAM.
Cause : Validation du token (introspection ou JWKS fetch).
Solutions :
-
Utiliser la validation locale :
oauth_token_validation="local" -
Cacher les clés JWKS : PostgreSQL 18 cache automatiquement, mais vérifier :
-- Vérifier le cache SELECT * FROM pg_oauth_jwks_cache;
-
Connection pooling :
# PgBouncer pool_mode = transaction server_lifetime = 3600
-
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;
Symptôme : Erreur "Token size exceeds maximum".
Cause : Token JWT avec beaucoup de claims (groupes, permissions...).
Solution :
-
Filtrer les claims côté OAuth : Ne pas inclure tous les groupes dans le token.
-
Utiliser des références :
{ "groups_ref": "https://api.company.com/users/alice/groups" } -
Introspection au lieu de JWT :
oauth_token_validation="introspection"
| 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 | |
| MFA | ✅ Délégué à OAuth | ❌ | ✅ Possible | ❌ |
| Mobilité | ✅ Tokens portables | ❌ Certificat requis | ||
| Audit | ✅ Centralisé | ✅ Centralisé | ✅ Bon | |
| Performance | ✅ Rapide | ✅ Rapide | ||
| Cas d'usage | Apps modernes, SSO | Standard, simple | Entreprise, AD | M2M, haute sécurité |
-
Single Sign-On (SSO) :
- Un seul login pour tous les services
- Meilleure expérience utilisateur
-
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
-
Gestion Centralisée :
- Désactivation d'un compte = révocation immédiate partout
- Politique de sécurité unifiée
- Audit centralisé
-
Intégration Cloud Native :
- Compatible avec Kubernetes, microservices
- Service-to-service authentication
- Support des identités managées (Azure MI, AWS IAM Roles)
-
Conformité :
- Standards de l'industrie (OAuth 2.0, OpenID Connect)
- Facilite les audits de sécurité
- Traçabilité complète
-
Dépendance Externe :
- Si le serveur OAuth est down, pas d'authentification possible
- Besoin de haute disponibilité du serveur OAuth
-
Complexité Initiale :
- Configuration plus lourde que SCRAM
- Courbe d'apprentissage pour les équipes
-
Performance :
- Validation du token ajoute de la latence
- Nécessite connection pooling agressif
-
Compatibilité :
- Tous les clients ne supportent pas OAuth (anciens outils)
- Nécessite PostgreSQL 18+
-
Mapping Utilisateurs :
- Pas d'auto-provisioning natif
- Nécessite synchronisation manuelle ou automatisée
- PostgreSQL 18 Authentication : https://www.postgresql.org/docs/18/auth-oauth.html
- RFC 6749 (OAuth 2.0) : https://datatracker.ietf.org/doc/html/rfc6749
- OpenID Connect : https://openid.net/connect/
- Keycloak (Open Source) : https://www.keycloak.org/
- Auth0 : https://auth0.com/
- Okta : https://www.okta.com/
- Azure AD : https://azure.microsoft.com/en-us/services/active-directory/
- Google Identity : https://developers.google.com/identity
- 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
- "Implementing OAuth 2.0 in PostgreSQL 18" (blog officiel)
- "Zero Trust Architecture with PostgreSQL" (Crunchy Data)
- "OAuth 2.0 Best Practices" (RFC 8252)
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.
- OAuth 2.0 = Authentification par tokens au lieu de mots de passe
- SSO natif : Les utilisateurs utilisent leur identité d'entreprise
- Sécurité renforcée : MFA, révocation instantanée, tokens temporaires
- Migration progressive : Coexistence possible avec SCRAM et autres méthodes
- Production-ready : Validation locale, performance acceptable avec pooling
✅ 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
- Environnement on-premise sans infrastructure OAuth
- Petite équipe sans compétence IAM
- Outils legacy ne supportant pas OAuth
- Besoins de simplicité maximale (SCRAM suffit)
- Évaluer votre infrastructure OAuth actuelle
- Planifier une migration progressive (6-12 mois)
- Former les équipes sur OAuth et JWT
- Tester en environnement de dev/staging
- 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