PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 18 beta 2 » Interfaces client » libpq -- Bibliothèque C » Support d'OAuth

32.20. Support d'OAuth #

libpq intègre le support du flux client OAuth v2 Device Authorization, comme indiqué dans RFC 8628, en tant que module optionnel. Voir la documentation d'installation pour des informations sur l'activation du support pour l'autorisation de périphérique en tant que flux natif.

Quand le support est activé et que le module optionnel est installé, libpq utilisera le flux natif par défaut si le demande un jeton de porteur lors de l'authentification. Ce flux peut être utilisé même si le système exécutant l'application cliente n'a pas de navigateur web utilisable, par exemple lors de l'exécution d'un client via SSH.

Par défaut, le flux natif affichera une URL à visiter et un code utilisateur à saisir ici :

$ psql 'dbname=postgres oauth_issuer=https://example.com oauth_client_id=...'
Visit https://example.com/device and enter the code: ABCD-EFGH

(Cet invite pourrait être personnalisée.) L'utilisateur saisira alors son fournisseur OAuth, qui demandera s'il doit autoriser libpq et le serveur à réaliser des actions à leur place. Une bonne pratique est de toujours vérifier l'URL et les droits affichés pour s'assurer qu'elles correspondent à votre attente avant de continuer. Les droits ne doivent pas être donnés à des tiers en qui vous n'avez pas confiance.

Les applications clientes peuvent implémenter leur propre flux pour personnaliser l'interaction et l'intégration avec les applications. Voir Section 32.20.1 pour plus d'informations sur l'ajout d'un flux personnalisé à libpq.

Pour qu'un flux client OAuth soit utilisable, la chaîne de connexion doit contenir au minimum oauth_issuer et oauth_client_id. (Ces paramètres sont déterminés par le fournisseur OAuth de votre organisation.) Le flux natif nécessite en plus que le serveur d'autorisation OAuth publie un endpoint d'autorisation du périphérique.

Note

Le flux natif d'autorisation du périphérique n'est actuellement pas accepté sur Windows. Les flux clients personnalisés peuvent toujours être implémentés.

32.20.1. Hooks Authdata #

Le comportement d'un flux OAuth peut être modifié ou remplacé par un client en utilisant l'API des hooks suivantes :

PQsetAuthDataHook #

Initialise PGauthDataHook, surchargeant la gestion par libpq d'un ou plusieurs aspects du client OAuth.

void PQsetAuthDataHook(PQauthDataHook_type hook);

Si hook vaut NULL, le gestionnaire par défaut sera réinstallé. Sinon l'application passe un pointeur à une fonction callback dont la signature est :

int hook_fn(PGauthData type, PGconn *conn, void *data);

que libpq appellera quand une action est requise de l'application. type décrit la requête faite, conn est le pointeur vers la connexion en cours d'authentification, et data pointe vers les métadonnées spécifiques à la requête. Le contenu de ce pointeur est déterminé par type ; voir Section 32.20.1.1 pour la liste supportée.

Les hooks peuvent être chaînés ensemble pour permettre un comportement coopératif et/ou une solution de repli. En général, l'implémentation d'un hook doit examiner le type en entrée (et, potentiellement, les métadonnées de la requête et/ou la configuration pour la connexion conn en cours d'utilisation) pour décider de gérer ou non une partie spécifique de authdata. Dans le cas contraire, il doit déléguer au hook précédent dans la chaîne (récupérable via PQgetAuthDataHook).

Le succès est indiqué en renvoyant un entier positif. Un entier négatif signale une condition d'erreur et provoque l'abandon de la tentative de connexion. (Une valeur zéro est réservée pour l'implémentation par défaut.)

PQgetAuthDataHook #

Récupère la valeur actuelle de PGauthDataHook.

PQauthDataHook_type PQgetAuthDataHook(void);

Au moment de l'initialisation (avant le premier appel à PQsetAuthDataHook), cette fonction renverra PQdefaultAuthDataHook.

32.20.1.1. Types de hook #

Les types PGauthData suivants et leur structure data correspondant sont définis :

PQAUTHDATA_PROMPT_OAUTH_DEVICE #

Remplace l'invite par défaut de l'utilisateur lors du flux client natif d'autorisation du périphérique. data pointe vers une instance de PGpromptOAuthDevice :

typedef struct _PGpromptOAuthDevice
{
    const char *verification_uri;   /* verification URI to visit */
    const char *user_code;          /* user code to enter */
    const char *verification_uri_complete;  /* optional combination of URI and
                                             * code, or NULL */
    int         expires_in;         /* seconds until user code expires */
} PGpromptOAuthDevice;

Le flux d'autorisation de périphérique OAuth qui peut être inclus dans libpq nécessite que l'utilisateur final visite une URL avec un navigateur puis saisisse un code qui permet à libpq de se connecter au serveur en leur nom. L'invite par défaut affiche seulement le verification_uri et le user_code sur la sortie des erreurs. Les implémentations de remplacement pourraient afficher cette information en utilisant toute autre méthode, par exemple avec une interface graphique.

Ce callback est seulement appelé lors du flux d'autorisation natif du périphérique. Si l'application installe un flux OAuth personnalisé ou que libpq n'était pas compilé avec le support du flux natif, ce type authdata ne sera pas utilisé.

Si un verification_uri_complete non NULL est fourni, il pourrait être utilisé en option pour une vérification non textuelle (par exemple en affichant un QR-code). L'URL et le code utilisateur devront toujours être affichés à l'utilisateur final dans ce cas car le code devra être manuellement confirmé par le fournisseur et l'URL laisse les utilisateurs continuer, même s'ils ne peuvent pas utiliser la méthode non textuelle. Pour plus d'informations, voir la section 3.3.1 dans la RFC 8628.

PQAUTHDATA_OAUTH_BEARER_TOKEN #

Ajoute une implémentation personnalisée d'un flux, remplaçant le flux natif s'il est installé. Le hook doit soit renvoyer directement un jeton du porteur pour la combinaison utilisateur/fournisseur/portée, si une est disponible sans blocage, soit configurer un callback asynchrone pour en récupérer un.

data pointe vers une instance de PGoauthBearerRequest, qui doit être remplie par cette implémentation :

typedef struct PGoauthBearerRequest
{
    /* Entrées du Hook (constantes pour tous les appels) */
    const char *openid_configuration; /* URL de découverte OIDC */
    const char *scope;                /* portée(s) requise(s), ou NULL */

    /* Sorties du Hook */

    /* Callback implémentant un flux personnalisé OAuth asynchrone. */
    PostgresPollingStatusType (*async) (PGconn *conn,
                                        struct PGoauthBearerRequest *request,
                                        SOCKTYPE *altsock);

    /* Callback pour nettoyer des allocations personnalisées. */
    void        (*cleanup) (PGconn *conn, struct PGoauthBearerRequest *request);

    char       *token;   /* jeton du porteur acquis */
    void       *user;    /* données allouées définies par le hook */
} PGoauthBearerRequest;

Deux pièces d'information sont fournies au hook par libpq : openid_configuration contient l'URL d'un document de découverte OAuth décrivant les flux acceptés par le serveur d'autorisation, et scope contient une liste (potentiellement vide) de portées OAuth séparées par des espace, requises pour accéder au serveur. Ils peuvent être NULL pour indiquer que l'information n'est pas disponible. (Dans ce cas, les implémentations pourraient être capables d'établir les prérequis en utilisant une certaine connaissance pré-configurée, ou elles pourraient choisir d'échouer.)

La sortie finale du hook est token, qui doit pointer vers un jeton valide du porteur pour l'utiliser sur la connexion. (Ce jeton doit être fourni par oauth_issuer et contenir les portées demandées, sinon la connexion sera rejetée par le module de validation du serveur.) La chaîne allouée du jeton doit rester valide jusqu'à ce que libpq ait terminé l'étape de connexion ; le hook doit configurer un callback cleanup qui sera appelé quand libpq n'en a plus besoin.

Si une implémentation ne peut pas immédiatement produire un jeton (token) lors de l'appel initial au hook, il doit configurer le callback async pour gérer une communication non bloquante avec le serveur d'autorisation. [16] Elle sera appelée pour commencer le flux immédiatement au retour du hook. Quand le callback ne peut plus avancer sans bloquer, il doit renvoyer soit PGRES_POLLING_READING soit PGRES_POLLING_WRITING après avoir configuré *pgsocket sur le descripteur de fichier qui sera marqué prêt en lecture/écriture quand la progression pourra reprendre. (Ce descripteur est ensuite fourniau haut niveau de la boucle d'interrogation via PQsocket().) Renvoyez PGRES_POLLING_OK après avoir configuré token quand le flux est terminé, ou PGRES_POLLING_FAILED pour indiquer l'échec.

Les implémentations pourraient vouloir enregistrer des données supplémentaires entre les appels aux callbacks async et cleanup. Le pointeur user est fourni dans ce but ; libpq ne touchera pas à son contenu et les applications pourraient l'utiliser à leur convenance. (Rappelez-vous de libérer toute allocation pendant le nettoyage du jeton.)

32.20.2. Paramètres de débogage et pour les développeurs #

Un « mode de debogage dangereux » peut être activé en configurant la variable d'environnement PGOAUTHDEBUG=UNSAFE. Cette fonctionnalité est fournie pour faciliter uniquement le développement local et les tests. Il fait plusieurs choses que vous ne voulez pas sur un système de production :

  • permet l'utilisation d'HTTP (donc non chiffré) pendant l'échange avec le fournisseur OAuth

  • permet de remplacer la liste CA de confiance du système avec le contenu de la variable d'environnement PGOAUTHCAFILE

  • affiche le trafic HTTP (contenant plusieurs secrets critiques) sur la sortie des erreurs pendant le flux OAuth

  • permet l'utilisation d'intervalles de nouvelles tentatives à zéro seconde qui peut causer le fait que le client entre dans une boucle et consomme sans raison du CPU

Avertissement

Ne partagez pas la sortie d'un trafic de flux OAuth avec des tierces parties. Il contient des secrets qui peuvent être utilisés pour attaquer vos clients et serveurs.



[16] Réaliser des opérations bloquantes lors de l'appel du hook PQAUTHDATA_OAUTH_BEARER_TOKEN interférera avec l'API de connexion non bloquante, tel que PQconnectPoll et empêchera les connexions concurrentes de progresser. Les applications qui utilisent seulement les primitives de connexion synchrones, comme PQconnectdb, pourraient récupérer en synchrone un jeton provenant du hook au lieu d'implémenter le callback async mais elles seront nécessairement limitées à une connexion à la fois.