diff --git a/app/graphql/resolvers/organizations/projects_resolver.rb b/app/graphql/resolvers/organizations/projects_resolver.rb
index b053801d31e676c89e02dd964b3f51295f948956..a4a930acd3886c0bebc4361da7d055a7b5b5b87f 100644
--- a/app/graphql/resolvers/organizations/projects_resolver.rb
+++ b/app/graphql/resolvers/organizations/projects_resolver.rb
@@ -19,3 +19,5 @@ def finder_params(args)
     end
   end
 end
+
+Resolvers::Organizations::ProjectsResolver.prepend_mod
diff --git a/app/models/project.rb b/app/models/project.rb
index d7719e72904a84af5770259529cc22255155a466..b966ef8d246db3e94e81af60f23d41ad0c91be9e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -646,6 +646,7 @@ def self.integration_association_name(name)
   scope :without_deleted, -> { where(pending_delete: false) }
   scope :not_hidden, -> { where(hidden: false) }
   scope :not_in_groups, ->(groups) { where.not(group: groups) }
+  scope :by_not_in_root_id, ->(root_id) { joins(:project_namespace).where('namespaces.traversal_ids[1] NOT IN (?)', root_id) }
   scope :not_aimed_for_deletion, -> { where(marked_for_deletion_at: nil).without_deleted }
 
   scope :with_storage_feature, ->(feature) do
diff --git a/ee/app/finders/ee/projects_finder.rb b/ee/app/finders/ee/projects_finder.rb
index 847cae6134cd894e9072f4c3196a9d32f118bbad..d2ce601b5f94d2e9f677730bcdd54f43cbf2d7f1 100644
--- a/ee/app/finders/ee/projects_finder.rb
+++ b/ee/app/finders/ee/projects_finder.rb
@@ -11,6 +11,7 @@ module EE
   #     feature_available: string[]
   #     aimed_for_deletion: Symbol
   #     include_hidden: boolean
+  #     filter_expired_saml_session_projects: boolean
   module ProjectsFinder
     extend ::Gitlab::Utils::Override
 
@@ -23,9 +24,22 @@ def filter_projects(collection)
       collection = by_feature_available(collection)
       collection = by_hidden(collection)
       collection = by_marked_for_deletion_on(collection)
+      collection = by_saml_sso_session(collection)
       by_aimed_for_deletion(collection)
     end
 
+    def by_saml_sso_session(projects)
+      return projects unless filter_expired_saml_session_projects?
+
+      projects.by_not_in_root_id(current_user.expired_sso_session_saml_providers.select(:group_id))
+    end
+
+    def filter_expired_saml_session_projects?
+      return false if current_user.nil? || current_user.can_read_all_resources?
+
+      params.fetch(:filter_expired_saml_session_projects, false)
+    end
+
     def by_plans(collection)
       if names = params[:plans].presence
         collection.for_plan_name(names)
diff --git a/ee/app/graphql/ee/resolvers/organizations/projects_resolver.rb b/ee/app/graphql/ee/resolvers/organizations/projects_resolver.rb
new file mode 100644
index 0000000000000000000000000000000000000000..046055e5f1dfe2fe076d25bc19d95245c026d891
--- /dev/null
+++ b/ee/app/graphql/ee/resolvers/organizations/projects_resolver.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module EE
+  module Resolvers
+    module Organizations
+      module ProjectsResolver
+        extend ::Gitlab::Utils::Override
+
+        override :finder_params
+        def finder_params(args)
+          super.merge(filter_expired_saml_session_projects: true)
+        end
+      end
+    end
+  end
+end
diff --git a/ee/spec/finders/ee/projects_finder_spec.rb b/ee/spec/finders/ee/projects_finder_spec.rb
index 5a9e67907086b50fc286111677ec5525893efdc3..1836a4e76bf38636f1c4ecf7d3582638a6a62c64 100644
--- a/ee/spec/finders/ee/projects_finder_spec.rb
+++ b/ee/spec/finders/ee/projects_finder_spec.rb
@@ -65,6 +65,78 @@
       it { is_expected.to contain_exactly(ultimate_project, ultimate_project2, premium_project, no_plan_project) }
     end
 
+    context 'filter by SAML SSO session' do
+      let(:params) { { filter_expired_saml_session_projects: true } }
+      let(:finder) { described_class.new(current_user: current_user, params: params) }
+
+      let_it_be(:current_user) { user }
+
+      let_it_be(:root_group1) do
+        create(:group, saml_provider: create(:saml_provider), developers: current_user) do |group|
+          create_saml_identity(group, current_user)
+        end
+      end
+
+      let_it_be(:root_group2) do
+        create(:group, saml_provider: create(:saml_provider))
+      end
+
+      let_it_be(:private_root_group) do
+        create(:group, :private, saml_provider: create(:saml_provider), developers: current_user) do |group|
+          create_saml_identity(group, current_user)
+        end
+      end
+
+      let_it_be(:project1) { create(:project, :public, group: root_group1) }
+      let_it_be(:project2) { create(:project, :public, group: root_group2) }
+      let_it_be(:private_project) { create(:project, :private, group: private_root_group) }
+      let_it_be(:all_projects) { [project1, project2, private_project] }
+
+      subject(:projects) { finder.execute.id_in(all_projects).to_a }
+
+      context 'when the current user is nil' do
+        let_it_be(:current_user) { nil }
+
+        it 'includes public SAML projects' do
+          expect(projects).to contain_exactly(project1, project2)
+        end
+      end
+
+      shared_examples 'includes all SAML projects' do
+        specify do
+          expect(projects).to match_array(all_projects)
+        end
+      end
+
+      context 'when the current user is an admin', :enable_admin_mode do
+        let_it_be(:current_user) { create(:admin) }
+
+        it_behaves_like 'includes all SAML projects'
+      end
+
+      context 'when the current user has no active SAML sessions' do
+        it 'filters out the SAML member projects' do
+          expect(projects).to contain_exactly(project2)
+        end
+      end
+
+      context 'when filter_expired_saml_session_projects param is false' do
+        let(:params) { { filter_expired_saml_session_projects: false } }
+
+        it_behaves_like 'includes all SAML projects'
+      end
+
+      context 'when the current user has active SAML sessions' do
+        before do
+          active_saml_sessions = { root_group1.saml_provider.id => Time.current,
+                                   private_root_group.saml_provider.id => Time.current }
+          allow(::Gitlab::Auth::GroupSaml::SsoState).to receive(:active_saml_sessions).and_return(active_saml_sessions)
+        end
+
+        it_behaves_like 'includes all SAML projects'
+      end
+    end
+
     context 'filter by hidden' do
       let_it_be(:hidden_project) { create(:project, :public, :hidden) }
 
@@ -119,6 +191,10 @@
 
     private
 
+    def create_saml_identity(group, current_user)
+      create(:group_saml_identity, saml_provider: group.saml_provider, user: current_user)
+    end
+
     def create_project(plan, visibility = :public)
       create(:project, visibility, namespace: create(:group_with_plan, plan: plan))
     end
diff --git a/ee/spec/requests/ee/api/graphql/organizations/organization_query_spec.rb b/ee/spec/requests/ee/api/graphql/organizations/organization_query_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5fda0885b4ae56dda9687ac77d938ce82d4b4af7
--- /dev/null
+++ b/ee/spec/requests/ee/api/graphql/organizations/organization_query_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting organization information', feature_category: :cell do
+  include ::GraphqlHelpers
+
+  let(:query) { graphql_query_for(:organization, { id: organization.to_global_id }, organization_fields) }
+  let(:current_user) { user }
+
+  let_it_be(:organization_owner) { create(:organization_owner) }
+  let_it_be(:organization) { organization_owner.organization }
+  let_it_be(:user) { organization_owner.user }
+  let_it_be(:project) { create(:project, organization: organization, developers: user) }
+
+  let_it_be(:saml_group) do
+    saml_provider = create(:saml_provider)
+    create(:group, organization: organization, saml_provider: saml_provider, developers: user) do |group|
+      create(:group_saml_identity, saml_provider: group.saml_provider, user: user)
+    end
+  end
+
+  let_it_be(:saml_project) { create(:project, group: saml_group, organization: organization, developers: user) }
+
+  subject(:request_organization) { post_graphql(query, current_user: current_user) }
+
+  context 'when requesting projects' do
+    let(:projects) { graphql_data_at(:organization, :projects, :nodes) }
+    let(:organization_fields) do
+      <<~FIELDS
+        projects(first: 1) {
+          nodes {
+            id
+          }
+        }
+      FIELDS
+    end
+
+    context 'when current user has an active SAML session' do
+      before do
+        active_saml_sessions = { saml_group.saml_provider.id => Time.current }
+        allow(::Gitlab::Auth::GroupSaml::SsoState).to receive(:active_saml_sessions).and_return(active_saml_sessions)
+      end
+
+      it 'includes SAML project' do
+        request_organization
+
+        expect(projects).to match_array(a_graphql_entity_for(saml_project))
+      end
+    end
+
+    context 'when current user has no active SAML session' do
+      it 'excludes SAML project' do
+        request_organization
+
+        expect(projects).to match_array(a_graphql_entity_for(project))
+      end
+    end
+  end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index ad207444b6bc071095a138fce8be90cafd5d0c2e..a12dc3b02d0f8fb79edb4bed828a398c954aa78e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2265,6 +2265,19 @@ def has_external_wiki
     end
   end
 
+  describe '.by_not_in_root_id' do
+    let_it_be(:group1) { create(:group) }
+    let_it_be(:group2) { create(:group) }
+    let_it_be(:group1_project) { create(:project, namespace: group1) }
+    let_it_be(:group2_project) { create(:project, namespace: group2) }
+    let_it_be(:subgroup_project) { create(:project, namespace: create(:group, parent: group1)) }
+
+    it 'returns correct namespaces' do
+      expect(described_class.by_not_in_root_id(group1.id)).to contain_exactly(group2_project)
+      expect(described_class.by_not_in_root_id(group2.id)).to contain_exactly(group1_project, subgroup_project)
+    end
+  end
+
   describe '.order_by_storage_size' do
     let_it_be(:project_1) { create(:project_statistics, repository_size: 1).project }
     let_it_be(:project_2) { create(:project_statistics, repository_size: 3).project }