diff --git a/src/condor_daemon_core.V6/daemon_command.cpp b/src/condor_daemon_core.V6/daemon_command.cpp
index 63a879489e..ba9c5d691d 100644
--- a/src/condor_daemon_core.V6/daemon_command.cpp
+++ b/src/condor_daemon_core.V6/daemon_command.cpp
@@ -881,6 +881,7 @@ DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::ReadCommand(
 					m_policy->LookupBool(ATTR_SEC_TRIED_AUTHENTICATION,tried_authentication);
 					m_sock->setTriedAuthentication(tried_authentication);
 					m_sock->setSessionID(session->id());
+					m_sock->setPolicyAd(*m_policy);
 				}
 
 				// If the cached policy doesn't have a version, then
@@ -1542,7 +1543,8 @@ DaemonCommandProtocol::CommandProtocolResult DaemonCommandProtocol::VerifyComman
 				// these limits if present.
 			std::string authz_policy;
 			bool can_attempt = true;
-			if (m_policy && m_policy->EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_policy)) {
+			const ClassAd* policy_ad = m_policy ? m_policy : m_sock->getPolicyAd();
+			if (policy_ad && policy_ad->EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_policy)) {
 				std::set<DCpermission> authz_limits;
 				for (const auto& limit_str: StringTokenIterator(authz_policy)) {
 					DCpermission limit_perm = getPermissionFromString(limit_str.c_str());
diff --git a/src/condor_daemon_core.V6/daemon_core_main.cpp b/src/condor_daemon_core.V6/daemon_core_main.cpp
index 1a7aa5fdc8..5177504617 100644
--- a/src/condor_daemon_core.V6/daemon_core_main.cpp
+++ b/src/condor_daemon_core.V6/daemon_core_main.cpp
@@ -1930,6 +1930,16 @@ handle_dc_start_token_request(int, Stream* stream)
 		return false;
 	}
 
+	if (!param_boolean("SEC_ENABLE_TOKEN_REQUEST", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "Token request disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	int error_code = 0;
 	std::string error_string;
 
@@ -2107,6 +2117,16 @@ handle_dc_finish_token_request(int, Stream* stream)
 		return false;
 	}
 
+	if (!param_boolean("SEC_ENABLE_TOKEN_REQUEST", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "Token request disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	int error_code = 0;
 	std::string error_string;
 
@@ -2203,6 +2223,16 @@ handle_dc_list_token_request(int, Stream* stream)
 		return false;
 	}
 
+	if (!param_boolean("SEC_ENABLE_TOKEN_REQUEST", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "Token request disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	int error_code = 0;
 	std::string error_string;
 
@@ -2310,6 +2340,16 @@ handle_dc_approve_token_request(int, Stream* stream)
 		return false;
 	}
 
+	if (!param_boolean("SEC_ENABLE_TOKEN_REQUEST", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "Token request disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	int error_code = 0;
 	std::string error_string;
 
@@ -2370,6 +2410,40 @@ handle_dc_approve_token_request(int, Stream* stream)
 		request_id = -1;
 	}
 
+	if (!error_code && !has_admin && static_cast<Sock*>(stream)->hasAuthorizationBoundingSet()) {
+		auto& token_authz_set = iter->second->getBoundingSet();
+		bool reject = false;
+		if (token_authz_set.empty()) {
+			reject = true;
+		}
+		for (const auto& authz: token_authz_set) {
+			if (! static_cast<Sock*>(stream)->isAuthorizationInBoundingSet(authz)) {
+				reject = true;
+				break;
+			}
+		}
+		if (reject) {
+			error_code = 7;
+			error_string = "Insufficient privilege to approve request (scope restricted).";
+			request_id = -1;
+		}
+	}
+
+	if (!error_code && !has_admin) {
+		const ClassAd* policy_ad = static_cast<ReliSock*>(stream)->getPolicyAd();
+		time_t client_expiry = -1;
+		if (policy_ad) {
+			policy_ad->LookupInteger("TokenExpirationTime", client_expiry);
+		}
+		time_t request_lifetime = iter->second->getLifetime();
+		if ((request_lifetime == -1 && client_expiry >= 0) ||
+		    (request_lifetime >= 0 && client_expiry >= 0 && (request_lifetime + time(nullptr) > client_expiry))) {
+			error_code = 8;
+			error_string = "Insufficient privilege to approve request (lifetime).";
+			request_id = -1;
+		}
+	}
+
 	CondorError err;
 	std::string final_key_name = htcondor::get_token_signing_key(err);
 	if ((request_id != -1) && final_key_name.empty()) {
@@ -2424,6 +2498,16 @@ handle_dc_auto_approve_token_request(int, Stream* stream )
 		return false;
 	}
 
+	if (!param_boolean("SEC_ENABLE_TOKEN_REQUEST", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "Token request disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	std::string netblock;
 	time_t lifetime = -1;
 	ad.EvaluateAttrString(ATTR_SUBNET, netblock);
@@ -2516,6 +2600,16 @@ handle_dc_exchange_scitoken(int, Stream *stream)
 		return false;
 	}
 
+	if (!param_boolean("SEC_ENABLE_SCITOKEN_EXCHANGE", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "SciToken exchange disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	classad::ClassAd result_ad;
 	std::string result_token;
 	int error_code = 0;
@@ -2616,12 +2710,49 @@ handle_dc_session_token(int, Stream* stream)
 		dprintf(D_FULLDEBUG, "handle_dc_session_token: failed to read input from client\n");
 		return false;
 	}
+
+	if (!param_boolean("SEC_ENABLE_TOKEN_FETCH", true)) {
+		ClassAd result_ad;
+		result_ad.InsertAttr(ATTR_ERROR_STRING, "Token fetch disabled.");
+		result_ad.InsertAttr(ATTR_ERROR_CODE, 44);
+		stream->encode();
+		putClassAd(stream, result_ad);
+		stream->end_of_message();
+		return false;
+	}
+
 	CondorError err;
 	classad::ClassAd result_ad;
 
+	ReliSock* sock = static_cast<ReliSock*>(stream);
 	std::vector<std::string> authz_list;
 	std::string authz_list_str;
-	if (ad.EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_list_str)) {
+	ad.EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_list_str);
+	bool sock_has_authz_limit = sock->hasAuthorizationBoundingSet();
+	if (!authz_list_str.empty()) {
+		if (sock_has_authz_limit) {
+			for (const auto& item: StringTokenIterator(authz_list_str)) {
+				if (sock->isAuthorizationInBoundingSet(item)) {
+					authz_list.emplace_back(item);
+				}
+			}
+			if (authz_list.empty()) {
+				result_ad.InsertAttr(ATTR_ERROR_STRING, "Session doesn't allow requested authorization levels.");
+				result_ad.InsertAttr(ATTR_ERROR_CODE, 4);
+				stream->encode();
+				if (!putClassAd(stream, result_ad) ||
+					!stream->end_of_message())
+				{
+					dprintf(D_FULLDEBUG, "handle_dc_session_token: failed to send response ad to client\n");
+					return false;
+				}
+				return true;
+			}
+		} else {
+			authz_list = split(authz_list_str);
+		}
+	} else if (sock_has_authz_limit) {
+		sock->getPolicyAd()->LookupString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_list_str);
 		authz_list = split(authz_list_str);
 	}
 	int requested_lifetime;
diff --git a/src/condor_includes/sock.h b/src/condor_includes/sock.h
index 20af90ddf1..ea63d23758 100644
--- a/src/condor_includes/sock.h
+++ b/src/condor_includes/sock.h
@@ -393,6 +393,7 @@ public:
 	void setAuthenticatedName(char const *auth_name);
 	const char *getAuthenticatedName() const;
 
+	bool hasAuthorizationBoundingSet() const;
 	bool isAuthorizationInBoundingSet(const std::string &) const;
 
 	void setCryptoMethodUsed(char const *crypto_method);
@@ -683,6 +684,8 @@ private:
 	   connection attempt.
 	 **/
 	void cancel_connect();
+
+	void computeAuthorizationBoundingSet() const;
 };
 
 void dprintf ( int flags, const Sock & sock, const char *fmt, ... ) CHECK_PRINTF_FORMAT(3,4);
diff --git a/src/condor_io/sock.cpp b/src/condor_io/sock.cpp
index aa6c21bf8e..685cafbbf8 100644
--- a/src/condor_io/sock.cpp
+++ b/src/condor_io/sock.cpp
@@ -302,6 +302,7 @@ Sock::setPolicyAd(const classad::ClassAd &ad)
 {
 	if (!_policy_ad) {_policy_ad = new classad::ClassAd();}
 	if (_policy_ad) {_policy_ad->CopyFrom(ad);}
+	m_authz_bound.clear();
 }
 
 
@@ -315,6 +316,41 @@ Sock::getPolicyAd(classad::ClassAd &ad) const
 }
 
 
+void
+Sock::computeAuthorizationBoundingSet() const
+{
+	const_cast<Sock*>(this)->m_authz_bound.clear();
+	if (_policy_ad) {
+		std::string authz_policy;
+		if (_policy_ad->EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_policy))
+		{
+			for (const auto& authz_name : StringTokenIterator(authz_policy)) {
+				const_cast<Sock*>(this)->m_authz_bound.insert(authz_name);
+				DCpermission implied_perm = getPermissionFromString(authz_name.c_str());
+				if (implied_perm != NOT_A_PERM) {
+					while ((implied_perm = DCpermissionHierarchy::nextImplied(implied_perm)) < LAST_PERM) {
+						const_cast<Sock*>(this)->m_authz_bound.insert(PermString(implied_perm));
+					}
+				}
+			}
+		}
+	}
+	if (m_authz_bound.empty()) {
+			// Put in a nonsense authz level to prevent re-parsing;
+			// an empty bounding set is interpretted as no bounding set at all.
+		const_cast<Sock*>(this)->m_authz_bound.insert("ALL_PERMISSIONS");
+	}
+}
+
+bool
+Sock::hasAuthorizationBoundingSet() const
+{
+	if (m_authz_bound.empty()) {
+		computeAuthorizationBoundingSet();
+	}
+	return m_authz_bound.find("ALL_PERMISSIONS") == m_authz_bound.end();
+}
+
 bool
 Sock::isAuthorizationInBoundingSet(const std::string &authz) const
 {
@@ -326,26 +362,7 @@ Sock::isAuthorizationInBoundingSet(const std::string &authz) const
 		// Cache the bounding set on first access.
 	if (m_authz_bound.empty())
 	{
-		if (_policy_ad) {
-			std::string authz_policy;
-			if (_policy_ad->EvaluateAttrString(ATTR_SEC_LIMIT_AUTHORIZATION, authz_policy))
-			{
-				for (const auto& authz_name : StringTokenIterator(authz_policy)) {
-					const_cast<Sock*>(this)->m_authz_bound.insert(authz_name);
-					DCpermission implied_perm = getPermissionFromString(authz_name.c_str());
-					if (implied_perm != NOT_A_PERM) {
-						while ((implied_perm = DCpermissionHierarchy::nextImplied(implied_perm)) < LAST_PERM) {
-							const_cast<Sock*>(this)->m_authz_bound.insert(PermString(implied_perm));
-						}
-					}
-				}
-			}
-		}
-		if (m_authz_bound.empty()) {
-				// Put in a nonsense authz level to prevent re-parsing;
-				// an empty bounding set is interpretted as no bounding set at all.
-			const_cast<Sock*>(this)->m_authz_bound.insert("ALL_PERMISSIONS");
-		}
+		computeAuthorizationBoundingSet();
 	}
 	return (m_authz_bound.find(authz) != m_authz_bound.end()) ||
 		(m_authz_bound.find("ALL_PERMISSIONS") != m_authz_bound.end());
diff --git a/src/condor_utils/param_info.in b/src/condor_utils/param_info.in
index 59b72ee01d..dd6de69cd1 100644
--- a/src/condor_utils/param_info.in
+++ b/src/condor_utils/param_info.in
@@ -2565,6 +2565,18 @@ description=Default max expiration time of issued session tokens.
 type=int
 tags=condor_auth_passwd
 
+[SEC_ENABLE_TOKEN_REQUEST]
+default=true
+type=bool
+
+[SEC_ENABLE_TOKEN_FETCH]
+default=true
+type=bool
+
+[SEC_ENABLE_SCITOKEN_EXCHANGE]
+default=true
+type=bool
+
 [EMAIL_DOMAIN]
 default=
 type=string
