public inbox for [email protected]  
help / color / mirror / Atom feed
From: Jacob Champion <[email protected]>
To: Zsolt Parragi <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Subject: Re: [oauth] Split and extend PGOAUTHDEBUG
Date: Wed, 1 Apr 2026 11:50:42 -0700
Message-ID: <CAOYmi+=865C93VegSzD9z4_uvutZwEZEUsS4P6jm3_i0paAMmA@mail.gmail.com> (raw)
In-Reply-To: <CAOYmi+mN2OeFotwLKwwSDAuXHJ6xmrn3AtkUoHRcfAEENSFbMA@mail.gmail.com>
References: <CAN4CZFMmDZMH56O9vb_g7vHqAk8ryWFxBMV19C39PFghENg8kA@mail.gmail.com>
	<CAOYmi+k_et3yXpJ8op71-95j7OYg-kX5bWLgW9YTV_5G7f+O1A@mail.gmail.com>
	<CAOYmi+kivcSnazEJA=KWknd3azGYnU3mMq9SUvht5Zq74qNcYQ@mail.gmail.com>
	<CAN4CZFMKCB2OXPGW0R_hCSu4Gg==B7dBSrv6Mf-YuFcrUncADg@mail.gmail.com>
	<CAOYmi+kCYZ3YiOu+oSv1gVW6LXQaNg4BcEpskYizWgfV1z12kA@mail.gmail.com>
	<CAN4CZFPNL4xuNAwmD1-TUR-z+C84axb+cdhip26k3YBFYo9r1g@mail.gmail.com>
	<CAOYmi+mN2OeFotwLKwwSDAuXHJ6xmrn3AtkUoHRcfAEENSFbMA@mail.gmail.com>

On Wed, Apr 1, 2026 at 10:09 AM Jacob Champion
<[email protected]> wrote:
> I didn't intend to introduce any new calls to
> oauth_get_debug_flags() over those already done in v1/v2; if I did
> that's a bug.

To make seeing that a little easier, here's the promised version of v3
as an exploded patch series with more detailed justification, based on
v2-0001.

I'm glad I did that, because I forgot to call attention to a
particular change I made that I think is important:

>             fprintf(stderr,
> -                   "WARNING: PGOAUTHDEBUG: unsafe option \"%s\" requires UNSAFE: prefix (ignored)\n"
> -                   "Use: PGOAUTHDEBUG=UNSAFE:%s\n",
> -                   option, option);
> +                   libpq_gettext("WARNING: PGOAUTHDEBUG option \"%s\" is unsafe (ignored)\n"),
> +                   option);

`UNSAFE` is intended to be a weak defense against social engineering
attacks. So these warnings need to be translated, if possible, and we
should not provide instructions on how to defeat that defense. The
only people who _should_ be using an unsafe feature should also know
how to fix this problem.

--Jacob


Attachments:

  [application/octet-stream] v3.1-0001-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-options.patch (19.5K, 2-v3.1-0001-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-options.patch)
  download | inline diff:
From 0b197fbb28720dda6fe36ae483dcd83d2ddb1e40 Mon Sep 17 00:00:00 2001
From: Zsolt Parragi <[email protected]>
Date: Thu, 11 Dec 2025 23:56:08 +0000
Subject: [PATCH v3.1 1/6] Split PGOAUTHDEBUG=UNSAFE into multiple options

---
 doc/src/sgml/libpq.sgml                      | 127 +++++++++++++----
 src/interfaces/libpq-oauth/meson.build       |   6 +-
 src/interfaces/libpq/meson.build             |   1 +
 src/interfaces/libpq-oauth/Makefile          |  11 +-
 src/interfaces/libpq/Makefile                |   3 +-
 src/interfaces/libpq-oauth/oauth-utils.h     |   3 +-
 src/interfaces/libpq/fe-auth-oauth.h         |  18 ++-
 src/interfaces/libpq-oauth/oauth-curl.c      |  16 +--
 src/interfaces/libpq-oauth/oauth-utils.c     |  11 --
 src/interfaces/libpq-oauth/test-oauth-curl.c |   6 +-
 src/interfaces/libpq/fe-auth-oauth-debug.c   | 140 +++++++++++++++++++
 src/interfaces/libpq/fe-auth-oauth.c         |  17 +--
 12 files changed, 295 insertions(+), 64 deletions(-)
 create mode 100644 src/interfaces/libpq/fe-auth-oauth-debug.c

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index a48d3161495..2e5fb9011e9 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -10643,35 +10643,112 @@ typedef struct
    </para>
 
    <para>
-    A "dangerous debugging mode" may be enabled by setting the environment
-    variable <envar>PGOAUTHDEBUG=UNSAFE</envar>. This functionality is provided
-    for ease of local development and testing only. It does several things that
-    you will not want a production system to do:
+    Debug features may be enabled by setting the <envar>PGOAUTHDEBUG</envar>
+    environment variable. This functionality is provided for ease of local
+    development and testing. The variable accepts a comma-separated list of
+    debug options:
+
+    <programlisting>
+PGOAUTHDEBUG=option1,option2,...    <lineannotation>for safe options only</lineannotation>
+PGOAUTHDEBUG=UNSAFE:option1,option2,...    <lineannotation>when using unsafe options</lineannotation>
+PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy format; enables all options</lineannotation>
+    </programlisting>
+   </para>
 
-    <itemizedlist spacing="compact">
-     <listitem>
-      <para>
-       permits the use of unencrypted HTTP during the OAuth provider exchange
-      </para>
-     </listitem>
-     <listitem>
-      <para>
-       prints HTTP traffic (containing several critical secrets) to standard
-       error during the OAuth flow
-      </para>
-     </listitem>
-     <listitem>
-      <para>
-       permits the use of zero-second retry intervals, which can cause the
-       client to busy-loop and pointlessly consume CPU
-      </para>
-     </listitem>
-    </itemizedlist>
+   <para>
+    Available debug options:
+
+    <variablelist>
+     <varlistentry>
+      <term><literal>http</literal> (unsafe)</term>
+      <listitem>
+       <para>
+        Permits the use of unencrypted HTTP during the OAuth provider exchange.
+        This allows OAuth credentials to be transmitted over unencrypted
+        connections, which is extremely dangerous and should only be used for
+        local testing.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>trace</literal> (unsafe)</term>
+      <listitem>
+       <para>
+        Prints HTTP traffic to standard error during the OAuth flow. This output
+        contains critical secrets including bearer tokens, client secrets, access
+        tokens, and authorization codes. Never share this output with third
+        parties.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>fast-retry</literal> (safe)</term>
+      <listitem>
+       <para>
+        Permits the use of zero-second retry intervals instead of the normal
+        minimum of one second. This can speed up tests but may cause the client
+        to busy-loop and consume CPU unnecessarily.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>poll-counts</literal> (safe)</term>
+      <listitem>
+       <para>
+        Prints the total number of poll() calls to standard error when the
+        OAuth flow completes. This helps developers debug the async multiplexer
+        behavior.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>print-plugin-errors</literal> (safe)</term>
+      <listitem>
+       <para>
+        Prints plugin loading errors to standard error. This helps developers
+        and package maintainers debug issues when the OAuth plugin fails to load.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+
+   <para>
+    Unsafe options (<literal>http</literal>, <literal>trace</literal>)
+    require the <literal>UNSAFE:</literal> prefix.
+    If unsafe options are specified without this prefix, a warning is printed
+    to standard error and that option is ignored. Other valid options in the
+    list continue to work. Safe options (<literal>fast-retry</literal>,
+    <literal>poll-counts</literal>, <literal>print-plugin-errors</literal>) can
+    be used without the prefix.
    </para>
+
+   <para>
+    Unrecognized option names will also trigger a warning and be ignored, while
+    valid options continue to work. This helps catch typos in the environment
+    variable configuration without breaking the debugging of valid options.
+   </para>
+
+   <para>
+    Examples:
+    <programlisting>
+PGOAUTHDEBUG=fast-retry,poll-counts    <lineannotation>safe options only</lineannotation>
+PGOAUTHDEBUG=UNSAFE:http,trace    <lineannotation>enable HTTP and traffic logging</lineannotation>
+PGOAUTHDEBUG=UNSAFE:http,poll-counts    <lineannotation>mix of unsafe and safe</lineannotation>
+PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy; enables all options</lineannotation>
+    </programlisting>
+   </para>
+
    <warning>
     <para>
-     Do not share the output of the OAuth flow traffic with third parties. It
-     contains secrets that can be used to attack your clients and servers.
+     Never use unsafe debug options in production environments. The
+     <literal>trace</literal> option in particular exposes secrets that can be
+     used to attack your clients and servers. Do not share the output with third
+     parties.
     </para>
    </warning>
   </sect2>
diff --git a/src/interfaces/libpq-oauth/meson.build b/src/interfaces/libpq-oauth/meson.build
index ea3a900f4f1..d8cc92e0c2c 100644
--- a/src/interfaces/libpq-oauth/meson.build
+++ b/src/interfaces/libpq-oauth/meson.build
@@ -6,6 +6,7 @@ endif
 
 libpq_oauth_sources = files(
   'oauth-curl.c',
+  '../libpq/fe-auth-oauth-debug.c',
 )
 
 # The shared library needs additional glue symbols.
@@ -62,7 +63,10 @@ endif
 
 libpq_oauth_test_deps = []
 
-oauth_test_sources = files('test-oauth-curl.c') + libpq_oauth_so_sources
+oauth_test_sources = files(
+  'test-oauth-curl.c',
+  '../libpq/fe-auth-oauth-debug.c',
+) + libpq_oauth_so_sources
 
 if host_system == 'windows'
   oauth_test_sources += rc_bin_gen.process(win32ver_rc, extra_args: [
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index b0ae72167a1..d031f4962e5 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -2,6 +2,7 @@
 
 libpq_sources = files(
   'fe-auth-oauth.c',
+  'fe-auth-oauth-debug.c',
   'fe-auth-scram.c',
   'fe-auth.c',
   'fe-cancel.c',
diff --git a/src/interfaces/libpq-oauth/Makefile b/src/interfaces/libpq-oauth/Makefile
index 11e1a3cf528..c6097dda531 100644
--- a/src/interfaces/libpq-oauth/Makefile
+++ b/src/interfaces/libpq-oauth/Makefile
@@ -36,15 +36,24 @@ override CPPFLAGS_SHLIB += -DUSE_PRIVATE_ENCODING_FUNCS
 OBJS = \
 	$(WIN32RES)
 
-OBJS_STATIC = oauth-curl.o
+OBJS_STATIC = \
+	oauth-curl.o \
+	fe-auth-oauth-debug.o
 
 # The shared library needs additional glue symbols.
 OBJS_SHLIB = \
 	oauth-curl_shlib.o \
 	oauth-utils.o \
+	fe-auth-oauth-debug_shlib.o
 
 oauth-utils.o: override CPPFLAGS += $(CPPFLAGS_SHLIB)
 
+fe-auth-oauth-debug.o: $(libpq_srcdir)/fe-auth-oauth-debug.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+fe-auth-oauth-debug_shlib.o: $(libpq_srcdir)/fe-auth-oauth-debug.c fe-auth-oauth-debug.o
+	$(CC) $(CFLAGS) $(CFLAGS_SL) $(CPPFLAGS) $(CPPFLAGS_SHLIB) -c $< -o $@
+
 # Add shlib-/stlib-specific objects.
 $(shlib): override OBJS += $(OBJS_SHLIB)
 $(shlib): $(OBJS_SHLIB)
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 0963995eed4..099c6557e77 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -44,7 +44,8 @@ OBJS = \
 	legacy-pqsignal.o \
 	libpq-events.o \
 	pqexpbuffer.o \
-	fe-auth.o
+	fe-auth.o \
+	fe-auth-oauth-debug.o
 
 # File shared across all SSL implementations supported.
 ifneq ($(with_ssl),no)
diff --git a/src/interfaces/libpq-oauth/oauth-utils.h b/src/interfaces/libpq-oauth/oauth-utils.h
index 293e9936989..dd4e38d525c 100644
--- a/src/interfaces/libpq-oauth/oauth-utils.h
+++ b/src/interfaces/libpq-oauth/oauth-utils.h
@@ -15,6 +15,7 @@
 #ifndef OAUTH_UTILS_H
 #define OAUTH_UTILS_H
 
+#include "fe-auth-oauth.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
 
@@ -35,7 +36,7 @@ typedef enum
 	PG_BOOL_NO					/* No (false) */
 } PGTernaryBool;
 
-extern bool oauth_unsafe_debugging_enabled(void);
+extern oauth_debug_flags oauth_get_debug_flags(void);
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe);
 
diff --git a/src/interfaces/libpq/fe-auth-oauth.h b/src/interfaces/libpq/fe-auth-oauth.h
index 872f5df551a..859975bcd67 100644
--- a/src/interfaces/libpq/fe-auth-oauth.h
+++ b/src/interfaces/libpq/fe-auth-oauth.h
@@ -39,8 +39,24 @@ typedef struct
 	void	   *flow_module;
 } fe_oauth_state;
 
+/*
+ * Debug flags for PGOAUTHDEBUG environment variable.
+ * Each flag controls a specific debug feature.
+ */
+typedef struct oauth_debug_flags
+{
+	/* UNSAFE features - require UNSAFE: prefix */
+	bool		http;			/* allow HTTP (unencrypted) connections */
+	bool		trace;			/* log HTTP traffic (exposes secrets) */
+
+	/* SAFE features - allowed without UNSAFE: prefix */
+	bool		fast_retry;		/* allow zero-second retry intervals */
+	bool		poll_counts;	/* print poll() statistics */
+	bool		print_plugin_errors;	/* print plugin loading errors */
+} oauth_debug_flags;
+
 extern void pqClearOAuthToken(PGconn *conn);
-extern bool oauth_unsafe_debugging_enabled(void);
+extern oauth_debug_flags oauth_get_debug_flags(void);
 
 /* Mechanisms in fe-auth-oauth.c */
 extern const pg_fe_sasl_mech pg_oauth_mech;
diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index 3baede1b2e7..564d76cf063 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -274,7 +274,7 @@ struct async_ctx
 	int			running;		/* is asynchronous work in progress? */
 	bool		user_prompted;	/* have we already sent the authz prompt? */
 	bool		used_basic_auth;	/* did we send a client secret? */
-	bool		debugging;		/* can we give unsafe developer assistance? */
+	oauth_debug_flags debug_flags;	/* can we give developer assistance */
 	int			dbg_num_calls;	/* (debug mode) how many times were we called? */
 };
 
@@ -1023,7 +1023,7 @@ parse_interval(struct async_ctx *actx, const char *interval_str)
 	parsed = ceil(parsed);
 
 	if (parsed < 1)
-		return actx->debugging ? 0 : 1;
+		return actx->debug_flags.fast_retry ? 0 : 1;
 
 	else if (parsed >= INT_MAX)
 		return INT_MAX;
@@ -1797,7 +1797,7 @@ setup_curl_handles(struct async_ctx *actx)
 	 */
 	CHECK_SETOPT(actx, CURLOPT_NOSIGNAL, 1L, return false);
 
-	if (actx->debugging)
+	if (actx->debug_flags.trace)
 	{
 		/*
 		 * Set a callback for retrieving error information from libcurl, the
@@ -1829,7 +1829,7 @@ setup_curl_handles(struct async_ctx *actx)
 		const long	unsafe = CURLPROTO_HTTPS | CURLPROTO_HTTP;
 #endif
 
-		if (actx->debugging)
+		if (actx->debug_flags.http)
 			protos = unsafe;
 
 		CHECK_SETOPT(actx, popt, protos, return false);
@@ -2297,7 +2297,7 @@ check_for_device_flow(struct async_ctx *actx)
 	 * decent time to bail out if we're not using HTTPS for the endpoints
 	 * we'll use for the flow.
 	 */
-	if (!actx->debugging)
+	if (!actx->debug_flags.http)
 	{
 		if (pg_strncasecmp(provider->device_authorization_endpoint,
 						   HTTPS_SCHEME, strlen(HTTPS_SCHEME)) != 0)
@@ -3027,7 +3027,7 @@ pg_fe_run_oauth_flow(PGconn *conn, struct PGoauthBearerRequest *request,
 	 * drain_timer_events(), when we're in debug mode, track the total number
 	 * of calls to this function and print that at the end of the flow.
 	 */
-	if (actx->debugging)
+	if (actx && actx->debug_flags.poll_counts)
 	{
 		actx->dbg_num_calls++;
 		if (result == PGRES_POLLING_OK || result == PGRES_POLLING_FAILED)
@@ -3087,8 +3087,8 @@ pg_start_oauthbearer(PGconn *conn, PGoauthBearerRequestV2 *request)
 	 * Now finish filling in the actx.
 	 */
 
-	/* Should we enable unsafe features? */
-	actx->debugging = oauth_unsafe_debugging_enabled();
+	/* Parse debug flags from the environment. */
+	actx->debug_flags = oauth_get_debug_flags();
 
 	initPQExpBuffer(&actx->work_data);
 	initPQExpBuffer(&actx->errbuf);
diff --git a/src/interfaces/libpq-oauth/oauth-utils.c b/src/interfaces/libpq-oauth/oauth-utils.c
index ccb0d9bf2c5..004d41f02aa 100644
--- a/src/interfaces/libpq-oauth/oauth-utils.c
+++ b/src/interfaces/libpq-oauth/oauth-utils.c
@@ -75,17 +75,6 @@ libpq_gettext(const char *msgid)
 
 #endif							/* ENABLE_NLS */
 
-/*
- * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
- */
-bool
-oauth_unsafe_debugging_enabled(void)
-{
-	const char *env = getenv("PGOAUTHDEBUG");
-
-	return (env && strcmp(env, "UNSAFE") == 0);
-}
-
 /*
  * Duplicate SOCK_ERRNO* definitions from libpq-int.h, for use by
  * pq_block/reset_sigpipe().
diff --git a/src/interfaces/libpq-oauth/test-oauth-curl.c b/src/interfaces/libpq-oauth/test-oauth-curl.c
index 4328a332738..06815be9a0a 100644
--- a/src/interfaces/libpq-oauth/test-oauth-curl.c
+++ b/src/interfaces/libpq-oauth/test-oauth-curl.c
@@ -89,7 +89,11 @@ init_test_actx(void)
 
 	actx->mux = PGINVALID_SOCKET;
 	actx->timerfd = -1;
-	actx->debugging = true;
+	actx->debug_flags.http = true;
+	actx->debug_flags.trace = true;
+	actx->debug_flags.fast_retry = true;
+	actx->debug_flags.poll_counts = true;
+	actx->debug_flags.print_plugin_errors = true;
 
 	initPQExpBuffer(&actx->errbuf);
 
diff --git a/src/interfaces/libpq/fe-auth-oauth-debug.c b/src/interfaces/libpq/fe-auth-oauth-debug.c
new file mode 100644
index 00000000000..f9a1b1f195f
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth-oauth-debug.c
@@ -0,0 +1,140 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth-oauth-debug.c
+ *	  Parsing logic for PGOAUTHDEBUG environment variable
+ *
+ * This file contains pure string parsing logic with no dependencies on
+ * libpq or libpq-oauth implementation details. It's compiled into both
+ * libraries to avoid code duplication.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * 	src/interfaces/libpq/fe-auth-oauth-debug.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fe-auth-oauth.h"
+
+/*
+ * Parse a single debug option from PGOAUTHDEBUG.
+ * Returns true if the option is recognized, false otherwise.
+ * Sets *is_unsafe to indicate if this option requires the UNSAFE: prefix.
+ */
+static bool
+parse_debug_option(const char *option, oauth_debug_flags *flags, bool *is_unsafe)
+{
+	*is_unsafe = false;
+
+	/* Unsafe options */
+	if (strcmp(option, "http") == 0)
+	{
+		flags->http = true;
+		*is_unsafe = true;
+		return true;
+	}
+	else if (strcmp(option, "trace") == 0)
+	{
+		flags->trace = true;
+		*is_unsafe = true;
+		return true;
+	}
+	/* Safe options */
+	else if (strcmp(option, "fast-retry") == 0)
+	{
+		flags->fast_retry = true;
+		return true;
+	}
+	else if (strcmp(option, "poll-counts") == 0)
+	{
+		flags->poll_counts = true;
+		return true;
+	}
+	else if (strcmp(option, "print-plugin-errors") == 0)
+	{
+		flags->print_plugin_errors = true;
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Parses the PGOAUTHDEBUG environment variable and returns debug flags.
+ *
+ * Supported formats:
+ *   PGOAUTHDEBUG=UNSAFE              - legacy format, enables all features
+ *   PGOAUTHDEBUG=option1,option2     - enable safe features only
+ *   PGOAUTHDEBUG=UNSAFE:opt1,opt2    - enable unsafe and/or safe features
+ *
+ * Prints a warning and skips the invalid option if:
+ * - An unrecognized option is specified
+ * - An unsafe option is specified without the UNSAFE: prefix
+ */
+oauth_debug_flags
+oauth_get_debug_flags(void)
+{
+	oauth_debug_flags flags = {0};
+	const char *env = getenv("PGOAUTHDEBUG");
+	char	   *options_str;
+	char	   *option;
+	char	   *saveptr = NULL;
+	bool		unsafe_prefix = false;
+
+	if (!env || env[0] == '\0')
+		return flags;
+
+	if (strcmp(env, "UNSAFE") == 0)
+	{
+		flags.http = true;
+		flags.trace = true;
+		flags.fast_retry = true;
+		flags.poll_counts = true;
+		flags.print_plugin_errors = true;
+		return flags;
+	}
+
+	if (strncmp(env, "UNSAFE:", 7) == 0)
+	{
+		unsafe_prefix = true;
+		env += 7;
+	}
+
+	options_str = strdup(env);
+	if (!options_str)
+		return flags;
+
+	option = strtok_r(options_str, ",", &saveptr);
+	while (option != NULL)
+	{
+		bool		is_unsafe;
+
+		if (!parse_debug_option(option, &flags, &is_unsafe))
+		{
+			fprintf(stderr,
+					"WARNING: PGOAUTHDEBUG: unrecognized debug option \"%s\" (ignored)\n",
+					option);
+		}
+		else if (is_unsafe && !unsafe_prefix)
+		{
+			fprintf(stderr,
+					"WARNING: PGOAUTHDEBUG: unsafe option \"%s\" requires UNSAFE: prefix (ignored)\n"
+					"Use: PGOAUTHDEBUG=UNSAFE:%s\n",
+					option, option);
+		}
+
+		option = strtok_r(NULL, ",", &saveptr);
+	}
+
+	free(options_str);
+
+	return flags;
+}
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index ac03d1d4f9d..5f5900a9ae7 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -389,7 +389,7 @@ issuer_from_well_known_uri(PGconn *conn, const char *wkuri)
 		authority_start = wkuri + strlen(HTTPS_SCHEME);
 
 	if (!authority_start
-		&& oauth_unsafe_debugging_enabled()
+		&& oauth_get_debug_flags().http
 		&& pg_strncasecmp(wkuri, HTTP_SCHEME, strlen(HTTP_SCHEME)) == 0)
 	{
 		/* Allow http:// for testing only. */
@@ -900,7 +900,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re
 		 *
 		 * Note that POSIX dlerror() isn't guaranteed to be threadsafe.
 		 */
-		if (oauth_unsafe_debugging_enabled())
+		if (oauth_get_debug_flags().print_plugin_errors)
 			fprintf(stderr, "failed dlopen for libpq-oauth: %s\n", dlerror());
 
 		return 0;
@@ -922,7 +922,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re
 		 * cause is still locked behind PGOAUTHDEBUG due to the dlerror()
 		 * threadsafety issue.
 		 */
-		if (oauth_unsafe_debugging_enabled())
+		if (oauth_get_debug_flags().print_plugin_errors)
 			fprintf(stderr, "failed dlsym for libpq-oauth: %s\n", dlerror());
 
 		dlclose(state->flow_module);
@@ -1437,17 +1437,6 @@ pqClearOAuthToken(PGconn *conn)
 	conn->oauth_token = NULL;
 }
 
-/*
- * Returns true if the PGOAUTHDEBUG=UNSAFE flag is set in the environment.
- */
-bool
-oauth_unsafe_debugging_enabled(void)
-{
-	const char *env = getenv("PGOAUTHDEBUG");
-
-	return (env && strcmp(env, "UNSAFE") == 0);
-}
-
 /*
  * Hook v1 Poisoning
  *
-- 
2.34.1



  [application/octet-stream] v3.1-0002-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch (6.1K, 3-v3.1-0002-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch)
  download | inline diff:
From 6219a268df4fefdd99bf64a3c2de9548338cbc62 Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Wed, 1 Apr 2026 10:29:16 -0700
Subject: [PATCH v3.1 2/6] squash! Split PGOAUTHDEBUG=UNSAFE into multiple
 options

- Rename user-facing options in parsing and documentation.
- Move dos-endpoint to UNSAFE.
- Realign lineannotations and update doc descriptions.
---
 doc/src/sgml/libpq.sgml                    | 45 ++++++++++------------
 src/interfaces/libpq/fe-auth-oauth-debug.c |  9 +++--
 2 files changed, 26 insertions(+), 28 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 2e5fb9011e9..01a65419f99 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -10649,9 +10649,9 @@ typedef struct
     debug options:
 
     <programlisting>
-PGOAUTHDEBUG=option1,option2,...    <lineannotation>for safe options only</lineannotation>
-PGOAUTHDEBUG=UNSAFE:option1,option2,...    <lineannotation>when using unsafe options</lineannotation>
-PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy format; enables all options</lineannotation>
+PGOAUTHDEBUG=option1,option2,...         <lineannotation>for safe options only</lineannotation>
+PGOAUTHDEBUG=UNSAFE:option1,option2,...  <lineannotation>when using unsafe options</lineannotation>
+PGOAUTHDEBUG=UNSAFE                      <lineannotation>legacy format; enables all options</lineannotation>
     </programlisting>
    </para>
 
@@ -10684,29 +10684,29 @@ PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy format; enables all options</linea
      </varlistentry>
 
      <varlistentry>
-      <term><literal>fast-retry</literal> (safe)</term>
+      <term><literal>dos-endpoint</literal> (unsafe)</term>
       <listitem>
        <para>
         Permits the use of zero-second retry intervals instead of the normal
-        minimum of one second. This can speed up tests but may cause the client
-        to busy-loop and consume CPU unnecessarily.
+        minimum of one second. This speeds up tests, but in normal operation it
+        will cause the client to busy-loop, consuming CPU and network resources.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><literal>poll-counts</literal> (safe)</term>
+      <term><literal>call-count</literal> (safe)</term>
       <listitem>
        <para>
-        Prints the total number of poll() calls to standard error when the
-        OAuth flow completes. This helps developers debug the async multiplexer
-        behavior.
+        Prints the total number of calls to the flow plugin to standard error
+        when the OAuth flow completes. This helps developers debug the async
+        callback behavior.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><literal>print-plugin-errors</literal> (safe)</term>
+      <term><literal>plugin-errors</literal> (safe)</term>
       <listitem>
        <para>
         Prints plugin loading errors to standard error. This helps developers
@@ -10718,13 +10718,12 @@ PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy format; enables all options</linea
    </para>
 
    <para>
-    Unsafe options (<literal>http</literal>, <literal>trace</literal>)
-    require the <literal>UNSAFE:</literal> prefix.
+    Unsafe options (<literal>http</literal>, <literal>trace</literal>,
+    <literal>dos-endpoint</literal>) require the <literal>UNSAFE:</literal> prefix.
     If unsafe options are specified without this prefix, a warning is printed
     to standard error and that option is ignored. Other valid options in the
-    list continue to work. Safe options (<literal>fast-retry</literal>,
-    <literal>poll-counts</literal>, <literal>print-plugin-errors</literal>) can
-    be used without the prefix.
+    list continue to work. Safe options (<literal>call-count</literal>,
+    <literal>plugin-errors</literal>) can be used without the prefix.
    </para>
 
    <para>
@@ -10736,19 +10735,17 @@ PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy format; enables all options</linea
    <para>
     Examples:
     <programlisting>
-PGOAUTHDEBUG=fast-retry,poll-counts    <lineannotation>safe options only</lineannotation>
-PGOAUTHDEBUG=UNSAFE:http,trace    <lineannotation>enable HTTP and traffic logging</lineannotation>
-PGOAUTHDEBUG=UNSAFE:http,poll-counts    <lineannotation>mix of unsafe and safe</lineannotation>
-PGOAUTHDEBUG=UNSAFE    <lineannotation>legacy; enables all options</lineannotation>
+PGOAUTHDEBUG=call-count              <lineannotation>safe options only</lineannotation>
+PGOAUTHDEBUG=UNSAFE:http,trace       <lineannotation>enable HTTP and traffic logging</lineannotation>
+PGOAUTHDEBUG=UNSAFE:http,call-count  <lineannotation>mix of unsafe and safe</lineannotation>
     </programlisting>
    </para>
 
    <warning>
     <para>
-     Never use unsafe debug options in production environments. The
-     <literal>trace</literal> option in particular exposes secrets that can be
-     used to attack your clients and servers. Do not share the output with third
-     parties.
+     Never use unsafe debug options in production environments. They expose
+     secrets and behaviors that can be used to attack your clients and servers.
+     Do not share <literal>trace</literal> output with third parties.
     </para>
    </warning>
   </sect2>
diff --git a/src/interfaces/libpq/fe-auth-oauth-debug.c b/src/interfaces/libpq/fe-auth-oauth-debug.c
index f9a1b1f195f..957da5d4068 100644
--- a/src/interfaces/libpq/fe-auth-oauth-debug.c
+++ b/src/interfaces/libpq/fe-auth-oauth-debug.c
@@ -47,18 +47,19 @@ parse_debug_option(const char *option, oauth_debug_flags *flags, bool *is_unsafe
 		*is_unsafe = true;
 		return true;
 	}
-	/* Safe options */
-	else if (strcmp(option, "fast-retry") == 0)
+	else if (strcmp(option, "dos-endpoint") == 0)
 	{
 		flags->fast_retry = true;
+		*is_unsafe = true;
 		return true;
 	}
-	else if (strcmp(option, "poll-counts") == 0)
+	/* Safe options */
+	else if (strcmp(option, "call-count") == 0)
 	{
 		flags->poll_counts = true;
 		return true;
 	}
-	else if (strcmp(option, "print-plugin-errors") == 0)
+	else if (strcmp(option, "plugin-errors") == 0)
 	{
 		flags->print_plugin_errors = true;
 		return true;
-- 
2.34.1



  [application/octet-stream] v3.1-0003-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch (9.7K, 4-v3.1-0003-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch)
  download | inline diff:
From fb26bb8b21e2c873d5c4cfd8fd54be692e67aaff Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Wed, 1 Apr 2026 10:58:46 -0700
Subject: [PATCH v3.1 3/6] squash! Split PGOAUTHDEBUG=UNSAFE into multiple
 options

- Switch from a struct to bitflags. (OAUTHDEBUG_UNSAFE_MASK is
  introduced a commit early to avoid unnecessary work.)
---
 src/interfaces/libpq-oauth/oauth-utils.h     |  2 +-
 src/interfaces/libpq/fe-auth-oauth.h         | 39 +++++++++++++-------
 src/interfaces/libpq-oauth/oauth-curl.c      | 12 +++---
 src/interfaces/libpq-oauth/test-oauth-curl.c |  6 +--
 src/interfaces/libpq/fe-auth-oauth-debug.c   | 25 +++++--------
 src/interfaces/libpq/fe-auth-oauth.c         |  6 +--
 6 files changed, 46 insertions(+), 44 deletions(-)

diff --git a/src/interfaces/libpq-oauth/oauth-utils.h b/src/interfaces/libpq-oauth/oauth-utils.h
index dd4e38d525c..64a9235ee85 100644
--- a/src/interfaces/libpq-oauth/oauth-utils.h
+++ b/src/interfaces/libpq-oauth/oauth-utils.h
@@ -36,7 +36,7 @@ typedef enum
 	PG_BOOL_NO					/* No (false) */
 } PGTernaryBool;
 
-extern oauth_debug_flags oauth_get_debug_flags(void);
+extern uint32 oauth_get_debug_flags(void);
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe);
 
diff --git a/src/interfaces/libpq/fe-auth-oauth.h b/src/interfaces/libpq/fe-auth-oauth.h
index 859975bcd67..a952fea09cf 100644
--- a/src/interfaces/libpq/fe-auth-oauth.h
+++ b/src/interfaces/libpq/fe-auth-oauth.h
@@ -40,23 +40,36 @@ typedef struct
 } fe_oauth_state;
 
 /*
- * Debug flags for PGOAUTHDEBUG environment variable.
- * Each flag controls a specific debug feature.
+ * Debug flags for the PGOAUTHDEBUG environment variable. Each flag controls a
+ * specific debug feature. OAUTHDEBUG_UNSAFE_* flags require the envvar to have
+ * a literal "UNSAFE:" prefix.
  */
-typedef struct oauth_debug_flags
-{
-	/* UNSAFE features - require UNSAFE: prefix */
-	bool		http;			/* allow HTTP (unencrypted) connections */
-	bool		trace;			/* log HTTP traffic (exposes secrets) */
 
-	/* SAFE features - allowed without UNSAFE: prefix */
-	bool		fast_retry;		/* allow zero-second retry intervals */
-	bool		poll_counts;	/* print poll() statistics */
-	bool		print_plugin_errors;	/* print plugin loading errors */
-} oauth_debug_flags;
+/* allow HTTP (unencrypted) connections */
+#define OAUTHDEBUG_UNSAFE_HTTP			(1<<0)
+/* log HTTP traffic (exposes secrets) */
+#define OAUTHDEBUG_UNSAFE_TRACE			(1<<1)
+/* allow zero-second retry intervals */
+#define OAUTHDEBUG_UNSAFE_DOS_ENDPOINT	(1<<2)
+
+/* mind the gap in values; see OAUTHDEBUG_UNSAFE_MASK below */
+
+/* print PQconnectPoll statistics */
+#define OAUTHDEBUG_CALL_COUNT			(1<<16)
+/* print plugin loading errors */
+#define OAUTHDEBUG_PLUGIN_ERRORS		(1<<17)
+
+/* all safe and unsafe flags, for the legacy UNSAFE behavior */
+#define OAUTHDEBUG_UNSAFE_ALL			((uint32) ~0)
+
+/* Flags are divided into "safe" and "unsafe" based on bit position. */
+#define OAUTHDEBUG_UNSAFE_MASK			((uint32) 0x0000FFFF)
+
+static_assert(OAUTHDEBUG_CALL_COUNT == OAUTHDEBUG_UNSAFE_MASK + 1,
+			  "the first safe OAUTHDEBUG flag should be above OAUTHDEBUG_UNSAFE_MASK");
 
 extern void pqClearOAuthToken(PGconn *conn);
-extern oauth_debug_flags oauth_get_debug_flags(void);
+extern uint32 oauth_get_debug_flags(void);
 
 /* Mechanisms in fe-auth-oauth.c */
 extern const pg_fe_sasl_mech pg_oauth_mech;
diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index 564d76cf063..7100824c560 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -274,7 +274,7 @@ struct async_ctx
 	int			running;		/* is asynchronous work in progress? */
 	bool		user_prompted;	/* have we already sent the authz prompt? */
 	bool		used_basic_auth;	/* did we send a client secret? */
-	oauth_debug_flags debug_flags;	/* can we give developer assistance */
+	uint32		debug_flags;	/* can we give developer assistance? */
 	int			dbg_num_calls;	/* (debug mode) how many times were we called? */
 };
 
@@ -1023,7 +1023,7 @@ parse_interval(struct async_ctx *actx, const char *interval_str)
 	parsed = ceil(parsed);
 
 	if (parsed < 1)
-		return actx->debug_flags.fast_retry ? 0 : 1;
+		return (actx->debug_flags & OAUTHDEBUG_UNSAFE_DOS_ENDPOINT) ? 0 : 1;
 
 	else if (parsed >= INT_MAX)
 		return INT_MAX;
@@ -1797,7 +1797,7 @@ setup_curl_handles(struct async_ctx *actx)
 	 */
 	CHECK_SETOPT(actx, CURLOPT_NOSIGNAL, 1L, return false);
 
-	if (actx->debug_flags.trace)
+	if (actx->debug_flags & OAUTHDEBUG_UNSAFE_TRACE)
 	{
 		/*
 		 * Set a callback for retrieving error information from libcurl, the
@@ -1829,7 +1829,7 @@ setup_curl_handles(struct async_ctx *actx)
 		const long	unsafe = CURLPROTO_HTTPS | CURLPROTO_HTTP;
 #endif
 
-		if (actx->debug_flags.http)
+		if (actx->debug_flags & OAUTHDEBUG_UNSAFE_HTTP)
 			protos = unsafe;
 
 		CHECK_SETOPT(actx, popt, protos, return false);
@@ -2297,7 +2297,7 @@ check_for_device_flow(struct async_ctx *actx)
 	 * decent time to bail out if we're not using HTTPS for the endpoints
 	 * we'll use for the flow.
 	 */
-	if (!actx->debug_flags.http)
+	if ((actx->debug_flags & OAUTHDEBUG_UNSAFE_HTTP) == 0)
 	{
 		if (pg_strncasecmp(provider->device_authorization_endpoint,
 						   HTTPS_SCHEME, strlen(HTTPS_SCHEME)) != 0)
@@ -3027,7 +3027,7 @@ pg_fe_run_oauth_flow(PGconn *conn, struct PGoauthBearerRequest *request,
 	 * drain_timer_events(), when we're in debug mode, track the total number
 	 * of calls to this function and print that at the end of the flow.
 	 */
-	if (actx && actx->debug_flags.poll_counts)
+	if (actx->debug_flags & OAUTHDEBUG_CALL_COUNT)
 	{
 		actx->dbg_num_calls++;
 		if (result == PGRES_POLLING_OK || result == PGRES_POLLING_FAILED)
diff --git a/src/interfaces/libpq-oauth/test-oauth-curl.c b/src/interfaces/libpq-oauth/test-oauth-curl.c
index 06815be9a0a..185c17e5807 100644
--- a/src/interfaces/libpq-oauth/test-oauth-curl.c
+++ b/src/interfaces/libpq-oauth/test-oauth-curl.c
@@ -89,11 +89,7 @@ init_test_actx(void)
 
 	actx->mux = PGINVALID_SOCKET;
 	actx->timerfd = -1;
-	actx->debug_flags.http = true;
-	actx->debug_flags.trace = true;
-	actx->debug_flags.fast_retry = true;
-	actx->debug_flags.poll_counts = true;
-	actx->debug_flags.print_plugin_errors = true;
+	actx->debug_flags = OAUTHDEBUG_UNSAFE_ALL;
 
 	initPQExpBuffer(&actx->errbuf);
 
diff --git a/src/interfaces/libpq/fe-auth-oauth-debug.c b/src/interfaces/libpq/fe-auth-oauth-debug.c
index 957da5d4068..c9a82b3f78e 100644
--- a/src/interfaces/libpq/fe-auth-oauth-debug.c
+++ b/src/interfaces/libpq/fe-auth-oauth-debug.c
@@ -30,38 +30,38 @@
  * Sets *is_unsafe to indicate if this option requires the UNSAFE: prefix.
  */
 static bool
-parse_debug_option(const char *option, oauth_debug_flags *flags, bool *is_unsafe)
+parse_debug_option(const char *option, uint32 *flags, bool *is_unsafe)
 {
 	*is_unsafe = false;
 
 	/* Unsafe options */
 	if (strcmp(option, "http") == 0)
 	{
-		flags->http = true;
+		*flags |= OAUTHDEBUG_UNSAFE_HTTP;
 		*is_unsafe = true;
 		return true;
 	}
 	else if (strcmp(option, "trace") == 0)
 	{
-		flags->trace = true;
+		*flags |= OAUTHDEBUG_UNSAFE_TRACE;
 		*is_unsafe = true;
 		return true;
 	}
 	else if (strcmp(option, "dos-endpoint") == 0)
 	{
-		flags->fast_retry = true;
+		*flags |= OAUTHDEBUG_UNSAFE_DOS_ENDPOINT;
 		*is_unsafe = true;
 		return true;
 	}
 	/* Safe options */
 	else if (strcmp(option, "call-count") == 0)
 	{
-		flags->poll_counts = true;
+		*flags |= OAUTHDEBUG_CALL_COUNT;
 		return true;
 	}
 	else if (strcmp(option, "plugin-errors") == 0)
 	{
-		flags->print_plugin_errors = true;
+		*flags |= OAUTHDEBUG_PLUGIN_ERRORS;
 		return true;
 	}
 
@@ -80,10 +80,10 @@ parse_debug_option(const char *option, oauth_debug_flags *flags, bool *is_unsafe
  * - An unrecognized option is specified
  * - An unsafe option is specified without the UNSAFE: prefix
  */
-oauth_debug_flags
+uint32
 oauth_get_debug_flags(void)
 {
-	oauth_debug_flags flags = {0};
+	uint32		flags = 0;
 	const char *env = getenv("PGOAUTHDEBUG");
 	char	   *options_str;
 	char	   *option;
@@ -94,14 +94,7 @@ oauth_get_debug_flags(void)
 		return flags;
 
 	if (strcmp(env, "UNSAFE") == 0)
-	{
-		flags.http = true;
-		flags.trace = true;
-		flags.fast_retry = true;
-		flags.poll_counts = true;
-		flags.print_plugin_errors = true;
-		return flags;
-	}
+		return OAUTHDEBUG_UNSAFE_ALL;
 
 	if (strncmp(env, "UNSAFE:", 7) == 0)
 	{
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index 5f5900a9ae7..6f7ec3a129e 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -389,7 +389,7 @@ issuer_from_well_known_uri(PGconn *conn, const char *wkuri)
 		authority_start = wkuri + strlen(HTTPS_SCHEME);
 
 	if (!authority_start
-		&& oauth_get_debug_flags().http
+		&& (oauth_get_debug_flags() & OAUTHDEBUG_UNSAFE_HTTP)
 		&& pg_strncasecmp(wkuri, HTTP_SCHEME, strlen(HTTP_SCHEME)) == 0)
 	{
 		/* Allow http:// for testing only. */
@@ -900,7 +900,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re
 		 *
 		 * Note that POSIX dlerror() isn't guaranteed to be threadsafe.
 		 */
-		if (oauth_get_debug_flags().print_plugin_errors)
+		if (oauth_get_debug_flags() & OAUTHDEBUG_PLUGIN_ERRORS)
 			fprintf(stderr, "failed dlopen for libpq-oauth: %s\n", dlerror());
 
 		return 0;
@@ -922,7 +922,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re
 		 * cause is still locked behind PGOAUTHDEBUG due to the dlerror()
 		 * threadsafety issue.
 		 */
-		if (oauth_get_debug_flags().print_plugin_errors)
+		if (oauth_get_debug_flags() & OAUTHDEBUG_PLUGIN_ERRORS)
 			fprintf(stderr, "failed dlsym for libpq-oauth: %s\n", dlerror());
 
 		dlclose(state->flow_module);
-- 
2.34.1



  [application/octet-stream] v3.1-0004-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch (4.5K, 5-v3.1-0004-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch)
  download | inline diff:
From 59aca1b12a8b6c71edc2f15e910f0950a012aa63 Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Wed, 1 Apr 2026 11:05:46 -0700
Subject: [PATCH v3.1 4/6] squash! Split PGOAUTHDEBUG=UNSAFE into multiple
 options

- Set specific debug flags in 001_server.pl.

- Add failing test to ensure ignored flags are actually ignored. Fix by
  inlining parse_debug_option().
---
 src/interfaces/libpq/fe-auth-oauth-debug.c    | 66 +++++--------------
 .../modules/oauth_validator/t/001_server.pl   | 22 ++++++-
 2 files changed, 38 insertions(+), 50 deletions(-)

diff --git a/src/interfaces/libpq/fe-auth-oauth-debug.c b/src/interfaces/libpq/fe-auth-oauth-debug.c
index c9a82b3f78e..8bf710ce46a 100644
--- a/src/interfaces/libpq/fe-auth-oauth-debug.c
+++ b/src/interfaces/libpq/fe-auth-oauth-debug.c
@@ -24,50 +24,6 @@
 
 #include "fe-auth-oauth.h"
 
-/*
- * Parse a single debug option from PGOAUTHDEBUG.
- * Returns true if the option is recognized, false otherwise.
- * Sets *is_unsafe to indicate if this option requires the UNSAFE: prefix.
- */
-static bool
-parse_debug_option(const char *option, uint32 *flags, bool *is_unsafe)
-{
-	*is_unsafe = false;
-
-	/* Unsafe options */
-	if (strcmp(option, "http") == 0)
-	{
-		*flags |= OAUTHDEBUG_UNSAFE_HTTP;
-		*is_unsafe = true;
-		return true;
-	}
-	else if (strcmp(option, "trace") == 0)
-	{
-		*flags |= OAUTHDEBUG_UNSAFE_TRACE;
-		*is_unsafe = true;
-		return true;
-	}
-	else if (strcmp(option, "dos-endpoint") == 0)
-	{
-		*flags |= OAUTHDEBUG_UNSAFE_DOS_ENDPOINT;
-		*is_unsafe = true;
-		return true;
-	}
-	/* Safe options */
-	else if (strcmp(option, "call-count") == 0)
-	{
-		*flags |= OAUTHDEBUG_CALL_COUNT;
-		return true;
-	}
-	else if (strcmp(option, "plugin-errors") == 0)
-	{
-		*flags |= OAUTHDEBUG_PLUGIN_ERRORS;
-		return true;
-	}
-
-	return false;
-}
-
 /*
  * Parses the PGOAUTHDEBUG environment variable and returns debug flags.
  *
@@ -109,22 +65,36 @@ oauth_get_debug_flags(void)
 	option = strtok_r(options_str, ",", &saveptr);
 	while (option != NULL)
 	{
-		bool		is_unsafe;
-
-		if (!parse_debug_option(option, &flags, &is_unsafe))
+		uint32		flag = 0;
+
+		if (strcmp(option, "http") == 0)
+			flag = OAUTHDEBUG_UNSAFE_HTTP;
+		else if (strcmp(option, "trace") == 0)
+			flag = OAUTHDEBUG_UNSAFE_TRACE;
+		else if (strcmp(option, "dos-endpoint") == 0)
+			flag = OAUTHDEBUG_UNSAFE_DOS_ENDPOINT;
+		else if (strcmp(option, "call-count") == 0)
+			flag = OAUTHDEBUG_CALL_COUNT;
+		else if (strcmp(option, "plugin-errors") == 0)
+			flag = OAUTHDEBUG_PLUGIN_ERRORS;
+		else
 		{
 			fprintf(stderr,
 					"WARNING: PGOAUTHDEBUG: unrecognized debug option \"%s\" (ignored)\n",
 					option);
 		}
-		else if (is_unsafe && !unsafe_prefix)
+
+		if (!unsafe_prefix && ((flag & OAUTHDEBUG_UNSAFE_MASK) != 0))
 		{
+			flag = 0;
+
 			fprintf(stderr,
 					"WARNING: PGOAUTHDEBUG: unsafe option \"%s\" requires UNSAFE: prefix (ignored)\n"
 					"Use: PGOAUTHDEBUG=UNSAFE:%s\n",
 					option, option);
 		}
 
+		flags |= flag;
 		option = strtok_r(NULL, ",", &saveptr);
 	}
 
diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl
index c9c46e63539..3803dd08287 100644
--- a/src/test/modules/oauth_validator/t/001_server.pl
+++ b/src/test/modules/oauth_validator/t/001_server.pl
@@ -93,6 +93,21 @@ $node->connect_fails(
 	  qr@OAuth discovery URI "\Q$issuer\E/.well-known/openid-configuration" must use HTTPS@
 );
 
+{
+	# PGOAUTHDEBUG=http should have no effect (it needs an UNSAFE: marker).
+	local $ENV{PGOAUTHDEBUG} = "http";
+
+	$node->connect_fails(
+		"user=test dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635",
+		"HTTPS is required without debug mode (bad PGOAUTHDEBUG value)",
+		expected_stderr => qr[
+			^WARNING: .* \Qunsafe option "http" requires UNSAFE: prefix\E
+			.*
+			\QOAuth discovery URI "$issuer/.well-known/openid-configuration" must use HTTPS\E
+		]msx
+	);
+}
+
 # Switch to HTTPS.
 $issuer = "https://127.0.0.1:$port";
 
@@ -172,8 +187,11 @@ $node->connect_ok(
 	],
 	log_unlike => [qr/FATAL.*OAuth bearer authentication failed/]);
 
-# Enable PGOAUTHDEBUG for all remaining tests.
-$ENV{PGOAUTHDEBUG} = "UNSAFE";
+# Enable some debugging features for all remaining tests:
+# - trace, for detailed Curl logs on failure
+# - dos-endpoint, to speed up the three-way handshake
+# - call-count, for our later sanity check
+$ENV{PGOAUTHDEBUG} = "UNSAFE:trace,dos-endpoint,call-count";
 
 # The /alternate issuer uses slightly different parameters, along with an
 # OAuth-style discovery document.
-- 
2.34.1



  [application/octet-stream] v3.1-0005-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch (9.2K, 6-v3.1-0005-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch)
  download | inline diff:
From ec1318b9d169418c195f6e1625820921fde099a7 Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Wed, 1 Apr 2026 11:17:36 -0700
Subject: [PATCH v3.1 5/6] squash! Split PGOAUTHDEBUG=UNSAFE into multiple
 options

- Move the implementation into its own header; revert the build system
  changes that were needed for the .c approach.

- <stdio.h> et al are included in "c.h" and do not need to be explicitly
  pulled in.
---
 src/interfaces/libpq-oauth/meson.build        |  6 +--
 src/interfaces/libpq/meson.build              |  1 -
 src/interfaces/libpq-oauth/Makefile           | 11 +----
 src/interfaces/libpq/Makefile                 |  3 +-
 src/interfaces/libpq-oauth/oauth-utils.h      |  2 -
 src/interfaces/libpq/fe-auth-oauth.h          | 30 ------------
 .../{fe-auth-oauth-debug.c => oauth-debug.h}  | 49 ++++++++++++++-----
 src/interfaces/libpq-oauth/oauth-curl.c       |  1 +
 src/interfaces/libpq/fe-auth-oauth.c          |  1 +
 9 files changed, 43 insertions(+), 61 deletions(-)
 rename src/interfaces/libpq/{fe-auth-oauth-debug.c => oauth-debug.h} (62%)

diff --git a/src/interfaces/libpq-oauth/meson.build b/src/interfaces/libpq-oauth/meson.build
index d8cc92e0c2c..ea3a900f4f1 100644
--- a/src/interfaces/libpq-oauth/meson.build
+++ b/src/interfaces/libpq-oauth/meson.build
@@ -6,7 +6,6 @@ endif
 
 libpq_oauth_sources = files(
   'oauth-curl.c',
-  '../libpq/fe-auth-oauth-debug.c',
 )
 
 # The shared library needs additional glue symbols.
@@ -63,10 +62,7 @@ endif
 
 libpq_oauth_test_deps = []
 
-oauth_test_sources = files(
-  'test-oauth-curl.c',
-  '../libpq/fe-auth-oauth-debug.c',
-) + libpq_oauth_so_sources
+oauth_test_sources = files('test-oauth-curl.c') + libpq_oauth_so_sources
 
 if host_system == 'windows'
   oauth_test_sources += rc_bin_gen.process(win32ver_rc, extra_args: [
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index d031f4962e5..b0ae72167a1 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -2,7 +2,6 @@
 
 libpq_sources = files(
   'fe-auth-oauth.c',
-  'fe-auth-oauth-debug.c',
   'fe-auth-scram.c',
   'fe-auth.c',
   'fe-cancel.c',
diff --git a/src/interfaces/libpq-oauth/Makefile b/src/interfaces/libpq-oauth/Makefile
index c6097dda531..11e1a3cf528 100644
--- a/src/interfaces/libpq-oauth/Makefile
+++ b/src/interfaces/libpq-oauth/Makefile
@@ -36,24 +36,15 @@ override CPPFLAGS_SHLIB += -DUSE_PRIVATE_ENCODING_FUNCS
 OBJS = \
 	$(WIN32RES)
 
-OBJS_STATIC = \
-	oauth-curl.o \
-	fe-auth-oauth-debug.o
+OBJS_STATIC = oauth-curl.o
 
 # The shared library needs additional glue symbols.
 OBJS_SHLIB = \
 	oauth-curl_shlib.o \
 	oauth-utils.o \
-	fe-auth-oauth-debug_shlib.o
 
 oauth-utils.o: override CPPFLAGS += $(CPPFLAGS_SHLIB)
 
-fe-auth-oauth-debug.o: $(libpq_srcdir)/fe-auth-oauth-debug.c
-	$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
-
-fe-auth-oauth-debug_shlib.o: $(libpq_srcdir)/fe-auth-oauth-debug.c fe-auth-oauth-debug.o
-	$(CC) $(CFLAGS) $(CFLAGS_SL) $(CPPFLAGS) $(CPPFLAGS_SHLIB) -c $< -o $@
-
 # Add shlib-/stlib-specific objects.
 $(shlib): override OBJS += $(OBJS_SHLIB)
 $(shlib): $(OBJS_SHLIB)
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 099c6557e77..0963995eed4 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -44,8 +44,7 @@ OBJS = \
 	legacy-pqsignal.o \
 	libpq-events.o \
 	pqexpbuffer.o \
-	fe-auth.o \
-	fe-auth-oauth-debug.o
+	fe-auth.o
 
 # File shared across all SSL implementations supported.
 ifneq ($(with_ssl),no)
diff --git a/src/interfaces/libpq-oauth/oauth-utils.h b/src/interfaces/libpq-oauth/oauth-utils.h
index 64a9235ee85..dacd2dbacfe 100644
--- a/src/interfaces/libpq-oauth/oauth-utils.h
+++ b/src/interfaces/libpq-oauth/oauth-utils.h
@@ -15,7 +15,6 @@
 #ifndef OAUTH_UTILS_H
 #define OAUTH_UTILS_H
 
-#include "fe-auth-oauth.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
 
@@ -36,7 +35,6 @@ typedef enum
 	PG_BOOL_NO					/* No (false) */
 } PGTernaryBool;
 
-extern uint32 oauth_get_debug_flags(void);
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe);
 
diff --git a/src/interfaces/libpq/fe-auth-oauth.h b/src/interfaces/libpq/fe-auth-oauth.h
index a952fea09cf..a50d7b03408 100644
--- a/src/interfaces/libpq/fe-auth-oauth.h
+++ b/src/interfaces/libpq/fe-auth-oauth.h
@@ -39,37 +39,7 @@ typedef struct
 	void	   *flow_module;
 } fe_oauth_state;
 
-/*
- * Debug flags for the PGOAUTHDEBUG environment variable. Each flag controls a
- * specific debug feature. OAUTHDEBUG_UNSAFE_* flags require the envvar to have
- * a literal "UNSAFE:" prefix.
- */
-
-/* allow HTTP (unencrypted) connections */
-#define OAUTHDEBUG_UNSAFE_HTTP			(1<<0)
-/* log HTTP traffic (exposes secrets) */
-#define OAUTHDEBUG_UNSAFE_TRACE			(1<<1)
-/* allow zero-second retry intervals */
-#define OAUTHDEBUG_UNSAFE_DOS_ENDPOINT	(1<<2)
-
-/* mind the gap in values; see OAUTHDEBUG_UNSAFE_MASK below */
-
-/* print PQconnectPoll statistics */
-#define OAUTHDEBUG_CALL_COUNT			(1<<16)
-/* print plugin loading errors */
-#define OAUTHDEBUG_PLUGIN_ERRORS		(1<<17)
-
-/* all safe and unsafe flags, for the legacy UNSAFE behavior */
-#define OAUTHDEBUG_UNSAFE_ALL			((uint32) ~0)
-
-/* Flags are divided into "safe" and "unsafe" based on bit position. */
-#define OAUTHDEBUG_UNSAFE_MASK			((uint32) 0x0000FFFF)
-
-static_assert(OAUTHDEBUG_CALL_COUNT == OAUTHDEBUG_UNSAFE_MASK + 1,
-			  "the first safe OAUTHDEBUG flag should be above OAUTHDEBUG_UNSAFE_MASK");
-
 extern void pqClearOAuthToken(PGconn *conn);
-extern uint32 oauth_get_debug_flags(void);
 
 /* Mechanisms in fe-auth-oauth.c */
 extern const pg_fe_sasl_mech pg_oauth_mech;
diff --git a/src/interfaces/libpq/fe-auth-oauth-debug.c b/src/interfaces/libpq/oauth-debug.h
similarity index 62%
rename from src/interfaces/libpq/fe-auth-oauth-debug.c
rename to src/interfaces/libpq/oauth-debug.h
index 8bf710ce46a..ad5246a8402 100644
--- a/src/interfaces/libpq/fe-auth-oauth-debug.c
+++ b/src/interfaces/libpq/oauth-debug.h
@@ -1,28 +1,53 @@
 /*-------------------------------------------------------------------------
  *
- * fe-auth-oauth-debug.c
+ * oauth-debug.h
  *	  Parsing logic for PGOAUTHDEBUG environment variable
  *
- * This file contains pure string parsing logic with no dependencies on
- * libpq or libpq-oauth implementation details. It's compiled into both
- * libraries to avoid code duplication.
+ * Both libpq and libpq-oauth need this logic, so it's packaged in a small
+ * header for convenience.
  *
- * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- * 	src/interfaces/libpq/fe-auth-oauth-debug.c
+ * 	src/interfaces/libpq/oauth-debug.h
  *
  *-------------------------------------------------------------------------
  */
 
+#ifndef OAUTH_DEBUG_H
+#define OAUTH_DEBUG_H
+
 #include "postgres_fe.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * Debug flags for the PGOAUTHDEBUG environment variable. Each flag controls a
+ * specific debug feature. OAUTHDEBUG_UNSAFE_* flags require the envvar to have
+ * a literal "UNSAFE:" prefix.
+ */
+
+/* allow HTTP (unencrypted) connections */
+#define OAUTHDEBUG_UNSAFE_HTTP			(1<<0)
+/* log HTTP traffic (exposes secrets) */
+#define OAUTHDEBUG_UNSAFE_TRACE			(1<<1)
+/* allow zero-second retry intervals */
+#define OAUTHDEBUG_UNSAFE_DOS_ENDPOINT	(1<<2)
+
+/* mind the gap in values; see OAUTHDEBUG_UNSAFE_MASK below */
 
-#include "fe-auth-oauth.h"
+/* print PQconnectPoll statistics */
+#define OAUTHDEBUG_CALL_COUNT			(1<<16)
+/* print plugin loading errors */
+#define OAUTHDEBUG_PLUGIN_ERRORS		(1<<17)
+
+/* all safe and unsafe flags, for the legacy UNSAFE behavior */
+#define OAUTHDEBUG_UNSAFE_ALL			((uint32) ~0)
+
+/* Flags are divided into "safe" and "unsafe" based on bit position. */
+#define OAUTHDEBUG_UNSAFE_MASK			((uint32) 0x0000FFFF)
+
+static_assert(OAUTHDEBUG_CALL_COUNT == OAUTHDEBUG_UNSAFE_MASK + 1,
+			  "the first safe OAUTHDEBUG flag should be above OAUTHDEBUG_UNSAFE_MASK");
 
 /*
  * Parses the PGOAUTHDEBUG environment variable and returns debug flags.
@@ -36,7 +61,7 @@
  * - An unrecognized option is specified
  * - An unsafe option is specified without the UNSAFE: prefix
  */
-uint32
+static uint32
 oauth_get_debug_flags(void)
 {
 	uint32		flags = 0;
@@ -102,3 +127,5 @@ oauth_get_debug_flags(void)
 
 	return flags;
 }
+
+#endif							/* OAUTH_DEBUG_H */
diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index 7100824c560..5ee630dd9f7 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -31,6 +31,7 @@
 #include "common/jsonapi.h"
 #include "mb/pg_wchar.h"
 #include "oauth-curl.h"
+#include "oauth-debug.h"
 
 #ifdef USE_DYNAMIC_OAUTH
 
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index 6f7ec3a129e..c150f27df00 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -26,6 +26,7 @@
 #include "fe-auth.h"
 #include "fe-auth-oauth.h"
 #include "mb/pg_wchar.h"
+#include "oauth-debug.h"
 #include "pg_config_paths.h"
 #include "utils/memdebug.h"
 
-- 
2.34.1



  [application/octet-stream] v3.1-0006-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch (5.2K, 7-v3.1-0006-squash-Split-PGOAUTHDEBUG-UNSAFE-into-multiple-.patch)
  download | inline diff:
From a79b31835ce3e9eb46f9fd7505d83e9a00b0b6e4 Mon Sep 17 00:00:00 2001
From: Jacob Champion <[email protected]>
Date: Wed, 1 Apr 2026 11:22:15 -0700
Subject: [PATCH v3.1 6/6] squash! Split PGOAUTHDEBUG=UNSAFE into multiple
 options

- Translate the warnings, since they're a safety feature for confused
  end users. Requires a small complication for libpq_gettext().

- Update warning text. Do not provide instructions on how to defeat the
  UNSAFE protection; if a user doesn't know how to do that immediately,
  they should absolutely not be using an unsafe dev option.

- nit: rename unsafe_prefix to unsafe_allowed
---
 src/interfaces/libpq/oauth-debug.h            | 25 +++++++++++--------
 src/interfaces/libpq-oauth/oauth-curl.c       |  7 +++++-
 .../modules/oauth_validator/t/001_server.pl   |  2 +-
 src/tools/pginclude/headerscheck              |  2 ++
 4 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/src/interfaces/libpq/oauth-debug.h b/src/interfaces/libpq/oauth-debug.h
index ad5246a8402..0bd8467a09c 100644
--- a/src/interfaces/libpq/oauth-debug.h
+++ b/src/interfaces/libpq/oauth-debug.h
@@ -4,7 +4,8 @@
  *	  Parsing logic for PGOAUTHDEBUG environment variable
  *
  * Both libpq and libpq-oauth need this logic, so it's packaged in a small
- * header for convenience.
+ * header for convenience. This is not quite a standalone header, due to the
+ * complication introduced by libpq_gettext(); see note below.
  *
  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -20,6 +21,13 @@
 
 #include "postgres_fe.h"
 
+/*
+ * XXX libpq-oauth can't compile against libpq-int.h, so clients of this header
+ * need to provide the declaration of libpq_gettext() before #including it.
+ * Fortunately, there are only two such clients.
+ */
+/* #include "libpq-int.h" */
+
 /*
  * Debug flags for the PGOAUTHDEBUG environment variable. Each flag controls a
  * specific debug feature. OAUTHDEBUG_UNSAFE_* flags require the envvar to have
@@ -69,7 +77,7 @@ oauth_get_debug_flags(void)
 	char	   *options_str;
 	char	   *option;
 	char	   *saveptr = NULL;
-	bool		unsafe_prefix = false;
+	bool		unsafe_allowed = false;
 
 	if (!env || env[0] == '\0')
 		return flags;
@@ -79,7 +87,7 @@ oauth_get_debug_flags(void)
 
 	if (strncmp(env, "UNSAFE:", 7) == 0)
 	{
-		unsafe_prefix = true;
+		unsafe_allowed = true;
 		env += 7;
 	}
 
@@ -103,20 +111,17 @@ oauth_get_debug_flags(void)
 		else if (strcmp(option, "plugin-errors") == 0)
 			flag = OAUTHDEBUG_PLUGIN_ERRORS;
 		else
-		{
 			fprintf(stderr,
-					"WARNING: PGOAUTHDEBUG: unrecognized debug option \"%s\" (ignored)\n",
+					libpq_gettext("WARNING: unrecognized PGOAUTHDEBUG option \"%s\" (ignored)\n"),
 					option);
-		}
 
-		if (!unsafe_prefix && ((flag & OAUTHDEBUG_UNSAFE_MASK) != 0))
+		if (!unsafe_allowed && ((flag & OAUTHDEBUG_UNSAFE_MASK) != 0))
 		{
 			flag = 0;
 
 			fprintf(stderr,
-					"WARNING: PGOAUTHDEBUG: unsafe option \"%s\" requires UNSAFE: prefix (ignored)\n"
-					"Use: PGOAUTHDEBUG=UNSAFE:%s\n",
-					option, option);
+					libpq_gettext("WARNING: PGOAUTHDEBUG option \"%s\" is unsafe (ignored)\n"),
+					option);
 		}
 
 		flags |= flag;
diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index 5ee630dd9f7..eb2fe35d0cc 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -31,7 +31,6 @@
 #include "common/jsonapi.h"
 #include "mb/pg_wchar.h"
 #include "oauth-curl.h"
-#include "oauth-debug.h"
 
 #ifdef USE_DYNAMIC_OAUTH
 
@@ -50,6 +49,12 @@
 
 #endif							/* USE_DYNAMIC_OAUTH */
 
+/*
+ * oauth-debug.h needs the declaration of libpq_gettext(), from one of the above
+ * sources.
+ */
+#include "oauth-debug.h"
+
 /* One final guardrail against accidental inclusion... */
 #if defined(USE_DYNAMIC_OAUTH) && defined(LIBPQ_INT_H)
 #error do not rely on libpq-int.h in dynamic builds of libpq-oauth
diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl
index 3803dd08287..3d190c2ba71 100644
--- a/src/test/modules/oauth_validator/t/001_server.pl
+++ b/src/test/modules/oauth_validator/t/001_server.pl
@@ -101,7 +101,7 @@ $node->connect_fails(
 		"user=test dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635",
 		"HTTPS is required without debug mode (bad PGOAUTHDEBUG value)",
 		expected_stderr => qr[
-			^WARNING: .* \Qunsafe option "http" requires UNSAFE: prefix\E
+			^WARNING: .* \Qoption "http" is unsafe\E
 			.*
 			\QOAuth discovery URI "$issuer/.well-known/openid-configuration" must use HTTPS\E
 		]msx
diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck
index 14c466cc237..de50b6937af 100755
--- a/src/tools/pginclude/headerscheck
+++ b/src/tools/pginclude/headerscheck
@@ -153,6 +153,8 @@ do
 	test "$f" = src/include/catalog/syscache_ids.h && continue
 	test "$f" = src/include/catalog/syscache_info.h && continue
 
+	test "$f" = src/interfaces/libpq/oauth-debug.h && continue
+
 	# We can't make these Bison output files compilable standalone
 	# without using "%code require", which old Bison versions lack.
 	# parser/gram.h will be included by parser/gramparse.h anyway.
-- 
2.34.1



view thread (13+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected]
  Subject: Re: [oauth] Split and extend PGOAUTHDEBUG
  In-Reply-To: <CAOYmi+=865C93VegSzD9z4_uvutZwEZEUsS4P6jm3_i0paAMmA@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox