How to get Session ID for Salesforce managed packages

Oleksandr Putria
Noltic
Published in
8 min readSep 29, 2023

--

Photo by Markus Spiske on Unsplash

Salesforce Session ID is a crucial piece of authentication and security information used in the Salesforce platform. It is a unique identifier generated by Salesforce when a user logs in and represents an active user session. The Session ID is used to authenticate and authorize a user’s access to Salesforce resources, such as data, APIs, and user interfaces.

Developers often use Session ID when making API calls to Salesforce. The Session ID parameter is included in the HTTP headers of API requests as a form of authentication. This allows external applications and services to interact with Salesforce on behalf of a user without requiring them to log in each time.

Salesforce offers a security review process for applications developed on the platform. This review helps ensure that your application complies with Salesforce’s security policies and best practices of data safety.

During the security review, Salesforce evaluates how your application handles sensitive data, including Session IDs, and checks for vulnerabilities and security flaws.

Today I am going to invite you to go with me through the way I went to get Session ID for our company’s managed packages. To see the pitfalls we’ve met and what we found out.

The approaches to obtaining Session ID

When you’re looking to integrate with a web service or utilize Apex-MDapi, acquiring a Session ID is a key step, as it serves as your Bearer token for authentication purposes. Getting a Session ID can be accomplished through various methods, readily available with a quick online search.

The simplest one is

UserInfo.getSessionId();

When executing it within an anonymous window, the initial challenge you’ll encounter is the inability to view it in the debug log. In most cases, you’ll likely come across an output similar to the following:

|USER_DEBUG|[14]|DEBUG|SESSION_ID_REMOVED

There are a few workarounds to obtain the Session ID value:

Create a new Case record and save the Session ID in the description field. You can then retrieve it from the standard Salesforce user interface.

Case c = new Case(Description = UserInfo.getSessionId());

Execute this code snippet, retrieve the result, and then remove any whitespace.

UserInfo.getOrganizationId().substring(0, 15) + ‘ ‘ + UserInfo.getSessionId().substring(15)

To proceed with making a request, the next step involves retrieving the organization URL in an external format. You can quickly obtain it using the following code snippet:

URL.getOrgDomainUrl().toExternalForm();

With the Session ID and Organization URL in hand, you can leverage tools like Postman along with Salesforce Collections or simply use the Postman Salesforce Workspace to interact with the API and validate the functionality of your Session ID, also known as the token.

In the screenshot below, you’ll find an example illustrating the retrieval of Organization Limits.

Now, everything appears to be in order, and it seems like we can utilize it for our solutions. But there’s always a ‘but’. What if we attempt to use it with our Lightning Web Component? So let’s create a straightforward LWC component with a button and its corresponding Apex controller.

public with sharing class SessionIdController {

@AuraEnabled
public static String getLimits() {
String endpoint = Url.getOrgDomainUrl().toExternalForm() + '/services/data/v58.0/limits';
String bearer = 'Bearer ' + UserInfo.getSessionId();

HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('GET');
req.setHeader('Authorization', bearer);

Http http = new Http();
HttpResponse res = http.send(req);
return res.getBody();
}
}

When we attempt to do this from the LWC component, an error will occur, and the response will resemble what’s shown in the screenshot below:

Executing the same controller’s method in an anonymous window returns the expected response:

Visualforce page approach

In Salesforce, you don’t need to explicitly obtain the Session ID when working with Visualforce pages because the Session ID is automatically available within the context of a Visualforce page. Salesforce handles the authentication and session management for you.

<! — SessionIdPage →
<apex:page contentType=”application/json”>
{“sessionId”:”{!$Api.Session_ID}”}
</apex:page>

In the example above, the {$Api.Session_ID} expression is used to display the Session ID on the Visualforce page. Salesforce automatically provides this value, and you can access it directly.

So we can use this method to retrieve the Session ID from a Visualforce page. The most prevalent approach to acquire the Session ID securely is by creating a Visualforce page and incorporating a global merge field to extract it from the page.

public class SessionIdService {

public class SessionWrapper {
public string sessionId;
}
@AuraEnabled
public static string getSessionId() {
SessionWrapper wrapper = (SessionWrapper) JSON.deserialize(
Page.SessionIdPage.getContent().toString(),
SessionWrapper.class
);
return wrapper.sessionId;
}
}

Since this approach works as intended, many developers continue to use it successfully. However, when it comes to incorporating it within a managed package, several considerations and challenges arise.

Session ID, Managed Packages and Security Review

In Salesforce, they really value the trust of their customers. If you’re an independent software vendor (ISV) and want to share your managed package with the public, it has to go through a security review to make sure it’s safe.

We had some doubts about using the Visualforce approach in managed packages. It seemed more like a workaround than an solid solution. So, we set up a meeting with Salesforce Support to talk about our needs and figure out how to do it the right way, following all the security rules.

At Noltic, while creating our managed packages like CheckMyNumber and Tracky, we had some peculiar situations. We needed to connect our web services to give users a better experience. To do this, we used something called Apex-Mdapi as a part of our solution.

Canvas Connected Application

Canvas enables you to integrate a third-party application in Salesforce easily. Canvas is a set of tools and JavaScript APIs that you can use to expose an application as a canvas app. This means you can take your new or existing applications and make them available to your users as part of their Salesforce experience.

When you use a signed request for authentication in your canvas app, you receive a CanvasRequest object in the initial POST message from Salesforce. This object contains a lot of information about the Client and Context. The Client object has an oauth token so all other aspects we can handle from the service side.

There are some steps in order to create a Canvas Connected Application:

  • Callback URL is a required field, as we don’t use it and can’t leave it empty we can put in some dummy URL
  • In the Canvas Setting section, we need to specify our web service URL and select Access Method: Signed Request (POST)

We’ve achieved successful integration using Canvas for our CheckMyNumber application.

Connected Application for Apex-Mdapi

In our products, we had specific processes to implement, and we chose Apex-Mdapi for this purpose. Following Salesforce’s guidance, we explored authentication options for connected apps and selected OAuth 2.0 JWT Bearer Flow for Server-to-Server due to its lack of user interaction. The central idea is to utilize a connected app as a token provider, include it in the managed package, and use it in subscriber organizations.

The first step is generating a certificate

# Generate an RSA private key
openssl genrsa -des3 -passout pass:x -out server.pass.key 2048

# Create a key file from the server.pass.key file
openssl rsa -passin pass:x -in server.pass.key -out server.key

# Request and generate the certificate
openssl req -new -key server.key -out server.csr

# Generate the SSL certificate
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

Afterwards, we need to create a connected app

Then do these steps:

  • Go to Setup > Connected app > New.
  • Fill in Name and Email
  • Click on ‘Enable OAuth Setting’
  • Call back URL ‘https://login.salesforce.com’
  • Click on ‘Use Digital Signature’ and upload the certificate created.
  • Select the following OAuth Scopes — Manage user data via APIs (api), Perform requests at any time (refresh_token, offline_access)
  • Click on ‘Save’

JWT Flow Using Apex

We’ve set up the connected app, and we can finally use Apex code to obtain the access token.

There is some data that you need in order to perform the request.

Headers:

{“alg”:”RS256"}

Payload: Data

{
“iss” : “Connected App Consumer Key”,
“sub”: “CurrentUserName”,
“aud”: “https://login.salesforce.com" or “https://test.salesforce.com",
“exp”: now + 5minutes
}

Then your request should look like this:

Method: POST

URL: https://login.salesforce.com/services/oauth2/token or https://test.salesforce.com/services/oauth2/token (scratch org or sandbox)

Headers: ‘Content-type’, ‘application/x-www-form-urlencoded’

Body:

  • grand_type : urn:ietf:params:oauth:grant-type
  • assertion : JWT TOKEN

With the code below we can generate a JWT and make a call to acquire a Salesforce access token. Don’t forget to add login.salesforce.com and test.salesforce.com to your remote site settings as we’ll be using them as endpoints.

public String base64UrlEncode(Blob input){
String output = EncodingUtil.base64Encode(input);
output = output.replace(‘+’, ‘-’);
output = output.replace(‘/’, ‘_’);
while ( output.endsWith(‘=’)){
output = output.substring(0,output.length()-1);
}
return output;
}

String getJWT(
String iss,
String sub,
String aud,
String privateKey)
{
String encodedHeader = base64UrlEncode(
Blob.valueOf(
JSON.serialize(new Map<String, String> {‘alg’ => ‘RS256’})
)
);

String encodedBody = base64UrlEncode(
Blob.valueOf(
JSON.serialize(new Map<String, Object> {
'iss' => iss,
'sub' => sub,
'aud' => aud,
'exp' => Datetime.now().getTime()/1000 + 300
})
)
);

String jwt = encodedHeader + '.' + encodedBody;
Blob signature = Crypto.sign(
'rsa-sha256',
Blob.valueOf(jwt),
EncodingUtil.base64Decode(privateKey)
);

jwt += '.' + base64UrlEncode(signature);
return jwt;
}

There are only a couple of steps left to do. Or rather, there are only manual steps left that our subscribers need to do after the installation of our 1GP manage packages:

Set up Permitted users to ‘Admin approved users are pre-authorized’

Add profiles that are supposed to use your connected app

In conclusion, the Salesforce Session ID is a vital component for authentication and security within the Salesforce platform. It serves as a unique identifier representing an active user session and is essential for accessing Salesforce resources, making API calls, and interacting with the platform on behalf of users.

Obtaining the Session ID can be achieved through various methods. However, when working with managed packages and security considerations, developers should carefully evaluate their approach.

It’s essential to follow Salesforce’s security guidelines and consider the specific needs of your application when choosing the most appropriate method for obtaining and utilizing the Session ID.

This is Noltic’s approach for 1gp-managed packages and Session ID, I really hope it helps others working with Salesforce.

Read our other Medium posts and follow Noltic on LinkedIn, Facebook, and Twitter to stay updated about our news.

--

--

Oleksandr Putria
Noltic

☁️ 6x Certified Salesforce Engineer developing managed packages and Experience Cloud products