The only documented difference that I can find between the @RemoteAction Annotation and Non-Annotated apex methods is this section from the Apex Developer Guide (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_RemoteAction.htm):

RemoteAction Annotation

The RemoteAction annotation provides support for Apex methods used in Visualforce to be called via JavaScript. This process is often referred to as JavaScript remoting.

However, consider the following controller:

public without sharing class CPQController  {
    public CPQController(){}

    public static List<SBQQ__Quote__c> getQuotesRemote(){
        List<SBQQ__Quote__c> quotes = [SELECT Id,Name FROM SBQQ__Quote__c];
        return quotes;

    public List<SBQQ__Quote__c> getQuotes2(){
        List<SBQQ__Quote__c> quotes = [SELECT Id,Name FROM SBQQ__Quote__c];
        return quotes;

And the following Visualforce:

<apex:page sidebar="false" showHeader="false" controller="CPQController">
    <title>REST Test</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <body>Loading...<br />

    <div Id="main">
        <div Id="QuotesRemote">
            <button onclick="getQuotesRemote();">Get Quotes Remote</button><br />
            <h1>Quotes Remote:</h1><br />
            <apex:repeat value="{!quotesremote}" var="qr">
                QR: {!qr.Id}<br />

        <div Id="Quotes">
            <h1>Quotes:</h1><br />
            <apex:repeat value="{!quotes2}" var="q">
                Q: {!q}<br />

    <script type="text/javascript">
    function getQuotesRemote() {
            function(result, event){
                if (event.status) {
                    for(var i = 0;i < result.length;i++){
                        document.getElementById('QuotesRemote').innerHTML += 'QR2: ' + result[i].Id + '<br />';
            {escape: true}

Scenario 1: Visualforce Page Accessed by User with Managed Package License


  1. The Quotes div is Populated with the Quote Ids from <apex:repeat />,
  2. Pressing the "Get Quotes Remote" button appends the Quote Ids to the QuotesRemote div.

Licensed User Screenshot

Scenario 2: Visualforce Page Accessed by User without a Managed Package License


  1. The Quotes div is populated with a bunch of blank rows that start with Q:. Presumably because the SOQL is returning empty record(s), but the list itself is still iterated (not empty)? (see Screenshot below).
  2. Pressing the "Get Quotes Remote" button still appends the Quote Ids to the QuotesRemote div, contradicting the behavior of getQuotes2(), which runs the same SOQL query.

enter image description here

Is there a documented reason for this difference? I don't mind the behavior, but I also don't want to use Javascript Remoting to work with records if this undocumented behavior is subject to change.

1 Answer 1


The part you're missing is that the object/field security is not enforced in Apex, but in Visualforce. The RemoteAction method bypasses the Visualforce layer, and so returns/exposes all the values, while the Visualforce runtime is enforcing field security. You should use either the WITH SECURITY_ENFORCED SOQL keyword, or use stripInaccessible to restrict access to the data. In addition, you should use with sharing or inherited sharing keywords to restrict access to records the user should not be able to see.

This is true for other types of resources, too, including @AuraEnabled, @RestResource, and so on. It is the developer's responsibility to use the appropriate sharing keyword and language features to ensure that data is not leaked.

  • Thanks fox! I know that best practice is to do inherit/with sharing, this was just test code to understand the options/restrictions around accessing Managed Package data without a managed package license. Now that you mention it, it makes complete sense that the Visualforce automated FLS wouldn't apply to Javascript Remoting since the resulting object is being translated into a plain Javascript Object. Thanks! Apr 22, 2020 at 22:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .