diff --git a/app/finders/clusters/agents/authorizations/user_access/finder.rb b/app/finders/clusters/agents/authorizations/user_access/finder.rb
index 6bd0c226337b9429289eb473e2b4d339921b46ad..bde75fa46cb0cea51cfde4e3adde59e657e78bee 100644
--- a/app/finders/clusters/agents/authorizations/user_access/finder.rb
+++ b/app/finders/clusters/agents/authorizations/user_access/finder.rb
@@ -5,9 +5,12 @@ module Agents
     module Authorizations
       module UserAccess
         class Finder
-          def initialize(user, agent:)
+          def initialize(user, agent: nil, project: nil, preload: true, limit: nil)
             @user = user
             @agent = agent
+            @project = project
+            @limit = limit
+            @preload = preload
           end
 
           def execute
@@ -16,19 +19,23 @@ def execute
 
           private
 
-          attr_reader :user, :agent
+          attr_reader :user, :agent, :project, :preload, :limit
 
           def project_authorizations
             authorizations = Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization.for_user(user)
             authorizations = filter_by_agent(authorizations)
-            authorizations = preload(authorizations)
+            authorizations = filter_by_project(authorizations)
+            authorizations = apply_limit(authorizations)
+            authorizations = apply_preload(authorizations)
             authorizations.to_a
           end
 
           def group_authorizations
             authorizations = Clusters::Agents::Authorizations::UserAccess::GroupAuthorization.for_user(user)
             authorizations = filter_by_agent(authorizations)
-            authorizations = preload(authorizations)
+            authorizations = filter_by_project(authorizations)
+            authorizations = apply_limit(authorizations)
+            authorizations = apply_preload(authorizations)
             authorizations.to_a
           end
 
@@ -38,7 +45,21 @@ def filter_by_agent(authorizations)
             authorizations.for_agent(agent)
           end
 
-          def preload(authorizations)
+          def filter_by_project(authorizations)
+            return authorizations unless project.present?
+
+            authorizations.for_project(project)
+          end
+
+          def apply_limit(authorizations)
+            return authorizations unless limit.present?
+
+            authorizations.limit(limit)
+          end
+
+          def apply_preload(authorizations)
+            return authorizations unless preload
+
             authorizations.preloaded
           end
         end
diff --git a/app/graphql/resolvers/clusters/agents/authorizations/user_access_resolver.rb b/app/graphql/resolvers/clusters/agents/authorizations/user_access_resolver.rb
new file mode 100644
index 0000000000000000000000000000000000000000..280db570aa6f1614436554ae1f05ac0b95f2e6b6
--- /dev/null
+++ b/app/graphql/resolvers/clusters/agents/authorizations/user_access_resolver.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Resolvers
+  module Clusters
+    module Agents
+      module Authorizations
+        class UserAccessResolver < BaseResolver
+          type Types::Clusters::Agents::Authorizations::UserAccessType, null: true
+
+          alias_method :project, :object
+
+          def resolve(*)
+            ::Clusters::Agents::Authorizations::UserAccess::Finder.new(current_user, project: project).execute
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/app/graphql/types/clusters/agents/authorizations/user_access_type.rb b/app/graphql/types/clusters/agents/authorizations/user_access_type.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8c5a466cde2c1e17fd332e5515625ff2da400567
--- /dev/null
+++ b/app/graphql/types/clusters/agents/authorizations/user_access_type.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+  module Clusters
+    module Agents
+      module Authorizations
+        class UserAccessType < BaseObject # rubocop:disable Graphql/AuthorizeTypes
+          graphql_name 'ClusterAgentAuthorizationUserAccess'
+
+          field :agent, Types::Clusters::AgentType,
+            description: 'Authorized cluster agent.',
+            null: true
+
+          field :config, GraphQL::Types::JSON, # rubocop:disable Graphql/JSONType
+            description: 'Configuration for the authorized project.',
+            null: true
+        end
+      end
+    end
+  end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index a0f9864351c6a80a328e3876ebb120a49bb282d4..b10b4621534a8647532b091c62bc24125500085a 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -528,6 +528,12 @@ class ProjectType < BaseObject
           resolver: ::Resolvers::Clusters::Agents::Authorizations::CiAccessResolver,
           authorize: :read_cluster_agent
 
+    field :user_access_authorized_agents, ::Types::Clusters::Agents::Authorizations::UserAccessType.connection_type,
+          null: true,
+          description: 'Authorized cluster agents for the project through user_access keyword.',
+          resolver: ::Resolvers::Clusters::Agents::Authorizations::UserAccessResolver,
+          authorize: :read_cluster_agent
+
     field :merge_commit_template, GraphQL::Types::String,
           null: true,
           description: 'Template used to create merge commit message in merge requests.'
diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb
index b5d634927e5775e83627aca5a93a2fca9044db50..6980ec1c2d3a3675590e03431cdce0b410d5930b 100644
--- a/app/models/clusters/agent.rb
+++ b/app/models/clusters/agent.rb
@@ -71,6 +71,14 @@ def ci_access_authorized_for?(user)
       ).exists?
     end
 
+    def user_access_authorized_for?(user)
+      return false unless user
+      return false unless ::Feature.enabled?(:expose_authorized_cluster_agents, project)
+
+      Clusters::Agents::Authorizations::UserAccess::Finder
+        .new(user, agent: self, preload: false, limit: 1).execute.any?
+    end
+
     # As of today, all config values of associated authorization rows have the same value.
     # See `UserAccess::RefreshService` for more information.
     def user_access_config
diff --git a/app/models/clusters/agents/authorizations/user_access/group_authorization.rb b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb
index 486a6011d84924865d434f66f6d0a9819bd499cf..7027870855abf516a63f8c8e3935cce06623e4a0 100644
--- a/app/models/clusters/agents/authorizations/user_access/group_authorization.rb
+++ b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb
@@ -22,6 +22,10 @@ class GroupAuthorization < ApplicationRecord
               .order('id, access_level DESC')
           }
 
+          scope :for_project, ->(project) {
+            where('all_groups_with_membership.traversal_ids @> ARRAY[?]', project.namespace_id)
+          }
+
           validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' }
 
           def config_project
@@ -44,7 +48,9 @@ def all_groups_with_membership_cte
             def all_groups_with_membership
               ::Group.joins('INNER JOIN groups_with_direct_membership ON ' \
                             'namespaces.traversal_ids @> ARRAY[groups_with_direct_membership.id]')
-                     .select('namespaces.id AS id, groups_with_direct_membership.access_level AS access_level')
+                     .select('namespaces.id AS id, ' \
+                             'namespaces.traversal_ids AS traversal_ids, ' \
+                             'groups_with_direct_membership.access_level AS access_level')
             end
 
             def groups_with_direct_membership_cte(user)
diff --git a/app/models/clusters/agents/authorizations/user_access/project_authorization.rb b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb
index 3e2e6f6d35a6b4689ec6a9fcd9f5ceaa2e9e7219..476666e3ad8ab6b7456485df37d018fd24503c0c 100644
--- a/app/models/clusters/agents/authorizations/user_access/project_authorization.rb
+++ b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb
@@ -19,6 +19,8 @@ class ProjectAuthorization < ApplicationRecord
               .select('agent_user_access_project_authorizations.*, project_authorizations.access_level AS access_level')
           }
 
+          scope :for_project, ->(project) { where(project: project) }
+
           validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' }
 
           def config_project
diff --git a/app/policies/clusters/agent_policy.rb b/app/policies/clusters/agent_policy.rb
index afacf782a76abe8279c9b6182433e2153f30ca02..ecd83cceb8bc54b33f5bab427ba34a94c31232f0 100644
--- a/app/policies/clusters/agent_policy.rb
+++ b/app/policies/clusters/agent_policy.rb
@@ -12,7 +12,11 @@ class AgentPolicy < BasePolicy
       @subject.ci_access_authorized_for?(@user)
     end
 
-    rule { ci_access_authorized_agent }.policy do
+    condition(:user_access_authorized_agent, score: 10) do
+      @subject.user_access_authorized_for?(@user)
+    end
+
+    rule { ci_access_authorized_agent | user_access_authorized_agent }.policy do
       enable :read_cluster_agent
     end
   end
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 9babc0dfcbd563d058d8b760d7e6d64c8fa4a1ae..8c6948119002f6508fd011d0726ce48f1e63aa26 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7680,6 +7680,29 @@ The edge type for [`ClusterAgentAuthorizationCiAccess`](#clusteragentauthorizati
 | <a id="clusteragentauthorizationciaccessedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
 | <a id="clusteragentauthorizationciaccessedgenode"></a>`node` | [`ClusterAgentAuthorizationCiAccess`](#clusteragentauthorizationciaccess) | The item at the end of the edge. |
 
+#### `ClusterAgentAuthorizationUserAccessConnection`
+
+The connection type for [`ClusterAgentAuthorizationUserAccess`](#clusteragentauthorizationuseraccess).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="clusteragentauthorizationuseraccessconnectionedges"></a>`edges` | [`[ClusterAgentAuthorizationUserAccessEdge]`](#clusteragentauthorizationuseraccessedge) | A list of edges. |
+| <a id="clusteragentauthorizationuseraccessconnectionnodes"></a>`nodes` | [`[ClusterAgentAuthorizationUserAccess]`](#clusteragentauthorizationuseraccess) | A list of nodes. |
+| <a id="clusteragentauthorizationuseraccessconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `ClusterAgentAuthorizationUserAccessEdge`
+
+The edge type for [`ClusterAgentAuthorizationUserAccess`](#clusteragentauthorizationuseraccess).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="clusteragentauthorizationuseraccessedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="clusteragentauthorizationuseraccessedgenode"></a>`node` | [`ClusterAgentAuthorizationUserAccess`](#clusteragentauthorizationuseraccess) | The item at the end of the edge. |
+
 #### `ClusterAgentConnection`
 
 The connection type for [`ClusterAgent`](#clusteragent).
@@ -12392,6 +12415,15 @@ GitLab CI/CD configuration template.
 | <a id="clusteragentauthorizationciaccessagent"></a>`agent` | [`ClusterAgent`](#clusteragent) | Authorized cluster agent. |
 | <a id="clusteragentauthorizationciaccessconfig"></a>`config` | [`JSON`](#json) | Configuration for the authorized project. |
 
+### `ClusterAgentAuthorizationUserAccess`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="clusteragentauthorizationuseraccessagent"></a>`agent` | [`ClusterAgent`](#clusteragent) | Authorized cluster agent. |
+| <a id="clusteragentauthorizationuseraccessconfig"></a>`config` | [`JSON`](#json) | Configuration for the authorized project. |
+
 ### `ClusterAgentToken`
 
 #### Fields
@@ -18692,6 +18724,7 @@ Represents a product analytics dashboard visualization.
 | <a id="projectterraformstates"></a>`terraformStates` | [`TerraformStateConnection`](#terraformstateconnection) | Terraform states associated with the project. (see [Connections](#connections)) |
 | <a id="projecttimelogcategories"></a>`timelogCategories` **{warning-solid}** | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | **Introduced** in 15.3. This feature is an Experiment. It can be changed or removed at any time. Timelog categories for the project. |
 | <a id="projecttopics"></a>`topics` | [`[String!]`](#string) | List of project topics. |
+| <a id="projectuseraccessauthorizedagents"></a>`userAccessAuthorizedAgents` | [`ClusterAgentAuthorizationUserAccessConnection`](#clusteragentauthorizationuseraccessconnection) | Authorized cluster agents for the project through user_access keyword. (see [Connections](#connections)) |
 | <a id="projectuserpermissions"></a>`userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. |
 | <a id="projectvisibility"></a>`visibility` | [`String`](#string) | Visibility of the project. |
 | <a id="projectvulnerabilityimages"></a>`vulnerabilityImages` | [`VulnerabilityContainerImageConnection`](#vulnerabilitycontainerimageconnection) | Container images reported on the project vulnerabilities. (see [Connections](#connections)) |
diff --git a/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb b/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb
index 84112502d80e00385fdbca570733912d606f012d..7e6897a723d70e173cfda028196f76eb678bfa03 100644
--- a/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb
+++ b/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb
@@ -22,12 +22,12 @@
     end
 
     context 'with project authorizations' do
-      let!(:authorization) do
+      let!(:authorization_1) do
         create(:agent_user_access_project_authorization, agent: agent, project: deployment_project)
       end
 
       it 'returns authorization' do
-        is_expected.to eq([authorization])
+        is_expected.to contain_exactly(authorization_1)
 
         expect(subject.first.access_level).to eq(Gitlab::Access::DEVELOPER)
       end
@@ -36,7 +36,7 @@
         let(:user) { deployment_maintainer }
 
         it 'returns authorization' do
-          is_expected.to eq([authorization])
+          is_expected.to contain_exactly(authorization_1)
 
           expect(subject.first.access_level).to eq(Gitlab::Access::MAINTAINER)
         end
@@ -52,27 +52,53 @@
 
       context 'with multiple authorizations' do
         let_it_be(:agent_2) { create(:cluster_agent, project: agent_configuration_project) }
+        let_it_be(:agent_3) { create(:cluster_agent, project: agent_configuration_project) }
+        let_it_be(:deployment_project_2) { create(:project, namespace: organization) }
 
         let_it_be(:authorization_2) do
           create(:agent_user_access_project_authorization, agent: agent_2, project: deployment_project)
         end
 
+        let_it_be(:authorization_3) do
+          create(:agent_user_access_project_authorization, agent: agent_3, project: deployment_project_2)
+        end
+
+        before_all do
+          deployment_project_2.add_developer(deployment_developer)
+        end
+
         it 'returns authorizations' do
-          is_expected.to contain_exactly(authorization, authorization_2)
+          is_expected.to contain_exactly(authorization_1, authorization_2, authorization_3)
         end
 
         context 'with specific agent' do
           let(:params) { { agent: agent_2 } }
 
           it 'returns authorization' do
-            is_expected.to eq([authorization_2])
+            is_expected.to contain_exactly(authorization_2)
+          end
+        end
+
+        context 'with specific project' do
+          let(:params) { { project: deployment_project_2 } }
+
+          it 'returns authorization' do
+            is_expected.to contain_exactly(authorization_3)
+          end
+        end
+
+        context 'with limit' do
+          let(:params) { { limit: 1 } }
+
+          it 'returns authorization' do
+            expect(subject.count).to eq(1)
           end
         end
       end
     end
 
     context 'with group authorizations' do
-      let!(:authorization) do
+      let!(:authorization_1) do
         create(:agent_user_access_group_authorization, agent: agent, group: organization)
       end
 
@@ -83,7 +109,7 @@
       end
 
       it 'returns authorization' do
-        is_expected.to eq([authorization])
+        is_expected.to contain_exactly(authorization_1)
 
         expect(subject.first.access_level).to eq(Gitlab::Access::DEVELOPER)
       end
@@ -92,7 +118,7 @@
         let(:user) { deployment_maintainer }
 
         it 'returns authorization' do
-          is_expected.to eq([authorization])
+          is_expected.to contain_exactly(authorization_1)
 
           expect(subject.first.access_level).to eq(Gitlab::Access::MAINTAINER)
         end
@@ -113,8 +139,10 @@
           create(:agent_user_access_group_authorization, agent: agent_2, group: organization)
         end
 
+        let_it_be(:authorization_3) { create(:agent_user_access_group_authorization) }
+
         it 'returns authorizations' do
-          is_expected.to contain_exactly(authorization, authorization_2)
+          is_expected.to contain_exactly(authorization_1, authorization_2)
         end
 
         context 'with specific agent' do
@@ -124,20 +152,46 @@
             is_expected.to eq([authorization_2])
           end
         end
+
+        context 'with specific project' do
+          let(:params) { { project: deployment_project } }
+
+          it 'returns authorization' do
+            is_expected.to contain_exactly(authorization_1, authorization_2)
+          end
+        end
+
+        context 'with limit' do
+          let(:params) { { limit: 1 } }
+
+          it 'returns authorization' do
+            expect(subject.count).to eq(1)
+          end
+        end
       end
 
       context 'when sub-group is authorized' do
-        let_it_be(:subgroup) { create(:group, parent: organization) }
+        let_it_be(:subgroup_1) { create(:group, parent: organization) }
+        let_it_be(:subgroup_2) { create(:group, parent: organization) }
+        let_it_be(:deployment_project_1) { create(:project, group: subgroup_1) }
+        let_it_be(:deployment_project_2) { create(:project, group: subgroup_2) }
 
-        let!(:authorization) do
-          create(:agent_user_access_group_authorization, agent: agent, group: subgroup)
-        end
+        let!(:authorization_1) { create(:agent_user_access_group_authorization, agent: agent, group: subgroup_1) }
+        let!(:authorization_2) { create(:agent_user_access_group_authorization, agent: agent, group: subgroup_2) }
 
         it 'returns authorization' do
-          is_expected.to eq([authorization])
+          is_expected.to contain_exactly(authorization_1, authorization_2)
 
           expect(subject.first.access_level).to eq(Gitlab::Access::DEVELOPER)
         end
+
+        context 'with specific deployment project' do
+          let(:params) { { project: deployment_project_1 } }
+
+          it 'returns only the authorization connected to the parent group' do
+            is_expected.to contain_exactly(authorization_1)
+          end
+        end
       end
     end
   end
diff --git a/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb b/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b7e2fef78eb935e1002aa0bb589e5d64412c7a00
--- /dev/null
+++ b/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Clusters::Agents::Authorizations::UserAccessResolver,
+  feature_category: :deployment_management do
+  include GraphqlHelpers
+
+  it { expect(described_class.type).to eq(Types::Clusters::Agents::Authorizations::UserAccessType) }
+  it { expect(described_class.null).to be_truthy }
+
+  describe '#resolve' do
+    let_it_be(:project) { create(:project) }
+    let_it_be(:user) { create(:user, maintainer_projects: [project]) }
+
+    let(:ctx) { { current_user: user } }
+
+    subject { resolve(described_class, obj: project, ctx: ctx) }
+
+    it 'calls the finder' do
+      expect_next_instance_of(::Clusters::Agents::Authorizations::UserAccess::Finder,
+        user, project: project) do |finder|
+        expect(finder).to receive(:execute)
+      end
+
+      subject
+    end
+  end
+end
diff --git a/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb b/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0e798cd1b18cd0103e3e41563a9f735c04575bd3
--- /dev/null
+++ b/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ClusterAgentAuthorizationUserAccess'],
+  feature_category: :deployment_management do
+  let(:fields) { %i[agent config] }
+
+  it { expect(described_class.graphql_name).to eq('ClusterAgentAuthorizationUserAccess') }
+  it { expect(described_class).to have_graphql_fields(fields) }
+end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 4cae970de647390bcdfe9a117f856833cd55d95b..5c2e67ca787b4e2fe56bd6f44890b49bdcf5bfd1 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -34,7 +34,7 @@
       issue_status_counts terraform_states alert_management_integrations
       container_repositories container_repositories_count
       pipeline_analytics squash_read_only sast_ci_configuration
-      cluster_agent cluster_agents agent_configurations ci_access_authorized_agents
+      cluster_agent cluster_agents agent_configurations ci_access_authorized_agents user_access_authorized_agents
       ci_template timelogs merge_commit_template squash_commit_template work_item_types
       recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables
       timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules languages
diff --git a/spec/models/clusters/agent_spec.rb b/spec/models/clusters/agent_spec.rb
index c2b8c5ea0d0c3033d563feb9c3514415fd56d4e1..10081b955f44ca6bc02e1f7a2d46e6c2cb9a092b 100644
--- a/spec/models/clusters/agent_spec.rb
+++ b/spec/models/clusters/agent_spec.rb
@@ -236,6 +236,77 @@
     end
   end
 
+  describe '#user_access_authorized_for?' do
+    using RSpec::Parameterized::TableSyntax
+
+    let_it_be(:organization) { create(:group) }
+    let_it_be(:agent_management_project) { create(:project, group: organization) }
+    let_it_be(:agent) { create(:cluster_agent, project: agent_management_project) }
+    let_it_be(:deployment_project) { create(:project, group: organization) }
+
+    let(:user) { create(:user) }
+
+    subject { agent.user_access_authorized_for?(user) }
+
+    it { is_expected.to eq(false) }
+
+    context 'with project-level authorization' do
+      let!(:authorization) { create(:agent_user_access_project_authorization, agent: agent, project: deployment_project) }
+
+      where(:user_role, :allowed) do
+        :guest       | false
+        :reporter    | false
+        :developer   | true
+        :maintainer  | true
+        :owner       | true
+      end
+
+      with_them do
+        before do
+          deployment_project.add_member(user, user_role)
+        end
+
+        it { is_expected.to eq(allowed) }
+      end
+
+      context 'when expose_authorized_cluster_agents feature flag is disabled' do
+        before do
+          stub_feature_flags(expose_authorized_cluster_agents: false)
+        end
+
+        it { is_expected.to eq(false) }
+      end
+    end
+
+    context 'with group-level authorization' do
+      let!(:authorization) { create(:agent_user_access_group_authorization, agent: agent, group: organization) }
+
+      where(:user_role, :allowed) do
+        :guest       | false
+        :reporter    | false
+        :developer   | true
+        :maintainer  | true
+        :owner       | true
+      end
+
+      with_them do
+        before do
+          organization.add_member(user, user_role)
+        end
+
+        it { is_expected.to eq(allowed) }
+      end
+
+      context 'when expose_authorized_cluster_agents feature flag is disabled' do
+        before do
+          stub_feature_flags(expose_authorized_cluster_agents: false)
+        end
+
+        it { is_expected.to eq(false) }
+      end
+    end
+  end
+
   describe '#user_access_config' do
     let_it_be(:group) { create(:group) }
     let_it_be(:project) { create(:project) }
diff --git a/spec/policies/clusters/agent_policy_spec.rb b/spec/policies/clusters/agent_policy_spec.rb
index 200cb8ae99bc7637380d226a3db9bd9faaf9612d..b3c43647a847156c1a5d9ec2f640f90807cfb50f 100644
--- a/spec/policies/clusters/agent_policy_spec.rb
+++ b/spec/policies/clusters/agent_policy_spec.rb
@@ -9,6 +9,8 @@
   let(:project) { cluster_agent.project }
 
   describe 'rules' do
+    it { expect(policy).to be_disallowed :read_cluster_agent }
+
     context 'when developer' do
       before do
         project.add_developer(user)
@@ -32,5 +34,13 @@
 
       it { expect(policy).to be_allowed :read_cluster_agent }
     end
+
+    context 'when agent is user_access authorized for project members' do
+      before do
+        allow(cluster_agent).to receive(:user_access_authorized_for?).with(user).and_return(true)
+      end
+
+      it { expect(policy).to be_allowed :read_cluster_agent }
+    end
   end
 end
diff --git a/spec/requests/api/graphql/project/user_access_authorized_agents_spec.rb b/spec/requests/api/graphql/project/user_access_authorized_agents_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b8017171fd1c4c9f1dabee464d0b38d31702c7e1
--- /dev/null
+++ b/spec/requests/api/graphql/project/user_access_authorized_agents_spec.rb
@@ -0,0 +1,129 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Project.user_access_authorized_agents', feature_category: :deployment_management do
+  include GraphqlHelpers
+
+  let_it_be(:organization) { create(:group) }
+  let_it_be(:agent_management_project) { create(:project, :private, group: organization) }
+  let_it_be(:agent) { create(:cluster_agent, project: agent_management_project) }
+
+  let_it_be(:deployment_project) { create(:project, :private, group: organization) }
+  let_it_be(:deployment_developer) { create(:user).tap { |u| deployment_project.add_developer(u) } }
+  let_it_be(:deployment_reporter) { create(:user).tap { |u| deployment_project.add_reporter(u) } }
+
+  let(:user) { deployment_developer }
+
+  let(:query) do
+    %(
+      query {
+        project(fullPath: "#{deployment_project.full_path}") {
+          userAccessAuthorizedAgents {
+            nodes {
+              agent {
+                id
+                name
+                project {
+                  name
+                }
+              }
+              config
+            }
+          }
+        }
+      }
+    )
+  end
+
+  subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
+
+  context 'with project authorization' do
+    let!(:user_access) { create(:agent_user_access_project_authorization, agent: agent, project: deployment_project) }
+
+    it 'returns the authorized agent' do
+      authorized_agents = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes')
+
+      expect(authorized_agents.count).to eq(1)
+
+      authorized_agent = authorized_agents.first
+
+      expect(authorized_agent['agent']['id']).to eq(agent.to_global_id.to_s)
+      expect(authorized_agent['agent']['name']).to eq(agent.name)
+      expect(authorized_agent['config']).to eq({})
+      expect(authorized_agent['agent']['project']).to be_nil # User is not authorized to read other resources.
+    end
+
+    context 'when user is developer in the agent management project' do
+      before do
+        agent_management_project.add_developer(deployment_developer)
+      end
+
+      it 'returns the project information as well' do
+        authorized_agent = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes').first
+
+        expect(authorized_agent['agent']['project']['name']).to eq(agent_management_project.name)
+      end
+    end
+
+    context 'when user is reporter' do
+      let(:user) { deployment_reporter }
+
+      it 'returns nothing' do
+        expect(subject['data']['project']['userAccessAuthorizedAgents']).to be_nil
+      end
+    end
+  end
+
+  context 'with group authorization' do
+    let_it_be(:deployment_group) { create(:group, :private, parent: organization) }
+
+    let!(:user_access) { create(:agent_user_access_group_authorization, agent: agent, group: deployment_group) }
+
+    before_all do
+      deployment_group.add_developer(deployment_developer)
+      deployment_group.add_reporter(deployment_reporter)
+    end
+
+    it 'returns the authorized agent' do
+      authorized_agents = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes')
+
+      expect(authorized_agents.count).to eq(1)
+
+      authorized_agent = authorized_agents.first
+
+      expect(authorized_agent['agent']['id']).to eq(agent.to_global_id.to_s)
+      expect(authorized_agent['agent']['name']).to eq(agent.name)
+      expect(authorized_agent['config']).to eq({})
+      expect(authorized_agent['agent']['project']).to be_nil # User is not authorized to read other resources.
+    end
+
+    context 'when user is developer in the agent management project' do
+      before do
+        agent_management_project.add_developer(deployment_developer)
+      end
+
+      it 'returns the project information as well' do
+        authorized_agent = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes').first
+
+        expect(authorized_agent['agent']['project']['name']).to eq(agent_management_project.name)
+      end
+    end
+
+    context 'when user is reporter' do
+      let(:user) { deployment_reporter }
+
+      it 'returns nothing' do
+        expect(subject['data']['project']['userAccessAuthorizedAgents']).to be_nil
+      end
+    end
+  end
+
+  context 'when deployment project is not authorized to user_access to the agent' do
+    it 'returns empty' do
+      authorized_agents = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes')
+
+      expect(authorized_agents).to be_empty
+    end
+  end
+end