[SalesForce] Get a FIRST-CLASS SessionID for API Calls (looking for a clean way or alternative)

I am basically facing the same challenge as @Evan here

Different Session Ids in Different contexts

I'm also running into SessionID related issues, but in a completely different use case. Background is the creation of an free and 100% native IDE for force.com development http://www.elastify.eu/apps/CodeFusion running as an managed package. Goal is to get something more convenient and faster than eclipse or Developer Console.

Now I'm implementing Execute Anonymous. Using a SoapAPI callout which works – but is seriously crippled if not invoked with a "First Class Citizen SessionID". The crucial thing is to get proper Debug Logs. Therefore Headers must be set. The Log comes back as in the response. Look at this:

public static string execute(string ApexCode) {
    Http h = new Http(); HttpRequest req = new HttpRequest();
    req.setEndpoint( URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/s/'+'30.0');
    req.setMethod('POST');
    req.setHeader('Content-Type', 'text/xml'); 
    req.setHeader('SOAPAction', '""');
    string body = ''
        +'<?xml version="1.0" encoding="UTF-8"?>'
        +'<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
          +'<env:Header>'
            +'<DebuggingHeader xmlns="http://soap.sforce.com/2006/08/apex">'
              +'<categories>'+'<level>'+'WARN'+'</level>'+'</categories>' // you can add more cats if needed
              +'<debugLevel>DEBUGONLY</debugLevel>'
            +'</DebuggingHeader>'
            +'<SessionHeader xmlns="http://soap.sforce.com/2006/08/apex">'
              +'<sessionId>'+UserInfo.getSessionId()+'</sessionId>' // The SessionID - different in vairous contexts...
            +'</SessionHeader>'
          +'</env:Header>'
          +'<env:Body>'
            +'<executeAnonymous xmlns="http://soap.sforce.com/2006/08/apex">'
              +'<String>'+ApexCode+'</String>'
            +'</executeAnonymous>'
          +'</env:Body>'
        +'</env:Envelope>'        
    ;
    req.setBody(body);
    HttpResponse res = h.send(req);
    return res.getBody();
}    

This works perfectly if put into a class which is directly created on an Org. However if you put it into a Managed Package and deploy it to a different Orgs, you will get a hard time.

I found this sources of SessionIds

  • {!$Api.Session_ID} called from an ApexPage inside an installed package ==> Non-FirstClass
  • {!$Api.Session_ID} called from an ApexPage directly on the org ==> Non-FirstClass
  • UserInfo.getSessionId() called from an ApexClass inside an installed package ==> Non-FirstClass
  • UserInfo.getSessionId() called from an ApexClass directly on the org ==> FIRST-CLASS
  • {!$Api.Session_ID} in a custom-link or button on native page-layout ==> FIRST-CLASS
  • getCookie('sid') invoked in different contexts ==> INVALID

Unfortunately I found only very hackish ways to retrieve a First-Class Ids ending up in either peek it via an iframe or complex re-routes and passes via an URL-parameter. Both is ugly, probably insecure and a hell to maintain. Now I have packaged the execute anonymous into a separate extension package to get the nasty code outside (having security review in mind).

In just using a First-Class SessionId I can't see a security issue, since you can do all the very bad things without. It feels a bit overprotective by salesforce to give us a so hard time to retrieve it. Of course the process of retrieving it should be clean.

So I'm wondering, if one of you might have also encountered and probably solved this challenge to get a working First-Class SessionId cleanly into a managed package?

Best Answer

Following @eric's recommendation, I was able to use login() which indeed seems to return a full powered first-class sessionId - at least for my requirement I finally got the logs!

Instead of using the WSDL I found a much simpler way to use the AJAX Toolkit just with

sforce.connection.login(username,passwordAndToken);

In the end it will do the same callout as @eric would get via the WSDL, I guess. More details and possible pitfalls you can find here: Call Login() to get a SessionID via sforce.connection.login()

Hope this information will help others.

Related Topic