diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index c1c0d3449179ecf51319368800fa8be3093a05ed..0a93e71858e1dc9e42c671b28407355e09536e28 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -74,43 +74,27 @@ module API
 
       private
 
-      def find_user_from_access_token
-        return unless access_token
-
-        validate_access_token!
-
-        access_token.user || raise(UnauthorizedError)
-      end
-
-      # Check the Rails session for valid authentication details
-      def find_user_from_warden
-        warden.try(:authenticate) if verified_request?
-      end
-
-      def warden
-        env['warden']
-      end
-
-      # Check if the request is GET/HEAD, or if CSRF token is valid.
-      def verified_request?
-        Gitlab::RequestForgeryProtection.verified?(env)
-      end
-
-      def find_oauth_access_token
-        token = Doorkeeper::OAuth::Token.from_request(doorkeeper_request, *Doorkeeper.configuration.access_token_methods)
-        return unless token
-
-        # Expiration, revocation and scopes are verified in `find_user_by_access_token`
-        access_token = OauthAccessToken.by_token(token)
-        raise UnauthorizedError unless access_token
-
-        access_token.revoke_previous_refresh_token!
-        access_token
+      def raise_unauthorized_error!
+        raise UnauthorizedError
       end
 
-      def find_personal_access_token
-        token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
-        return unless token.present?
+      # If token is presented and valid, then it sets @current_user.
+      #
+      # If the token does not have sufficient scopes to cover the requred scopes,
+      # then it raises InsufficientScopeError.
+      #
+      # If the token is expired, then it raises ExpiredError.
+      #
+      # If the token is revoked, then it raises RevokedError.
+      #
+      # If the token is not found (nil), then it returns nil
+      #
+      # Arguments:
+      #
+      #   scopes: (optional) scopes required for this guard.
+      #           Defaults to empty array.
+      def find_user_by_access_token(access_token)
+        scopes = scopes_registered_for_endpoint
 
         # Expiration, revocation and scopes are verified in `find_user_by_access_token`
         access_token = PersonalAccessToken.find_by(token: token)
@@ -119,10 +103,6 @@ module API
         access_token
       end
 
-      def doorkeeper_request
-        @doorkeeper_request ||= ActionDispatch::Request.new(env)
-      end
-
       # An array of scopes that were registered (using `allow_access_with_scope`)
       # for the current endpoint class. It also returns scopes registered on
       # `API::API`, since these are meant to apply to all API routes.
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index 999104f91f57bf5cf31041ca470f8df68243a0ed..123a39dea751a0fa253804dedd1780e20469f1c6 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -3,6 +3,10 @@
 module Gitlab
   module Auth
     class RequestAuthenticator
+      include UserAuthFinders
+
+      attr_reader :request
+
       def initialize(request)
         @request = ensure_action_dispatch_request(request)
       end
@@ -14,49 +18,6 @@ module Gitlab
       def find_sessionless_user
         find_user_by_private_token || find_user_by_rss_token || find_user_by_oauth_token
       end
-
-      private
-
-      def find_session_user
-        @request.env['warden']&.authenticate if verified_request?
-      end
-
-      def find_user_by_private_token
-        token = @request.params[:private_token].presence || @request.headers['PRIVATE-TOKEN'].presence
-        return unless token.present?
-
-        User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
-      end
-
-      def find_user_by_rss_token
-        return unless @request.path.ends_with?('atom') || @request.format == 'atom'
-
-        token = @request.params[:rss_token].presence
-        return unless token.present?
-
-        User.find_by_rss_token(token)
-      end
-
-      def find_user_by_oauth_token
-        access_token = find_oauth_access_token
-        access_token&.user
-      end
-
-      def find_oauth_access_token
-        token = Doorkeeper::OAuth::Token.from_request(@request, *Doorkeeper.configuration.access_token_methods)
-        OauthAccessToken.by_token(token) if token
-      end
-
-      # Check if the request is GET/HEAD, or if CSRF token is valid.
-      def verified_request?
-        Gitlab::RequestForgeryProtection.verified?(@request.env)
-      end
-
-      def ensure_action_dispatch_request(request)
-        return request if request.is_a?(ActionDispatch::Request)
-
-        ActionDispatch::Request.new(request.env)
-      end
     end
   end
 end
diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2f4aeff14ac83b2ee54408ec18287892672291d2
--- /dev/null
+++ b/lib/gitlab/auth/user_auth_finders.rb
@@ -0,0 +1,92 @@
+module Gitlab
+  module Auth
+    module UserAuthFinders
+      # Check the Rails session for valid authentication details
+      def find_session_user
+        request.env['warden']&.authenticate if verified_request?
+      end
+
+      def find_user_by_private_token
+        token = private_token
+        return unless token.present?
+
+        user =
+          find_user_by_authentication_token(token) ||
+          find_user_by_personal_access_token(token)
+
+        raise_unauthorized_error! unless user
+
+        user
+      end
+
+      def private_token
+        request.params[:private_token].presence ||
+          request.headers['PRIVATE-TOKEN'].presence
+      end
+
+      def find_user_by_authentication_token(token_string)
+        User.find_by_authentication_token(token_string)
+      end
+
+      def find_user_by_personal_access_token(token_string)
+        access_token = PersonalAccessToken.find_by_token(token_string)
+        return unless access_token
+
+        find_user_by_access_token(access_token)
+      end
+
+      def find_user_by_rss_token
+        return unless request.path.ends_with?('atom') || request.format.atom?
+
+        token = request.params[:rss_token].presence
+        return unless token.present?
+
+        user = User.find_by_rss_token(token)
+        raise_unauthorized_error! unless user
+
+        user
+      end
+
+      def find_user_by_oauth_token
+        access_token = find_oauth_access_token
+
+        return unless access_token
+
+        find_user_by_access_token(access_token)
+      end
+
+      def find_oauth_access_token
+        return @oauth_access_token if defined?(@oauth_access_token)
+
+        current_request = ensure_action_dispatch_request(request)
+        token = Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods)
+        return @oauth_access_token = nil unless token
+
+        @oauth_access_token = OauthAccessToken.by_token(token)
+        raise_unauthorized_error! unless @oauth_access_token
+
+        @oauth_access_token.revoke_previous_refresh_token!
+        @oauth_access_token
+      end
+
+      def find_user_by_access_token(access_token)
+        access_token&.user
+      end
+
+      # Check if the request is GET/HEAD, or if CSRF token is valid.
+      def verified_request?
+        Gitlab::RequestForgeryProtection.verified?(request.env)
+      end
+
+      def ensure_action_dispatch_request(request)
+        return request if request.is_a?(ActionDispatch::Request)
+
+        ActionDispatch::Request.new(request.env)
+      end
+
+      def raise_unauthorized_error!
+        return nil
+      end
+    end
+  end
+end
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 6c0996c543d38ea287ea890768cf65fc961eee99..fc1444e40185b9990cda4e633f21d7495a0e9f51 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -23,6 +23,7 @@ describe API::Helpers do
     }
   end
   let(:header) { }
+  let(:request) { Grape::Request.new(env)}
 
   before do
     allow_any_instance_of(self.class).to receive(:options).and_return({})