15

Here's the scenario. I have a remote application which is invoked from an HTTP callout. In this callout, I am passing the session ID. The application can then do its stuff without needing a username and password. However, I want to be able to invoke the application from a scheduled job, and getsessionid() returns null in this case. Is there any way around this? I am trying very hard to avoid storing sfdc usernames and passwords on my remote server, and I want to avoid the hassle of using oAuth with all the complications of client secrets, etc.

  • 1
    If you are trying to schedule a job from within an Apex Class you can pass the session Id in the Constructor as an argument – logontokartik Mar 6 '13 at 2:18
  • 1
    logontokartik, do you mean soemthing like this: proschedule p = new proschedule(UserInfo.getSessionID()); String sch = '0 0 8 13 2 ?'; system.schedule('One Time Pro', sch, p); Wouldn't the session ID expire by the time the scheduled job ran? – hamayoun Mar 6 '13 at 4:15
  • Re passing in the session id, see this post salesforce.stackexchange.com/questions/11341/… – Phil Hawthorn Jun 5 '13 at 21:53
12

You will need to establish a session from within the Scheduled job.

As a proof of concept I managed to get this working by using the OAuth 2.0 Username-Password Flow. You make a HTTP POST request from Salesforce-to-Salesforce to request an Access Token which can then be used as a Session ID.

You will need to create a new Remote Access Application where you will get Consumer Key (client_id) and Consumer Secret (client_secret). You will also need to create a new Remote Site Setting for the Token Endpoint URL so a POST request can be sent to it.

public class UsernamePasswordFlow {
    String username;
    String password;
    String clientId;
    String clientSecret;
    String tokenEndpoint = 'https://test.salesforce.com/services/oauth2/token';

    public UsernamePasswordFlow(String username, String password, String clientId, String clientSecret) {
        this.username = username;
        this.password = password;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }

    public String requestAccessToken() {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(tokenEndpoint);
        req.setMethod('POST');
        req.setBody(buildHttpQuery(new Map<String, String> {
            'grant_type' => 'password',
            'username' => username,
            'password' => password,
            'client_id' => clientId,
            'client_secret' => clientSecret
        }));

        Http http = new Http();
        HttpResponse resp = http.send(req);

        Map<String, Object> m =
            (Map<String, Object>) JSON.deserializeUntyped(resp.getBody());

        return (String) m.get('access_token');
    }

    static String buildHttpQuery(Map<String, String> queryParams) {
        if (queryParams.isEmpty()) {
            return '';
        }

        String[] params = new String[] {};
        for (String k : queryParams.keySet()) {
            String v = EncodingUtil.urlEncode(queryParams.get(k), 'UTF-8');

            params.add(String.format('{0}={1}', new String[] { k, v }));
        }

        return String.join(params, '&');
    }
}

Example Usage:

UsernamePasswordFlow upf = new UsernamePasswordFlow(
    'my.name@example.com',
    'mypassword',
    '3MdPGzpc3Iwg5lcwtc7HAprnDCVG9JNJ1CQBISiBA9bE1WXe_xlItLNdlxckCVyIo',
    '436267497522435674970'
);
String sessionId = upf.requestAccessToken();

Unfortunately the drawback to this approach is having the store the credentials, in my case I used a custom settings object to store them. So there are security concerns to take into consideration.

  • Alan, do you have a test class for this? It uses a web callout, which makes it more complicated to test. – hamayoun Feb 12 '14 at 23:27
  • @hamayoun, as this was only a quick hack I didn't write any tests. I would recommend reading Testing HTTP Callouts – Alan Morey Feb 12 '14 at 23:37
  • 1
    Is there any way to secure the credentials in this solution? All I can think of right now is to obfuscate it a little bit by encoding it in base64, and then storing that value in some odd-named custom setting, so that it at least doesn't glare at people.. Gotta be a better way. – Kirill Yunussov Aug 21 '14 at 23:43
6

Here's my suggestion:

Use Outbound Messaging, which contains the capability to pass the Session ID for the purpose of you using it to call back.

Set up a workflow rule on a custom object to fire when a record is changed and send the outbound message.

Use your scheduled job to modify the custom object, thus firing the workflow rule and sending the outbound message and session id.

  • You would need something in Salesforce to receive the Outbound message, which probably means a Public Site. – Daniel Ballinger Apr 21 '17 at 2:36
1

For scheduled jobs this is a bit complicated, but for Queueables and Futures, you could post your current Session ID along to the Queueable and Future and then use it right there.

For Scheduled Jobs, you could store at any moment your current SessionId in e.g. a Custom Setting and then retrieve it from there when the Scheduled Job is run. You should however ensure that the SessionID is fresh enough so it is not expired when the Scheduled Job is run.

Another option (if you only need specific limits) would be to scrape the 'company profile page' like this

PageReference pr = new PageReference('/00D20000000HsCQ'); // use company profile page
pr.setRedirect(false);
String result = pr.getContent().toString();
Integer start_index = result.indexOf('API Requests, Last 24 Hours', 1) + 52;
Integer end_index = result.indexOf('<', start_index);
result = result.substring(start_index, end_index);
result = result.replaceAll('&nbsp;', ' ');
return result;

(found here https://stackoverflow.com/questions/9221170/retrieve-salesforce-daily-api-requests-limit)

If you need all the limits, all the time, then as suggested, doing a fresh login call is probably your most reliable option.

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.