diff --git a/doc/src/sgml/oauth-validators.sgml b/doc/src/sgml/oauth-validators.sgml
index 704089dd7b3..05c626bab93 100644
--- a/doc/src/sgml/oauth-validators.sgml
+++ b/doc/src/sgml/oauth-validators.sgml
@@ -312,6 +312,7 @@ typedef struct OAuthValidatorCallbacks
     ValidatorStartupCB startup_cb;
     ValidatorShutdownCB shutdown_cb;
     ValidatorValidateCB validate_cb;
+    ValidatorExpireCB expire_cb;    /* Optional: check token expiration */
 } OAuthValidatorCallbacks;
 
 typedef const OAuthValidatorCallbacks *(*OAuthValidatorModuleInit) (void);
@@ -320,6 +321,15 @@ typedef const OAuthValidatorCallbacks *(*OAuthValidatorModuleInit) (void);
    Only the <function>validate_cb</function> callback is required, the others
    are optional.
   </para>
+  <para>
+   The <literal>magic</literal> field identifies the ABI version of the module.
+   The server supports both <literal>PG_OAUTH_VALIDATOR_MAGIC_V1</literal> (the
+   original version without <function>expire_cb</function>) and
+   <literal>PG_OAUTH_VALIDATOR_MAGIC_V2</literal> (which adds
+   <function>expire_cb</function>).  New modules should use
+   <literal>PG_OAUTH_VALIDATOR_MAGIC</literal>, which always refers to the
+   latest version.
+  </para>
  </sect1>
 
  <sect1 id="oauth-validator-callbacks">
@@ -412,5 +422,27 @@ typedef void (*ValidatorShutdownCB) (ValidatorModuleState *state);
    </para>
   </sect2>
 
+  <sect2 id="oauth-validator-callback-expire">
+   <title>Expire Callback</title>
+   <para>
+    The <function>expire_cb</function> callback is an optional callback that
+    can be used to check whether the OAuth token has expired. This is called
+    during credential validation to verify that the token is still valid.
+<programlisting>
+typedef bool (*ValidatorExpireCB) (const ValidatorModuleState *state);
+</programlisting>
+    The callback should return <literal>true</literal> if the token is still
+    valid, or <literal>false</literal> if the token has expired. If this
+    callback is not provided (set to NULL), the server assumes the token
+    remains valid.
+   </para>
+   <para>
+    This callback was added in <literal>PG_OAUTH_VALIDATOR_MAGIC_V2</literal>.
+    Modules compiled against the older <literal>PG_OAUTH_VALIDATOR_MAGIC_V1</literal>
+    do not have this field, and the server will not attempt to call it for
+    such modules.
+   </para>
+  </sect2>
+
  </sect1>
 </chapter>
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 98eb2a8242d..32e4c7280e5 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -18,6 +18,8 @@ OBJS = \
 	auth-oauth.o \
 	auth-sasl.o \
 	auth-scram.o \
+	auth-validate-methods.o \
+	auth-validate.o \
 	auth.o \
 	be-fsstubs.o \
 	be-secure-common.o \
diff --git a/src/backend/libpq/auth-oauth.c b/src/backend/libpq/auth-oauth.c
index 11365048951..495234343c2 100644
--- a/src/backend/libpq/auth-oauth.c
+++ b/src/backend/libpq/auth-oauth.c
@@ -43,6 +43,7 @@ static void shutdown_validator_library(void *arg);
 
 static ValidatorModuleState *validator_module_state;
 static const OAuthValidatorCallbacks *ValidatorCallbacks;
+static int ValidatorABIVersion;		/* tracks V1 vs V2 module ABI */
 
 /* Mechanism declaration */
 const pg_be_sasl_mech pg_be_oauth_mech = {
@@ -767,13 +768,22 @@ load_validator_library(const char *libname)
 	 * Check the magic number, to protect against break-glass scenarios where
 	 * the ABI must change within a major version. load_external_function()
 	 * already checks for compatibility across major versions.
+	 *
+	 * We accept both V1 and V2 magic numbers for backward compatibility.
+	 * V1 modules don't have the expire_cb field, so we track the version
+	 * to avoid accessing non-existent struct members.
 	 */
-	if (ValidatorCallbacks->magic != PG_OAUTH_VALIDATOR_MAGIC)
+	if (ValidatorCallbacks->magic == PG_OAUTH_VALIDATOR_MAGIC_V2)
+		ValidatorABIVersion = 2;
+	else if (ValidatorCallbacks->magic == PG_OAUTH_VALIDATOR_MAGIC_V1)
+		ValidatorABIVersion = 1;
+	else
 		ereport(ERROR,
 				errmsg("%s module \"%s\": magic number mismatch",
 					   "OAuth validator", libname),
-				errdetail("Server has magic number 0x%08X, module has 0x%08X.",
-						  PG_OAUTH_VALIDATOR_MAGIC, ValidatorCallbacks->magic));
+				errdetail("Server expects magic number 0x%08X or 0x%08X, module has 0x%08X.",
+						  PG_OAUTH_VALIDATOR_MAGIC_V2, PG_OAUTH_VALIDATOR_MAGIC_V1,
+						  ValidatorCallbacks->magic));
 
 	/*
 	 * Make sure all required callbacks are present in the ValidatorCallbacks
@@ -892,3 +902,24 @@ done:
 
 	return (*err_msg == NULL);
 }
+
+/*
+ * Check if an OAuth token has expired.
+ * This is called from credential validation to check token validity.
+ */
+bool
+CheckOAuthValidatorExpiration(void)
+{
+	/*
+	 * Delegate to validator's expire_cb if available.  Only V2+ modules have
+	 * the expire_cb field, so we must check the ABI version before accessing
+	 * it to maintain backward compatibility with V1 modules.
+	 */
+	if (ValidatorCallbacks != NULL &&
+		ValidatorABIVersion >= 2 &&
+		ValidatorCallbacks->expire_cb != NULL)
+		return ValidatorCallbacks->expire_cb(validator_module_state);
+
+	/* V1 module or no expire_cb, assume token is valid */
+	return true;
+}
diff --git a/src/backend/libpq/auth-validate-methods.c b/src/backend/libpq/auth-validate-methods.c
new file mode 100644
index 00000000000..f6516459624
--- /dev/null
+++ b/src/backend/libpq/auth-validate-methods.c
@@ -0,0 +1,136 @@
+/*-------------------------------------------------------------------------
+ *
+ * auth-validate-methods.c
+ *      Implementation of authentication credential validation methods
+ *
+ * This module provides credential validation methods for various authentication
+ * types during active PostgreSQL sessions. It includes validation for password
+ * expiry, OAuth token expiry, and can be extended to other authentication
+ * mechanisms.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *      src/backend/libpq/auth-validate-methods.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/pg_authid.h"
+#include "catalog/catalog.h"
+#include "libpq/auth-validate.h"
+#include "libpq/libpq-be.h"
+#include "libpq/oauth.h"
+#include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.h"
+
+/* Function declarations for internal use */
+static bool validate_password_credentials(void);
+static bool validate_oauth_credentials(void);
+
+/* Function prototypes */
+void InitializeValidationMethods(void);
+
+/*
+ * Initialize validation methods
+ */
+void
+InitializeValidationMethods(void)
+{
+	/* Register all the validation methods */
+	RegisterCredentialValidator(CVT_PASSWORD, validate_password_credentials);
+	RegisterCredentialValidator(CVT_OAUTH, validate_oauth_credentials);
+}
+
+/*
+ * Validate password credentials by checking rolvaliduntil
+ * Returns true if credentials are still valid, false if they have expired.
+ */
+static bool
+validate_password_credentials(void)
+{
+	HeapTuple   tuple = NULL;
+	Datum       rolvaliduntil_datum;
+	bool        validuntil_null;
+	TimestampTz valid_until = 0;
+	TimestampTz current_time;
+	Oid         userid;
+	bool        result = false;
+
+	userid = GetSessionUserId();
+
+	/*
+	 * Try to take AccessShareLock on pg_authid to prevent concurrent modifications
+	 * from interfering with our validation. Use conditional acquisition to avoid
+	 * indefinite waiting during credential validation.
+	 */
+	if (!ConditionalLockRelationOid(AuthIdRelationId, AccessShareLock))
+	{
+		/*
+		 * Could not acquire lock immediately, which likely means another session
+		 * is modifying user data. For credential validation, it's better to
+		 * consider credentials valid and retry later than to block indefinitely.
+		 */
+		elog(LOG, "credential validation: could not acquire lock on pg_authid immediately, will retry later");
+		return true; /* Consider valid */
+	}
+
+	PG_TRY();
+	{
+		tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(userid));
+
+		if (HeapTupleIsValid(tuple))
+		{
+			/* Get the expiration time column */
+			rolvaliduntil_datum = SysCacheGetAttr(AUTHOID, tuple,
+												  Anum_pg_authid_rolvaliduntil,
+												  &validuntil_null);
+			if (!validuntil_null)
+			{
+				valid_until = DatumGetTimestampTz(rolvaliduntil_datum);
+				current_time = GetCurrentTimestamp();
+
+				result = !(valid_until < current_time);
+			}
+			else
+				result = true;
+
+			ReleaseSysCache(tuple);
+			tuple = NULL;
+		}
+	}
+	PG_FINALLY();
+	{
+		if (tuple != NULL)
+			ReleaseSysCache(tuple);
+
+		UnlockRelationOid(AuthIdRelationId, AccessShareLock);
+	}
+	PG_END_TRY();
+
+	return result;
+}
+
+/*
+ * Check if an OAuth token has expired.
+ *
+ * Returns true if the token is still valid, false if it has expired.
+ *
+ * Calls wrapper CheckOAuthValidatorExpiration() function
+ * to verify that the token hasn't expired.
+ */
+static bool
+validate_oauth_credentials(void)
+{
+	/* Call the validator's expire_cb to check token expiration */
+	if (!CheckOAuthValidatorExpiration())
+		return false;
+
+	return true;
+}
diff --git a/src/backend/libpq/auth-validate.c b/src/backend/libpq/auth-validate.c
new file mode 100644
index 00000000000..92d4683651b
--- /dev/null
+++ b/src/backend/libpq/auth-validate.c
@@ -0,0 +1,260 @@
+/*-------------------------------------------------------------------------
+*
+* auth-validate.c
+*      Implementation of authentication credential validation
+*
+* This module provides a mechanism for validating credentials during
+* an active PostgreSQL session.
+*
+* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+* Portions Copyright (c) 1994, Regents of the University of California
+*
+* IDENTIFICATION
+*      src/backend/libpq/auth-validate.c
+*
+*-------------------------------------------------------------------------
+*/
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "libpq/auth.h"
+#include "libpq/libpq-be.h"
+#include "libpq/auth-validate.h"
+#include "libpq/auth-validate-methods.h"
+#include "miscadmin.h"
+#include "postmaster/postmaster.h"
+#include "storage/ipc.h"
+#include "tcop/tcopprot.h"
+#include "utils/elog.h"
+#include "utils/guc.h"
+#include "utils/timestamp.h"
+#include "utils/timeout.h"
+
+/* GUC variables */
+bool		credential_validation_enabled;
+int			credential_validation_interval;
+
+
+/* Registered credential validators */
+static CredentialValidationCallback validators[CVT_COUNT];
+
+
+/*
+ * Convert UserAuth enum to CredentialValidationType for validator selection
+ */
+static CredentialValidationType
+UserAuthToValidationType(UserAuth auth_method)
+{
+	switch (auth_method)
+	{
+		case uaPassword:
+		case uaMD5:
+		case uaSCRAM:
+		/* All password-based methods use the password validator */
+			return CVT_PASSWORD;
+		case uaOAuth:
+			return CVT_OAUTH;
+		default:
+			/* No specific validator for other auth methods */
+			return CVT_COUNT;  /* Invalid value */
+	}
+}
+
+/*
+ * Process credential validation
+ */
+void
+ProcessCredentialValidation(void)
+{
+	/* Skip validation during initialization, bootstrap, authentication or connection setup */
+	if (ClientAuthInProgress || IsInitProcessingMode() || IsBootstrapProcessingMode())
+		return;
+
+	/* Check credentials if validation is enabled */
+	if (credential_validation_enabled && MyClientConnectionInfo.authn_id != NULL)
+	{
+		CredentialValidationStatus status;
+		UserAuth	auth_method = MyClientConnectionInfo.auth_method;
+
+		status = CheckCredentialValidity();
+
+		switch (status)
+		{
+			case CVS_VALID:
+				/* Credentials are valid, continue */
+				break;
+
+			case CVS_EXPIRED:
+				elog(LOG, "credential validation: credentials expired for auth_method=%d",
+					 (int) auth_method);
+				ereport(FATAL,
+						(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+						 errmsg("session credentials have expired"),
+						 errhint("Please reconnect to establish a new authenticated session")));
+				break;
+
+			case CVS_ERROR:
+				elog(LOG, "credential validation: error checking credentials for auth_method=%d",
+					 (int) auth_method);
+				ereport(WARNING,
+						(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+						 errmsg("error checking credential validity"),
+						 errhint("Credential validation will be retried at the next interval")));
+				break;
+			}
+	}
+}
+
+/*
+ * Initialize credential validation system Called from InitPostgres after
+ * authentication completes
+ */
+void
+InitializeCredentialValidation(void)
+{
+	int			i;
+
+	/* Define GUC variables */
+	DefineCustomBoolVariable("credential_validation.enabled",
+							 "Enable periodic credential validation.",
+							 NULL,
+							 &credential_validation_enabled,
+							 false,
+							 PGC_SUSET,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
+	DefineCustomIntVariable("credential_validation.interval",
+							"Credential validation interval in minutes.",
+							NULL,
+							&credential_validation_interval,
+							1,	/* default: 1 minute */
+							1,	/* min: 1 minute */
+							60,	/* max: 60 minutes */
+							PGC_SUSET,
+							GUC_UNIT_MIN,
+							NULL,
+							NULL,
+							NULL);
+
+	/* Initialize validator callbacks to NULL */
+	for (i = 0; i < CVT_COUNT; i++)
+		validators[i] = NULL;
+
+	/* Initialize and register all validation methods */
+	InitializeValidationMethods();
+}
+
+/*
+ * Enable or re-enable the credential validation timeout timer.
+ * Called at session startup and after each validation or error recovery.
+ */
+void
+EnableCredentialValidationTimeout(void)
+{
+	int			interval_ms;
+
+	/* Only enable if credential validation is configured */
+	if (!credential_validation_enabled)
+		return;
+
+	/* Skip for non-client backends */
+	if (!IsExternalConnectionBackend(MyBackendType))
+		return;
+
+	/* Convert interval from minutes to milliseconds */
+	interval_ms = credential_validation_interval * 60 * 1000;
+
+	enable_timeout_after(CREDENTIAL_VALIDATION_TIMEOUT, interval_ms);
+
+	elog(DEBUG1, "credential validation timeout enabled, interval=%d min", credential_validation_interval);
+}
+
+/*
+ * Register a validator callback for a specific authentication method
+ */
+void
+RegisterCredentialValidator(CredentialValidationType method_type, CredentialValidationCallback validator)
+{
+	if (method_type < 0 || method_type >= CVT_COUNT)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid validation method type: %d", method_type)));
+
+	validators[method_type] = validator;
+}
+
+/*
+ * Check credential validity using the appropriate validator
+ */
+CredentialValidationStatus
+CheckCredentialValidity(void)
+{
+	CredentialValidationCallback validator = NULL;
+	CredentialValidationStatus status;
+
+	/*
+	 * Skip validation for:
+	 * - During shutdown or recovery
+	 * - Non-client backends (any process not serving a client connection)
+	 * - AutoVacuum processes (launcher and workers)
+	 * - Background worker processes
+	 * - Authentication is in progress
+	 */
+	if (proc_exit_inprogress ||
+		RecoveryInProgress() ||
+		!IsExternalConnectionBackend(MyBackendType) ||
+		AmAutoVacuumLauncherProcess() ||
+		AmAutoVacuumWorkerProcess() ||
+		AmBackgroundWorkerProcess() ||
+		ClientAuthInProgress)
+		return CVS_VALID;
+	/*
+	 * Use the session's authentication method from MyClientConnectionInfo
+	 * to select the appropriate validator.
+	 */
+	if (MyClientConnectionInfo.authn_id != NULL)
+	{
+		CredentialValidationType validation_type;
+
+		validation_type = UserAuthToValidationType(MyClientConnectionInfo.auth_method);
+
+		/*
+		 * If we have a valid validation type, get the corresponding
+		 * validator
+		 */
+		if (validation_type < CVT_COUNT)
+			validator = validators[validation_type];
+
+	}
+
+	/*
+	 * If no validator found for the current auth method or no
+	 * authenticated session, skip validation and consider credentials
+	 * valid
+	 */
+	if (validator == NULL || !MyClientConnectionInfo.authn_id)
+			return CVS_VALID;
+
+	/* Call the validator and interpret result */
+	elog(DEBUG1, "credential validation: validating auth_method=%d", (int) MyClientConnectionInfo.auth_method);
+
+	PG_TRY();
+	{
+		bool		result = validator();
+
+		status = result ? CVS_VALID : CVS_EXPIRED;
+	}
+	PG_CATCH();
+	{
+		/* Error during validation */
+		FlushErrorState();
+		status = CVS_ERROR;
+	}
+	PG_END_TRY();
+
+	return status;
+}
diff --git a/src/backend/libpq/meson.build b/src/backend/libpq/meson.build
index 8571f652844..2e69685672b 100644
--- a/src/backend/libpq/meson.build
+++ b/src/backend/libpq/meson.build
@@ -4,6 +4,8 @@ backend_sources += files(
   'auth-oauth.c',
   'auth-sasl.c',
   'auth-scram.c',
+  'auth-validate-methods.c',
+  'auth-validate.c',
   'auth.c',
   'be-fsstubs.c',
   'be-secure-common.c',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b3563113219..ce9f9cef5c5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -44,6 +44,7 @@
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
+#include "libpq/auth-validate.h"
 #include "mb/pg_wchar.h"
 #include "mb/stringinfo_mb.h"
 #include "miscadmin.h"
@@ -98,6 +99,25 @@ bool		Log_disconnections = false;
 
 int			log_statement = LOGSTMT_NONE;
 
+
+
+/*
+ * Function that performs credential validation when needed
+ * Uses a timer-based approach to periodically validate credentials
+ * during normal operation, skipping validation in bootstrapping.
+ */
+static void
+CheckAndExecuteCredentialValidation(void)
+{
+	CredentialValidationTimeoutPending = false;
+
+	/* Process credential validation */
+	ProcessCredentialValidation();
+
+	/* Re-enable the timeout for the next validation cycle */
+	EnableCredentialValidationTimeout();
+}
+
 /* wait N seconds to allow attach from a debugger */
 int			PostAuthDelay = 0;
 
@@ -1050,6 +1070,10 @@ exec_simple_query(const char *query_string)
 	 */
 	start_xact_command();
 
+	/* Check and potentially execute credential validation */
+	if (CredentialValidationTimeoutPending && IsNormalProcessingMode())
+		CheckAndExecuteCredentialValidation();
+
 	/*
 	 * Zap any pre-existing unnamed statement.  (While not strictly necessary,
 	 * it seems best to define simple-Query mode as if it used the unnamed
@@ -1431,6 +1455,11 @@ exec_parse_message(const char *query_string,	/* string to execute */
 	 */
 	start_xact_command();
 
+
+	/* Check and potentially execute credential validation */
+	if (CredentialValidationTimeoutPending && IsNormalProcessingMode())
+		CheckAndExecuteCredentialValidation();
+
 	/*
 	 * Switch to appropriate context for constructing parsetrees.
 	 *
@@ -1706,6 +1735,10 @@ exec_bind_message(StringInfo input_message)
 	 */
 	start_xact_command();
 
+	/* Check and potentially execute credential validation */
+	if (CredentialValidationTimeoutPending && IsNormalProcessingMode())
+		CheckAndExecuteCredentialValidation();
+
 	/* Switch back to message context */
 	MemoryContextSwitchTo(MessageContext);
 
@@ -2218,6 +2251,10 @@ exec_execute_message(const char *portal_name, long max_rows)
 	 */
 	start_xact_command();
 
+	/* Check and potentially execute credential validation */
+	if (CredentialValidationTimeoutPending && IsNormalProcessingMode())
+		CheckAndExecuteCredentialValidation();
+
 	/*
 	 * If we re-issue an Execute protocol request against an existing portal,
 	 * then we are only fetching more rows rather than completely re-executing
@@ -2636,6 +2673,10 @@ exec_describe_statement_message(const char *stmt_name)
 	 */
 	start_xact_command();
 
+	/* Check and potentially execute credential validation */
+	if (CredentialValidationTimeoutPending && IsNormalProcessingMode())
+		CheckAndExecuteCredentialValidation();
+
 	/* Switch back to message context */
 	MemoryContextSwitchTo(MessageContext);
 
@@ -2728,6 +2769,10 @@ exec_describe_portal_message(const char *portal_name)
 	 */
 	start_xact_command();
 
+	/* Check and potentially execute credential validation */
+	if (CredentialValidationTimeoutPending && IsNormalProcessingMode())
+		CheckAndExecuteCredentialValidation();
+
 	/* Switch back to message context */
 	MemoryContextSwitchTo(MessageContext);
 
@@ -4635,6 +4680,11 @@ PostgresMain(const char *dbname, const char *username)
 					enable_timeout_after(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 										 IdleInTransactionSessionTimeout);
 				}
+
+				/* Re-enable credential validation timer if needed */
+				if (credential_validation_enabled &&
+					!get_timeout_active(CREDENTIAL_VALIDATION_TIMEOUT))
+					EnableCredentialValidationTimeout();
 			}
 			else
 			{
@@ -4687,6 +4737,11 @@ PostgresMain(const char *dbname, const char *username)
 					enable_timeout_after(IDLE_SESSION_TIMEOUT,
 										 IdleSessionTimeout);
 				}
+
+				/* Re-enable credential validation timer if needed */
+				if (credential_validation_enabled &&
+					!get_timeout_active(CREDENTIAL_VALIDATION_TIMEOUT))
+					EnableCredentialValidationTimeout();
 			}
 
 			/* Report any recently-changed GUC options */
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 36ad708b360..aab526a45dd 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -34,6 +34,7 @@ volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t CheckClientConnectionPending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t CredentialValidationTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t TransactionTimeoutPending = false;
 volatile sig_atomic_t IdleSessionTimeoutPending = false;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 26118661f07..dceb7453013 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -34,6 +34,7 @@
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_tablespace.h"
 #include "libpq/auth.h"
+#include "libpq/auth-validate.h"
 #include "libpq/libpq-be.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
@@ -90,6 +91,7 @@ static void TransactionTimeoutHandler(void);
 static void IdleSessionTimeoutHandler(void);
 static void IdleStatsUpdateTimeoutHandler(void);
 static void ClientCheckTimeoutHandler(void);
+static void CredentialValidationTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
 static void process_settings(Oid databaseid, Oid roleid);
@@ -774,6 +776,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
 		RegisterTimeout(CLIENT_CONNECTION_CHECK_TIMEOUT, ClientCheckTimeoutHandler);
 		RegisterTimeout(IDLE_STATS_UPDATE_TIMEOUT,
 						IdleStatsUpdateTimeoutHandler);
+		RegisterTimeout(CREDENTIAL_VALIDATION_TIMEOUT,
+						CredentialValidationTimeoutHandler);
 	}
 
 	/*
@@ -1227,6 +1231,12 @@ InitPostgres(const char *in_dbname, Oid dboid,
 	/* Initialize this backend's session state. */
 	InitializeSession();
 
+	/* Initialize credential validation system */
+	InitializeCredentialValidation();
+
+	/* Enable credential validation timeout if configured */
+	EnableCredentialValidationTimeout();
+
 	/*
 	 * If this is an interactive session, load any libraries that should be
 	 * preloaded at backend start.  Since those are determined by GUCs, this
@@ -1433,6 +1443,14 @@ IdleStatsUpdateTimeoutHandler(void)
 	SetLatch(MyLatch);
 }
 
+static void
+CredentialValidationTimeoutHandler(void)
+{
+	CredentialValidationTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 ClientCheckTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e4abe6c0077..05e28143281 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -904,6 +904,12 @@
 #include_if_exists = '...'              # include file only if it exists
 #include = '...'                        # include file
 
+#------------------------------------------------------------------------------
+# CREDENTIAL VALIDATION
+#------------------------------------------------------------------------------
+
+credential_validation.enabled = true    # enable periodic credential validation
+credential_validation.interval = 1      # validation interval in minutes (1-60)
 
 #------------------------------------------------------------------------------
 # CUSTOMIZED OPTIONS
diff --git a/src/include/libpq/auth-validate-methods.h b/src/include/libpq/auth-validate-methods.h
new file mode 100644
index 00000000000..420183a1c7d
--- /dev/null
+++ b/src/include/libpq/auth-validate-methods.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * auth-validate-methods.h
+ *      Interface for authentication credential validation methods
+ *
+ * This file provides declarations for various credential validation methods
+ * used with the credential validation system.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/libpq/auth-validate-methods.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef AUTH_VALIDATE_METHODS_H
+#define AUTH_VALIDATE_METHODS_H
+
+#include "libpq/libpq-be.h"
+#include "utils/timestamp.h"
+
+/* Initialize all validation methods */
+extern void InitializeValidationMethods(void);
+
+#endif                          /* AUTH_VALIDATE_METHODS_H */
diff --git a/src/include/libpq/auth-validate.h b/src/include/libpq/auth-validate.h
new file mode 100644
index 00000000000..ea08c52fdb9
--- /dev/null
+++ b/src/include/libpq/auth-validate.h
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * auth-validate.h
+ *	  Interface for authentication credential validation
+ *
+ * This file provides a common interface for validating credentials
+ * during an active PostgreSQL session.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/libpq/auth-validate.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef AUTH_VALIDATE_H
+#define AUTH_VALIDATE_H
+
+#include "libpq/libpq-be.h"
+#include "libpq/protocol.h"
+#include "postmaster/postmaster.h"
+#include "utils/guc.h"
+#include "utils/timeout.h"
+
+/* Define credential validation method types as an enum */
+typedef enum CredentialValidationType
+{
+	CVT_PASSWORD = 0,          /* All password-based methods (md5, scram, etc) */
+	CVT_OAUTH,                 /* OAuth bearer token authentication */
+	CVT_COUNT                  /* Total number of credential validation types */
+} CredentialValidationType;
+
+/* Process credential validation */
+extern void ProcessCredentialValidation(void);
+
+/* GUC variables */
+extern PGDLLIMPORT bool credential_validation_enabled;
+extern PGDLLIMPORT int credential_validation_interval;
+
+/* Common credential validation callback prototype */
+typedef bool (*CredentialValidationCallback) (void);
+
+/* Credential validation status */
+typedef enum CredentialValidationStatus
+{
+	CVS_VALID,					/* Credentials are valid */
+	CVS_EXPIRED,				/* Credentials have expired */
+	CVS_ERROR					/* Error during validation */
+} CredentialValidationStatus;
+
+/* Initialize credential validation system */
+extern void InitializeCredentialValidation(void);
+
+/* Register a validation callback for a specific authentication method */
+extern void RegisterCredentialValidator(CredentialValidationType method_type,
+										CredentialValidationCallback validator);
+
+/* Check credential validity */
+extern CredentialValidationStatus CheckCredentialValidity(void);
+
+/* Enable credential validation timeout timer */
+extern void EnableCredentialValidationTimeout(void);
+
+#endif							/* AUTH_VALIDATE_H */
diff --git a/src/include/libpq/oauth.h b/src/include/libpq/oauth.h
index 4a822e9a1f2..2fd30c55814 100644
--- a/src/include/libpq/oauth.h
+++ b/src/include/libpq/oauth.h
@@ -64,6 +64,7 @@ typedef void (*ValidatorShutdownCB) (ValidatorModuleState *state);
 typedef bool (*ValidatorValidateCB) (const ValidatorModuleState *state,
 									 const char *token, const char *role,
 									 ValidatorModuleResult *result);
+typedef bool (*ValidatorExpireCB) (const ValidatorModuleState *state);
 
 /*
  * Identifies the compiled ABI version of the validator module. Since the server
@@ -71,7 +72,9 @@ typedef bool (*ValidatorValidateCB) (const ValidatorModuleState *state,
  * versions, this is reserved for emergency use within a stable release line.
  * May it never need to change.
  */
-#define PG_OAUTH_VALIDATOR_MAGIC 0x20250220
+#define PG_OAUTH_VALIDATOR_MAGIC_V1 0x20250220
+#define PG_OAUTH_VALIDATOR_MAGIC_V2 0x20260326
+#define PG_OAUTH_VALIDATOR_MAGIC PG_OAUTH_VALIDATOR_MAGIC_V2
 
 typedef struct OAuthValidatorCallbacks
 {
@@ -80,6 +83,7 @@ typedef struct OAuthValidatorCallbacks
 	ValidatorStartupCB startup_cb;
 	ValidatorShutdownCB shutdown_cb;
 	ValidatorValidateCB validate_cb;
+	ValidatorExpireCB expire_cb;  /* Optional: Check token expiration */
 } OAuthValidatorCallbacks;
 
 /*
@@ -98,4 +102,8 @@ extern PGDLLIMPORT const pg_be_sasl_mech pg_be_oauth_mech;
  */
 extern bool check_oauth_validator(HbaLine *hbaline, int elevel, char **err_msg);
 
+/*
+ * Check OAuth token expiration using validator's expire_cb if available.
+ */
+bool CheckOAuthValidatorExpiration(void);
 #endif							/* PG_OAUTH_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f16f35659b9..42a09e2f299 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -99,6 +99,7 @@ extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending;
 
 extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
 extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;
+extern PGDLLIMPORT volatile sig_atomic_t CredentialValidationTimeoutPending;
 
 /* these are marked volatile because they are examined by signal handlers: */
 extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 0965b590b34..d4673a8a408 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -36,6 +36,7 @@ typedef enum TimeoutId
 	IDLE_STATS_UPDATE_TIMEOUT,
 	CLIENT_CONNECTION_CHECK_TIMEOUT,
 	STARTUP_PROGRESS_TIMEOUT,
+	CREDENTIAL_VALIDATION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
 	/* Maximum number of timeout reasons */
diff --git a/src/test/modules/oauth_validator/fail_validator.c b/src/test/modules/oauth_validator/fail_validator.c
index 3de0470a541..8754e1e8f85 100644
--- a/src/test/modules/oauth_validator/fail_validator.c
+++ b/src/test/modules/oauth_validator/fail_validator.c
@@ -29,6 +29,7 @@ static const OAuthValidatorCallbacks validator_callbacks = {
 	PG_OAUTH_VALIDATOR_MAGIC,
 
 	.validate_cb = fail_token,
+	.expire_cb = NULL,
 };
 
 const OAuthValidatorCallbacks *
diff --git a/src/test/modules/oauth_validator/magic_validator.c b/src/test/modules/oauth_validator/magic_validator.c
index 550da41d11b..6e4d72fde30 100644
--- a/src/test/modules/oauth_validator/magic_validator.c
+++ b/src/test/modules/oauth_validator/magic_validator.c
@@ -30,6 +30,7 @@ static const OAuthValidatorCallbacks validator_callbacks = {
 	0xdeadbeef,
 
 	.validate_cb = validate_token,
+	.expire_cb = NULL,
 };
 
 const OAuthValidatorCallbacks *
diff --git a/src/test/modules/oauth_validator/validator.c b/src/test/modules/oauth_validator/validator.c
index 0b983a9dc8f..2784708a784 100644
--- a/src/test/modules/oauth_validator/validator.c
+++ b/src/test/modules/oauth_validator/validator.c
@@ -34,7 +34,8 @@ static const OAuthValidatorCallbacks validator_callbacks = {
 
 	.startup_cb = validator_startup,
 	.shutdown_cb = validator_shutdown,
-	.validate_cb = validate_token
+	.validate_cb = validate_token,
+	.expire_cb = NULL,			/* Optional: not implemented */
 };
 
 /* GUCs */
