diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 98eb2a8242d..d263ca8d931 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -18,6 +18,7 @@ OBJS = \
 	auth-oauth.o \
 	auth-sasl.o \
 	auth-scram.o \
+	auth-validate.o \
 	auth.o \
 	be-fsstubs.o \
 	be-secure-common.o \
@@ -25,6 +26,7 @@ OBJS = \
 	crypt.o \
 	hba.o \
 	ifaddr.o \
+	password-validate.o \
 	pqcomm.o \
 	pqformat.o \
 	pqmq.o \
diff --git a/src/backend/libpq/auth-validate.c b/src/backend/libpq/auth-validate.c
new file mode 100644
index 00000000000..ed1218377a3
--- /dev/null
+++ b/src/backend/libpq/auth-validate.c
@@ -0,0 +1,296 @@
+/*-------------------------------------------------------------------------
+*
+* auth-validate.c
+*      Implementation of authentication credential validation
+*
+* 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 <time.h>
+#include <sys/time.h>
+
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "libpq/auth.h"
+#include "libpq/hba.h"
+#include "libpq/libpq-be.h"
+#include "libpq/auth-validate.h"
+#include "miscadmin.h"
+#include "postmaster/postmaster.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/guc.h"
+#include "utils/ps_status.h"
+#include "utils/snapmgr.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[AUTH_REQ_LAST];
+
+/*
+ * Convert UserAuth enum to AUTH_REQ_* constant for validator selection
+ */
+static int
+UserAuthToAuthReq(UserAuth auth_method)
+{
+	switch (auth_method)
+	{
+		case uaPassword:
+		case uaMD5:
+		case uaSCRAM:
+		/* All password-based methods use the password validator */
+			return AUTH_REQ_PASSWORD;
+		default:
+			/* No specific validator for other auth methods */
+			return -1;
+	}
+}
+
+/*
+ * Process credential validation
+ */
+void
+ProcessCredentialValidation(void)
+{
+	/* Skip validation during authentication or connection setup */
+	if (ClientAuthInProgress)
+		return;
+
+	/* Check credentials if validation is enabled */
+	if (credential_validation_enabled && MyClientConnectionInfo.authn_id != NULL)
+	{
+		CredentialValidationStatus status;
+		UserAuth	auth_method = MyClientConnectionInfo.auth_method;
+
+		elog(DEBUG1, "credential validation: checking credentials for auth_method=%d",
+			 (int) auth_method);
+
+		status = CheckCredentialValidity();
+
+		switch (status)
+		{
+			case CVS_VALID:
+				/* Credentials are valid, continue */
+				elog(DEBUG1, "credential validation: credentials valid for auth_method=%d",
+					 (int) auth_method);
+				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 seconds.",
+							NULL,
+							&credential_validation_interval,
+							60,
+							10,
+							3600,
+							PGC_SUSET,
+							0,
+							NULL,
+							NULL,
+							NULL);
+
+	/* Initialize validator callbacks to NULL */
+	for (i = 0; i < AUTH_REQ_LAST; i++)
+		validators[i] = NULL;
+
+	/* Register built-in validators */
+	RegisterCredentialValidator(AUTH_REQ_PASSWORD, validate_password_credentials);
+
+	/* Ensure we log the registration of the validator */
+	elog(DEBUG1, "Registered password validator for AUTH_REQ_PASSWORD=%d", AUTH_REQ_PASSWORD);
+
+	/*
+	 * Schedule the first credential validation check if enabled. The
+	 * timeout is already registered in postinit.c.
+	 */
+	if (credential_validation_enabled && credential_validation_interval > 0)
+	{
+			/* Enable periodic checks at the specified interval */
+			enable_timeout_every(CREDENTIAL_VALIDATION_TIMEOUT,
+								 GetCurrentTimestamp(),
+								 credential_validation_interval * 1000);
+	}
+
+	/* Initialize method-specific validation systems */
+	InitializePasswordValidation();
+}
+
+/*
+ * Register a validator callback for a specific authentication method
+ */
+void
+RegisterCredentialValidator(int authmethod, CredentialValidationCallback validator)
+{
+	if (authmethod < 0 || authmethod >= AUTH_REQ_LAST)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid authentication method code: %d", authmethod)));
+
+	validators[authmethod] = validator;
+}
+
+/*
+ * Check credential validity using the appropriate validator
+ */
+CredentialValidationStatus
+CheckCredentialValidity(void)
+{
+	CredentialValidationCallback validator = NULL;
+	int			auth_req_code = -1;
+	bool		started_transaction = false;
+	bool		need_snapshot = false;
+
+	/*
+	 * Skip validation if not in a transaction state or during shutdown or
+	 * Recovery
+	 */
+	if (proc_exit_inprogress || RecoveryInProgress())
+		return CVS_VALID;
+
+	/*
+	 * Use the session's authentication method from MyClientConnectionInfo
+	 * to select the appropriate validator.
+	 */
+	if (MyClientConnectionInfo.authn_id != NULL)
+	{
+		auth_req_code = UserAuthToAuthReq(MyClientConnectionInfo.auth_method);
+
+		/*
+		 * If we have a valid auth_req_code, get the corresponding
+		 * validator
+		 */
+		if (auth_req_code >= 0 && auth_req_code < AUTH_REQ_LAST)
+			validator = validators[auth_req_code];
+
+		/* Log detailed info at DEBUG1 level for troubleshooting */
+		elog(DEBUG1, "credential validation: auth_method=%d, auth_req_code=%d, validator=%p",
+				 (int) MyClientConnectionInfo.auth_method, auth_req_code, validator);
+	}
+	else
+	{
+		elog(DEBUG1, "credential validation: no authn_id available");
+	}
+
+	/*
+	 * 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 */
+	PG_TRY();
+	{
+		bool		result;
+		CredentialValidationStatus status;
+
+		/* Start a transaction if we're not in one */
+		if (!IsTransactionState())
+		{
+			StartTransactionCommand();
+			started_transaction = true;
+		}
+
+		/* Ensure we have an active snapshot for catalog access */
+		if (!ActiveSnapshotSet())
+		{
+			PushActiveSnapshot(GetTransactionSnapshot());
+			need_snapshot = true;
+		}
+
+		elog(DEBUG1, "credential validation: calling validator for auth_method=%d",
+			 (int) MyClientConnectionInfo.auth_method);
+
+		result = validator();
+
+		if (!result)
+		{
+			elog(DEBUG1, "credential validation: credentials expired");
+			status = CVS_EXPIRED;	/* Validator reports credentials expired */
+		}
+		else
+			status = CVS_VALID;
+
+		if (need_snapshot)
+			PopActiveSnapshot();
+
+		if (started_transaction)
+			CommitTransactionCommand();
+
+		return status;
+	}
+	PG_CATCH();
+	{
+		if (need_snapshot)
+			PopActiveSnapshot();
+
+		if (started_transaction)
+			CommitTransactionCommand();
+
+		/* Error during validation */
+		elog(DEBUG1, "credential validation: error during validation");
+		FlushErrorState();
+		return CVS_ERROR;
+	}
+	PG_END_TRY();
+
+	/* Validation passed, credentials are valid */
+	return CVS_VALID;
+}
diff --git a/src/backend/libpq/password-validate.c b/src/backend/libpq/password-validate.c
new file mode 100644
index 00000000000..0e04db447ca
--- /dev/null
+++ b/src/backend/libpq/password-validate.c
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+*
+* password-validate.c
+*	  Password validation for PostgreSQL
+*
+* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+* Portions Copyright (c) 1994, Regents of the University of California
+*
+* IDENTIFICATION
+*	  src/backend/libpq/password-validate.c
+*
+*-------------------------------------------------------------------------
+*/
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "catalog/pg_authid.h"
+#include "libpq/auth-validate.h"
+	/* #include "libpq/libpq-be.h" */
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.h"
+
+/*
+ * Initialize password validation
+ */
+void
+InitializePasswordValidation(void)
+{
+}
+
+/*
+ * Validate password credentials by checking rolvaliduntil
+ */
+bool
+validate_password_credentials(void)
+{
+	HeapTuple	tuple;
+	Datum		rolvaliduntil_datum;
+	bool		validuntil_null;
+	TimestampTz valid_until = 0;
+
+	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetSessionUserId()));
+
+	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);
+
+			if (valid_until < GetCurrentTimestamp())
+			{
+				ReleaseSysCache(tuple);
+				return false;
+			}
+		}
+		ReleaseSysCache(tuple);
+		return true;
+	}
+	else
+		return false;		/* If user not found, consider Invalid */
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d01a09dd0c4..d5c6d65063f 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"
@@ -185,6 +186,7 @@ static void report_recovery_conflict(RecoveryConflictReason reason);
 static void log_disconnections(int code, Datum arg);
 static void enable_statement_timeout(void);
 static void disable_statement_timeout(void);
+static void ExecuteCredentialValidationCheck(void);
 
 
 /* ----------------------------------------------------------------
@@ -1049,6 +1051,9 @@ exec_simple_query(const char *query_string)
 	 */
 	start_xact_command();
 
+	if (CredentialValidationPending)
+		ExecuteCredentialValidationCheck();
+
 	/*
 	 * Zap any pre-existing unnamed statement.  (While not strictly necessary,
 	 * it seems best to define simple-Query mode as if it used the unnamed
@@ -1430,6 +1435,9 @@ exec_parse_message(const char *query_string,	/* string to execute */
 	 */
 	start_xact_command();
 
+	if (CredentialValidationPending)
+		ExecuteCredentialValidationCheck();
+
 	/*
 	 * Switch to appropriate context for constructing parsetrees.
 	 *
@@ -1705,6 +1713,9 @@ exec_bind_message(StringInfo input_message)
 	 */
 	start_xact_command();
 
+	if (CredentialValidationPending)
+		ExecuteCredentialValidationCheck();
+
 	/* Switch back to message context */
 	MemoryContextSwitchTo(MessageContext);
 
@@ -2217,6 +2228,9 @@ exec_execute_message(const char *portal_name, long max_rows)
 	 */
 	start_xact_command();
 
+	if (CredentialValidationPending)
+		ExecuteCredentialValidationCheck();
+
 	/*
 	 * If we re-issue an Execute protocol request against an existing portal,
 	 * then we are only fetching more rows rather than completely re-executing
@@ -2635,6 +2649,9 @@ exec_describe_statement_message(const char *stmt_name)
 	 */
 	start_xact_command();
 
+	if (CredentialValidationPending)
+		ExecuteCredentialValidationCheck();
+
 	/* Switch back to message context */
 	MemoryContextSwitchTo(MessageContext);
 
@@ -2727,6 +2744,9 @@ exec_describe_portal_message(const char *portal_name)
 	 */
 	start_xact_command();
 
+	if (CredentialValidationPending)
+		ExecuteCredentialValidationCheck();
+
 	/* Switch back to message context */
 	MemoryContextSwitchTo(MessageContext);
 
@@ -5271,3 +5291,33 @@ disable_statement_timeout(void)
 	if (get_timeout_active(STATEMENT_TIMEOUT))
 		disable_timeout(STATEMENT_TIMEOUT, false);
 }
+
+/*
+ * Process credential validation in exec_simple_query
+ * This is called when CredentialValidationPending flag is set
+ */
+static void
+ExecuteCredentialValidationCheck(void)
+{
+	TimestampTz next_check_time;
+
+	/* Clear the flag immediately */
+	CredentialValidationPending = false;
+
+	/* Check credentials if we're in a transaction */
+	if (IsTransactionState())
+	{
+		ProcessCredentialValidation();
+
+		/* Re-enable the timeout for next check */
+		if (credential_validation_enabled && credential_validation_interval > 0)
+		{
+			/* Calculate next timeout time as current time + full interval */
+			next_check_time = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
+												   credential_validation_interval * 1000);
+
+			/* Use the existing credential validation timeout ID */
+			enable_timeout_at(CREDENTIAL_VALIDATION_TIMEOUT, next_check_time);
+		}
+	}
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 36ad708b360..7981056e3e5 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 CredentialValidationPending = 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 b59e08605cc..37f1c073d6c 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"
@@ -89,6 +90,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);
@@ -773,6 +775,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);
 	}
 
 	/*
@@ -1226,6 +1230,9 @@ InitPostgres(const char *in_dbname, Oid dboid,
 	/* Initialize this backend's session state. */
 	InitializeSession();
 
+	/* Initialize credential validation system */
+	InitializeCredentialValidation();
+
 	/*
 	 * If this is an interactive session, load any libraries that should be
 	 * preloaded at backend start.  Since those are determined by GUCs, this
@@ -1440,6 +1447,12 @@ ClientCheckTimeoutHandler(void)
 	SetLatch(MyLatch);
 }
 
+static void
+CredentialValidationTimeoutHandler(void)
+{
+	CredentialValidationPending = true;
+}
+
 /*
  * Returns true if at least one role is defined in this database cluster.
  */
diff --git a/src/include/libpq/auth-validate.h b/src/include/libpq/auth-validate.h
new file mode 100644
index 00000000000..9d41125537c
--- /dev/null
+++ b/src/include/libpq/auth-validate.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * auth-validate.h
+ *	  Interface for authentication credential validation
+ *
+ * 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 auth method constants needed for credential validation */
+#define AUTH_REQ_SCRAM_SHA_256 20	/* SCRAM SHA-256 authentication */
+#define AUTH_REQ_LAST 21		/* One past the last auth request code */
+
+/* Process credential validation */
+void		ProcessCredentialValidation(void);
+
+/* Method-specific initialization */
+void		InitializePasswordValidation(void);
+
+/* GUC variables */
+extern bool credential_validation_enabled;
+extern int	credential_validation_interval;
+
+/* Common credential validation callback prototype */
+
+/* Common credential validation callback prototype */
+typedef bool (*CredentialValidationCallback) ();
+
+/* Credential validation status */
+typedef enum
+{
+	CVS_VALID,					/* Credentials are valid */
+	CVS_EXPIRED,				/* Credentials have expired */
+	CVS_ERROR					/* Error during validation */
+}CredentialValidationStatus;
+
+/* Initialize credential validation system */
+void InitializeCredentialValidation(void);
+
+/* Register a validation callback for a specific authentication method */
+void RegisterCredentialValidator(int authmethod,
+										CredentialValidationCallback validator);
+
+/* Check credential validity */
+CredentialValidationStatus CheckCredentialValidity(void);
+
+/* Validation callback for password */
+bool validate_password_credentials(void);
+
+#endif							/* AUTH_VALIDATE_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f16f35659b9..96085dd7c9b 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 CredentialValidationPending;
 
 /* 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 */
