1

I am working with a managed package that is installed in Org A. The user wants to be able to access information from their other org, Org B, where this package is not installed. The plan is to use web server oauth flow to grant access to the managed package and return the user to their originating org.

As a proof of concept I'm doing the following:

  1. Log into Org A
  2. Manually navigate to https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=[MyClientId]&redirect_uri=http%3A%2F%2Fna1.salesforce.com%2Fapex%2FExternalOAuthHandler
  3. Log in to Org B and approve access to the connected app.
  4. Grab the "code" parameter off of the query string and execute a POST request to get the access token and instance url for Org B.

Here's some sample code for what i'm posting for step 4 (demo only - no way would you put the client_id and secret here):

$.post("https://login.salesforce.com/services/oauth2/token", {
    data: {
        "grant_type": "authorization_code",
        "code": "[code from first request]",
        "client_id": "[MyClientId]",
        "secret": "[MySecret]",
        "redirect_uri", "https://na1.salesforce.com/apex/ExternalOAuthHandler"
    }
}).done(function(response) {
    console.log(response);
});

There are 2 issues I'm running into:

  1. The callback url that I configure for my connected app will be in an unpredictable domain for each customer that wants to use this feature. I won't know their instance and they could also have it installed in an org with My Domain enabled. The callback url can't contain wildcards either. If I use na1 for the domain, i.e. http://na1.salesforce.com/apex/ExternalOAuthHandler, then it will look for that page in Org B where it doesn't exist. So using na1 is definitely not an option. If I just pass whatever the originating host is and append "/apex/ExternalOAuthHandler", I receive a callback mismatch error because it will be unpredictable and won't match the configured callback url. How can I resolve this?
  2. Even though I don't redirect to the correct URI with the na1 url, I do still get the code on the query string and a page not found message in OrgB. I can still grab that code and perform step 4 manually. However, I receive the following response:

{ "error": "invalid_client", "error_description": "invalid client credentials" }

Why would I get invalid client credentials here? Am I going about this the right way and missing something?

The tutorial I followed is here: https://www.youtube.com/watch?v=vlrK3YZ_Fj0. However the difference with my scenario is that I'm getting this to work with a managed package installed in someone's org where the domain of the redirect URL is unpredictable.

  • For non-SF oAuth callback I set redirect URL to login.salesforce.com/apex/packagename__pagename. Problem is that in your case, both environments are salesforce, so it may happen then during authentication you get redirected to the same org, where you just got authorised. Similar problem is here salesforce.stackexchange.com/questions/104458/… – kurunve Jul 26 '17 at 19:44
2

I just went through something similar where the final destination varied depending on the customer so a static callback int he code did not work.

What I did on our LMA org to handle this was:

  1. Placed the connected app within the LMA org
  2. Added a VF page and code to handle the callback on a force.com site
  3. Added the URL of the callback on the connected app for the sites page

Then when doing the oAuth process I use the state parameter to define the final URL to return the user to with the encrypted session information. This final URL would be a VF page within your package or something else that you control as you have to pass the session information.

public class state{
    public boolean isSandbox;
    public string uri;

    public state(Boolean isSandbox, String URI){
        this.isSandbox = isSandbox; //Allows for test or login in my implementation. May or may not be needed for you
        this.URI = URI; //You will need to URL encode this
    }
}

I encrypt the JSON serialized auth response and put it in a parameter named auth the is returned to the final endpoint

public static String encryptString(String toEncryptString) {
    Blob encryptedData = Crypto.encryptWithManagedIV('AES128', EncodingUtil.base64Decode(keyHash), EncodingUtil.base64Decode(EncodingUtil.base64Encode(Blob.valueOf(toEncryptString))));
    return EncodingUtil.urlEncode(EncodingUtil.base64Encode(encryptedData), 'UTF-8');
}

and the auth response class:

public class auth_response {

    @AuraEnabled public string refresh_token;
    @AuraEnabled public string access_token;
    @AuraEnabled public string instance_url;
    @AuraEnabled public string error_description;
    @AuraEnabled public string id;
    @AuraEnabled public string scope;
    @AuraEnabled public string token_type;

}

Your implementation will vary but the key is to use a known Callback and pass JSON'd State parameters that define the final endpoint. Then encrypt, url encode, etc the auth parameters and pass them to that final endpoint as a parameter.

The complete implementation is a bit much to post here. Maybe I will get around to writing a simple self contained example at some point.

Note Using heroku as a callback for the connected app may be prudent if you expect a lot of API calls.

  • Hey @Eric, thanks for the detailed response. I'm close to marking this as the answer.. I'm doing something very similar. I wrote a .net core service that is living in azure to handle this at the moment. However, I keep getting invalid_client_credentials when i request the access token with my authorization code. did you ever run into that – Donuts Jul 31 '17 at 15:32
  • Yes, and there could be many reasons. Check endpoint, your oAuth process, etc. It is best to get the basic oAuth process working then expand the logic as required. Makes it easier to troubleshoot when you have the basics working. – Eric Jul 31 '17 at 15:35
  • solved my issue with the invalid client credentials. had a typo in the token request. I'm now redirected to the URL of origin. HOWEVER, one final problem. I'm logged in on Org A, I call my service which redirects me to the SF login to approve the connected app. I'm still logged in to Org A with that request. so I click "not you" to log out of Org A and log in to Org B. If I do that and approve the client, i'm redirected back to the originating url (in Org A), but now i am no longer signed in. @Eric did you run into something like that as well? – Donuts Jul 31 '17 at 20:46
  • technically my original question is answered, and i'll mark this as solved. – Donuts Jul 31 '17 at 20:48
  • @Donuts - Yes. Since we are doing dev on the same browser we typicalyl run into these issues. End users will typically not run into them. to test the process use a broswer that you do not typically use and ensure all sessions have expired or clear the cache. That will give you a real example of what the user experiences so you can tell if you need to modify anything – Eric Jul 31 '17 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.