Session ID from scheduled jobs

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.

Answer

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.

Attribution
Source : Link , Question Author : hamayoun , Answer Author : Alan Morey

Leave a Comment