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.
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.
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
.
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.)
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
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.