[SalesForce] Connect to Salesforce production REST API from Sandbox using Named Credentials

Background

I would like to connect to the REST API of a production org, from a sandbox org.

Configuration so far:

  1. Production Org has a Connected App
  2. Sandbox Org has a Auth. Provider
  3. Sandbox Org has a Named Credential
  4. Apex code in the Sandbox org uses the Named Credential to access the Production Org REST API

1. Production Org – Connected App

  • Connected App Name: Sandbox
  • Enable OAuth Settings is ticked
  • Callback URL: https://x--x.cs83.my.salesforce.com/services/authcallback/Salesforce_REST_API which is the sandbox
  • Selected OAuth Scopes: full refresh_token
  • Require Secret for Web Server Flow is ticked.
  • Noted the Key and Secret for use later in the sandbox Auth. Provider

2. Sandbox Org – Auth. Provider

  • Provider Type: Salesforce
  • Name: Salesforce Production REST API
  • URL Suffix: Salesforce_REST_API
  • Consumer Key set to the key from the Production Org Connected App
  • Consumer Secret set to the Secret from the Production Org Connected App
  • Authorize Endpoint URL used the default value https://test.salesforce.com/services/oauth2/authorize
  • Token Endpoint URL uses the default value https://test.salesforce.com/services/oauth2/token

3. Sandbox Org – Named Credential

  • Label: Salesforce Production REST API
  • Name: Salesforce_Production_REST_API
  • URL: https://xx.my.salesforce.com which is the production org url
  • Identity Type: Named Principle
  • Authentication Protocol: OAuth 2.0
  • Authentication Provider: Salesforce Production REST API from step 2
  • Scope: full refresh_token
  • Start Authentication Flow on Save is ticked
  • Generate Authorization Header is ticked
  • Allow Merge Fields in HTTP Header is ticked

Saving starts the OAuth process, and I successfully authenticate with my sandbox login details.

4. Apex code in the Sandbox org uses the Named Credential

The below code is called from a Lightning component which is running inside a publicly available community page.

public with sharing class ReportApi {

    private final static String REPORTS_RESOURCE = '/services/data/v44.0/analytics/reports/';

    private static HttpResponse get(String reportID) {

        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.GET);
        request.setEndpoint('callout:Salesforce_Production_REST_API' + REPORTS_RESOURCE + reportID);
        request.setTimeout(120000); // 2 Minutes
        request.setHeader('Accept', 'application/json');    
        request.setHeader('Content-Type',  'application/json'); 
        request.setHeader('Authorization','OAuth {!$Credential.OAuthToken}');

        HttpResponse httpResponse = new Http().send(request);

        return httpResponse;
    }
}

Usage

HttpResponse response = ReportApi.get(reportId);

Yet I get this response from the production REST API:

{"message":"Session expired or invalid","errorCode":"INVALID_SESSION_ID"}

When I debug the HttpRequest I get this:

System.HttpRequest[Endpoint=callout:Salesforce_Production_REST_API/services/data/v44.0/analytics/reports/00O0O00000AYR0JUAX, Method=GET]

When I debug the HttpResponse I get this:

System.HttpResponse[Status=Unauthorized, StatusCode=401]

Question

  1. What is wrong with the above configuration?
  2. How should I configure the two orgs, to allow the Sandbox Org to access the Production Org's REST API?

Best Answer

What is wrong with the above configuration?

The Sandbox Org Auth. Provider should have been configured to use:

  • Authorize Endpoint URL: https://login.salesforce.com/services/oauth2/authorize
  • Token Endpoint URL: https://login.salesforce.com/services/oauth2/token

Which are the URLs for production login, allowing me to authenticate against the Production Org when using OAuth