diff --git a/changelogs/unreleased/22465-rack-attack-authenticate-runner-requests-with-job-token-basic-auth.yml b/changelogs/unreleased/22465-rack-attack-authenticate-runner-requests-with-job-token-basic-auth.yml
new file mode 100644
index 0000000000000000000000000000000000000000..06f618bd29be0400eccf79270c1d7a6070c791e3
--- /dev/null
+++ b/changelogs/unreleased/22465-rack-attack-authenticate-runner-requests-with-job-token-basic-auth.yml
@@ -0,0 +1,5 @@
+---
+title: Authenticate requests with job token as basic auth header for request limiting
+merge_request: 21562
+author:
+type: fixed
diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb
index 6210aca739a66753dfc31af43c341163b6e81722..33cbb070c2f28370e144c030d9dda116db1b0547 100644
--- a/lib/gitlab/auth/auth_finders.rb
+++ b/lib/gitlab/auth/auth_finders.rb
@@ -21,6 +21,7 @@ module AuthFinders
       prepend_if_ee('::EE::Gitlab::Auth::AuthFinders') # rubocop: disable Cop/InjectEnterpriseEditionModule
 
       include Gitlab::Utils::StrongMemoize
+      include ActionController::HttpAuthentication::Basic
 
       PRIVATE_TOKEN_HEADER = 'HTTP_PRIVATE_TOKEN'
       PRIVATE_TOKEN_PARAM = :private_token
@@ -67,6 +68,19 @@ def find_user_from_job_token
         job.user
       end
 
+      def find_user_from_basic_auth_job
+        return unless has_basic_credentials?(current_request)
+
+        login, password = user_name_and_password(current_request)
+        return unless login.present? && password.present?
+        return unless ::Ci::Build::CI_REGISTRY_USER == login
+
+        job = ::Ci::Build.find_by_token(password)
+        raise UnauthorizedError unless job
+
+        job.user
+      end
+
       # We only allow Private Access Tokens with `api` scope to be used by web
       # requests on RSS feeds or ICS files for backwards compatibility.
       # It is also used by GraphQL/API requests.
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index 9b1b7b8e879f9be82be110ada5f96f7c102df12e..34ccff588f402f0037ddac023e9adf9c8bc37f67 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -32,7 +32,8 @@ def runner
       def find_sessionless_user(request_format)
         find_user_from_web_access_token(request_format) ||
           find_user_from_feed_token(request_format) ||
-          find_user_from_static_object_token(request_format)
+          find_user_from_static_object_token(request_format) ||
+          find_user_from_basic_auth_job
       rescue Gitlab::Auth::AuthenticationError
         nil
       end
diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb
index 3d10f4113102b54e459f03298b242d0df5b23a74..82ff8e7f76cd4b96a576c1877983aae932020c28 100644
--- a/spec/lib/gitlab/auth/auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/auth_finders_spec.rb
@@ -335,6 +335,72 @@ def set_param(key, value)
     end
   end
 
+  describe '#find_user_from_basic_auth_job' do
+    def basic_http_auth(username, password)
+      ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
+    end
+
+    def set_auth(username, password)
+      env['HTTP_AUTHORIZATION'] = basic_http_auth(username, password)
+    end
+
+    subject { find_user_from_basic_auth_job }
+
+    context 'when the request does not have AUTHORIZATION header' do
+      it { is_expected.to be_nil }
+    end
+
+    context 'with wrong credentials' do
+      it 'returns nil without user and password' do
+        set_auth(nil, nil)
+
+        is_expected.to be_nil
+      end
+
+      it 'returns nil without password' do
+        set_auth('some-user', nil)
+
+        is_expected.to be_nil
+      end
+
+      it 'returns nil without user' do
+        set_auth(nil, 'password')
+
+        is_expected.to be_nil
+      end
+
+      it 'returns nil without CI username' do
+        set_auth('user', 'password')
+
+        is_expected.to be_nil
+      end
+    end
+
+    context 'with CI username' do
+      let(:username) { ::Ci::Build::CI_REGISTRY_USER }
+      let(:user) { create(:user) }
+      let(:build) { create(:ci_build, user: user) }
+
+      it 'returns nil without password' do
+        set_auth(username, nil)
+
+        is_expected.to be_nil
+      end
+
+      it 'returns user with valid token' do
+        set_auth(username, build.token)
+
+        is_expected.to eq user
+      end
+
+      it 'raises error with invalid token' do
+        set_auth(username, 'token')
+
+        expect { subject }.to raise_error(Gitlab::Auth::UnauthorizedError)
+      end
+    end
+  end
+
   describe '#validate_access_token!' do
     let(:personal_access_token) { create(:personal_access_token, user: user) }