S-URL and Public Share Setup (S-Drive 2.7.12 and later)
This page explains how to set up and use S-URL which works by using Salesforce communities (“Digital Experience” sites) and allows use of Public Share links where users can open, download, upload or delete files, as well as edit file meta-data.
To use Legacy S-URL (not recommended) see Legacy S-URL: How to Activate Public Share Link & S-URL.
File links that were created using Legacy S-URL will continue to work. However links created once this S-URL configuration is set up will not work if you switch back to Legacy S-URL.
This page explains how to set up S-Drive so that short url links (S-URL or surl) can be used. S-URL is needed in order to create public share files to share files with users outside of Salesforce. S-URL can also be used to open and download files internally in Salesforce if desired.
S-URL uses a Salesforce Digital Experience Site (formerly known as Salesforce Community) to access the file. It requires a dedicated user that will use passwordless login to the community.
To use this feature, you will need to do the following:
Create a self-signed certificate to use for the Connected App
Give Community User Profile access to the Connected App and PasswordlessLogin Class and Page
Give the Community User access to the necessary objects through sharing rules
We will go through the steps below.
STEP 1: Create a Digital Experience (Community)
If you do not already have Digital Experiences enabled
Go to Setup > Feature Settings > Digital Experiences > Settings
Check Enable Digital Experiences checkbox.
Choose a domain name. (This will be used for all your Digital Experience sites, not just S-Drive or S-URL. It can be something like your company name)
Click Save.
Click OK on the prompt.
You will be redirected in another tab into Setup > Feature Settings> Digital Experiences > All Sites with a success message “Success! You can now create new experiences.”.
Click New.
Select Build Your Own. (There are 2 Build Your Own options. Do NOT select (LWR) one.)
Click Get Started.
Give it a name such as SURL.
For the URL, set it as surl.
Click Create.
Wait for the community to be created.
We will now create a community user and profile and will return to the community later to complete the setup.
STEP 2: Create a Community User
This user will be used solely for accessing files that use S-URL.
You can either use an internal user or a Community User. This user will need View All permissions for the objects you want to share files for. If you use a community user, View All permission is not allowed, so you will need to set up sharing rules instead, which is done in STEP 9.
If you use a community user, we recommend a user with a Customer Community Plus license or some other license that allows the use of sharing rules. (For example, a Customer Community license can only use sharing sets, not sharing rules.)
Choose either Option 1 (Community User License) or Option 2 (Salesforce License) to set up your user.
Option 1: Community User option:
A community user is set up from a Contact on an Account. You may wish to create a simple dedicated Account just for this contact/user.
To create your user and profile using a customer community plus license and profile:
Create the profile
Go to Setup-->profiles
Clone the Customer Community Plus profile. You may want to use this profile only for S-URL since it will have access to a Custom App created for S-URL, as well as an Apex class and Visualforce page
Give the following profile permissions
Parent objects (ex: Account) where you store files you want to share: Read
File object (ex: Account Files) of files you want to share: Create/Read/Edit/Delete as needed
Custom file objects with lookup relationship (such as a custom Account File object that has a lookup with Account): Read/Create/Edit/Delete as desired
SURL object: Create/Read/Edit/Delete
Make sure there is Field Level access to the fields on the objects you’re allowing.
Create an Account. Note: the Account owner must have a role.
Create a Contact on this account that you’ll use for community access
On the contact, click Enable Customer User (if this button is not there, go to page layout of Contact, go to Mobile and Lightning Actions and add it to the page.)
Give the user a license (Customer Community Plus or another “Plus” license is needed) and the profile you just created.
Click Save
Option 2: Internal user option:
Create the profile
Go to Setup-->profiles
Clone any profile, such as standard user profile
Give the following profile permissions
Parent objects (ex: Account) where there is a master-detail relationship with files you want to share (ex: Accounts for Account Files): View All (user will not see the Accounts because you will only be sharing files, but they need access to the account in order to access the Account Files because of the master-detail relationship.)
File object (ex: Account Files) of files you want to share: Read/Create/Edit/Delete as desired
Custom file objects with lookup relationship (such as a custom Account File object that has a lookup with Account): Read/Create/Edit/Delete as desired
SURL object: Create/Read/Edit/Delete or Modify All
If using Preview & Thumbnail: Preview object: Read/Create/Edit/Delete
If using File Activities: File Activity object: Read/Create
Click Save
Create a user
Assign the profile you just created to this user
STEP 3: Create a Self-Signed Certificate
Create a Self-Signed Certificate to use with the passwordless login method.
Go to Setup > Security > Certificate and Key Management.
Click on Create Self-Signed Certificate.
Name its label and the unique name as “SURL” (Case sensitive).
Make sure the Exportable Private Key is checked.
Click Save.
Click on the certificate you just created.
Click Download Certificate.
STEP 4: Create a Connected App
Create a Connected App to use with the passwordless login method.
Go to Setup > Apps > App Manager.
Click on New Connected App.
Enter Connected App Name and Contact Email.
Check the box for Enable OAuth Settings
Enter Callback URL as http://localhost:8080/auth2/callback
Check Use digital signatures and upload the self-signed certificate you downloaded.
Choose the OAuth Scopes
Click Save.
Click Continue.
Click on Click to reveal next to the Consumer Secret field.
Copy both Consumer Key and Consumer Secret as you will use them in the passwordless login system.
Click Manage at the top of the Connected App
Click Edit Policies
Under Permitted Users, change it to “Admin approved users are pre-authorized”
Click Save
STEP 5: Create Passwordless Login Page and Controller
Create a Visualforce page and its controller class to allow the user to access shared items by logging in automatically by a passwordless login system.
Set up Custom Settings needed for the controller class
Go to Setup-->Custom Settings
Find SDriveConfig and click Manage
Click New
Enter Name: SUrl_Community_Username / Value: the username of the delegated Salesforce user
Click Save & New
Enter Name: SUrl_Community_ClientId / Value: the consumer key from the connected app
Click Save & New
Enter Name: SUrl_Community_BaseUrl / Value: the base URL of the community (go to setup>digital experiences>all sites to see the url you created in STEP 1.
Click Save & New
Enter Name: SUrl_Community_Certificate / Value: the unique name of the self-signed certificate
Click Save
Go to Setup-->Visualforce pages
Click Developer Console
Click File->New->Apex Class
Create the
PasswordlessLoginController
class as shown below:global with sharing class PasswordlessLoginController { cg__SDriveConfig__c sdrivesetting_SUrl_Community_Username = cg__SDriveConfig__c.getValues('SUrl_Community_Username'); public String username = sdrivesetting_SUrl_Community_Username.cg__Value__c; cg__SDriveConfig__c sdrivesetting_SUrl_Community_ClientId = cg__SDriveConfig__c.getValues('SUrl_Community_ClientId'); public String clientId = sdrivesetting_SUrl_Community_ClientId.cg__Value__c; cg__SDriveConfig__c sdrivesetting_SUrl_Community_BaseUrl = cg__SDriveConfig__c.getValues('SUrl_Community_BaseUrl'); public String communityBaseUrl = sdrivesetting_SUrl_Community_BaseUrl.cg__Value__c; cg__SDriveConfig__c sdrivesetting_SUrl_Community_Certificate = cg__SDriveConfig__c.getValues('SUrl_Community_Certificate'); public String certificate = sdrivesetting_SUrl_Community_Certificate.cg__Value__c; public PageReference JWTlogin() { String url = ApexPages.currentPage().getHeaders().get('referer')==null?'':ApexPages.currentPage().getHeaders().get('referer'); String surlId = url==''?'':url.substring(url.lastIndexOf('sid=') + 4); system.debug('surlId: ' + surlId); String token = getAccessToken(); system.debug('token: ' + token); PageReference PR = New PageReference(communityBaseUrl + '/secur/frontdoor.jsp?sid=' + token + '&retURL=' + communityBaseUrl + '/s/?sid=' + surlId); if(Test.isRunningTest()){ PR = NULL; } return PR; } public String getAccessToken() { String tokenEndpoint = communityBaseUrl + '/services/oauth2/token'; Auth.JWT jwt = new Auth.JWT(); jwt.setIss(clientId); jwt.setSub(username); jwt.setAud(communityBaseUrl); Auth.JWS jws = new Auth.JWS(jwt, certificate); Auth.JWTBearerTokenExchange bearer = new Auth.JWTBearerTokenExchange(tokenEndpoint, jws); String accessToken=''; if(!Test.isRunningTest()){ accessToken = bearer.getAccessToken(); } return accessToken; } }
Click File->Save
Click New->Visualforce Page
Create the
PasswordlessLogin
VFP as below:<apex:page controller="PasswordlessLoginController" showHeader="false" cache="false" standardStylesheets="false" action="{!JWTlogin}"> </apex:page>
Create the following test class to use when needed for migration to production
@isTest public class PasswordlessLoginController_Test { @testSetup static void makeData() { cg__SDriveConfig__c sdrivesetting_SUrl_Community_Username = new cg__SDriveConfig__c(); sdrivesetting_SUrl_Community_Username.Name = 'SUrl_Community_Username'; sdrivesetting_SUrl_Community_Username.cg__Value__c = 'communitysurl@surl.com'; insert sdrivesetting_SUrl_Community_Username; cg__SDriveConfig__c sdrivesetting_SUrl_Community_ClientId = new cg__SDriveConfig__c(); sdrivesetting_SUrl_Community_ClientId.Name = 'SUrl_Community_ClientId'; sdrivesetting_SUrl_Community_ClientId.cg__Value__c = 'ABCDEFGHIJ'; insert sdrivesetting_SUrl_Community_ClientId; cg__SDriveConfig__c sdrivesetting_SUrl_Community_BaseUrl = new cg__SDriveConfig__c(); sdrivesetting_SUrl_Community_BaseUrl.Name = 'SUrl_Community_BaseUrl'; sdrivesetting_SUrl_Community_BaseUrl.cg__Value__c = 'https://test.cs99.force.com/surl'; insert sdrivesetting_SUrl_Community_BaseUrl; cg__SDriveConfig__c sdrivesetting_SUrl_Community_Certificate = new cg__SDriveConfig__c(); sdrivesetting_SUrl_Community_Certificate.Name = 'SUrl_Community_Certificate'; sdrivesetting_SUrl_Community_Certificate.cg__Value__c = 'MySurl'; insert sdrivesetting_SUrl_Community_Certificate; } @isTest static void passwordlessLoginTest() { if(Test.isRunningTest()){ PasswordlessLoginController controller = new PasswordlessLoginController(); String accessToken = controller.getAccessToken(); System.assertEquals('', accessToken); PageReference pageReference = controller.JWTlogin(); System.assertEquals(null, pageReference); PasswordlessLoginController controller2 = new PasswordlessLoginController(); controller2.username = 'invalid@example.com'; String accessToken2 = controller2.getAccessToken(); System.assertEquals('', accessToken2); PageReference pageReference2 = controller2.JWTlogin(); System.assertEquals(null, pageReference2); PasswordlessLoginController controller3 = new PasswordlessLoginController(); controller3.clientId = 'invalidclientid'; String accessToken3 = controller3.getAccessToken(); System.assertEquals('', accessToken3); PageReference pageReference3 = controller3.JWTlogin(); System.assertEquals(null, pageReference3); PasswordlessLoginController controller4 = new PasswordlessLoginController(); controller4.communityBaseUrl = 'invalidcommunityBaseUrl'; String accessToken4 = controller4.getAccessToken(); System.assertEquals('', accessToken4); PageReference pageReference4 = controller4.JWTlogin(); System.assertEquals(null, pageReference4); PasswordlessLoginController controller5 = new PasswordlessLoginController(); controller5.certificate = 'surl'; String accessToken5 = controller5.getAccessToken(); System.assertEquals('', accessToken5); PageReference pageReference5 = controller5.JWTlogin(); System.assertEquals(null, pageReference5); } } }
Click File->Save
STEP 6: Finish setting up your Community
Access your community by going to Setup->Digital Experiences->All Sites and click Builder
Go to Settings-->General and click on the Community Guest User profile
Give this profile access to the PasswordlessLogin Visualforce page. (This is needed even though we will not be using the community Guest User.)
Go back to your community and go to Workspaces
Click on Administration.
Go to the Members tab.
Add the profile of the User you set up in STEP 2 to the Selected Profiles section and click Save. NOTE: If using a community user profile, make sure to change the dropdown for “Search” to Customer instead of Internal.
Click Save
Go to Login & Registration tab.
Under Login Page Setup, Select Login Page Type as Visualforce Page and select the
PasswordlessLogin
VFP.Click Save.
Click on the Administration on the top and click on Builder.
Click the Trash icon on the “Let’s get started!” section to remove it.
When prompted click Delete.
Click on the Components (lightning icon) section.
Drag a Visualforce Page component on the Content section.
Select SUrlRedirect as the Visualforce Page.
Set its height to 1000.
Keep the Record ID as {!recordId}.
You should either see the S-URL page load as a preview (with an error message)
Click on the Paintbrush to the left and the arrow in the new pop and click on Edit CSS
Click on User Overrides in the popup box
Add the following CSS code.
Click Save
Click on Settings (gear icon) > Security & Privacy
Make the Security Level as Relaxed CSP: Permit Access to Inline Scripts and Allowed Hosts.
Click Publish
Click Publish on the prompt.
Go to Administration.
Click Activate.
STEP 7: Add Trusted Sites and Remote Site Settings to Salesforce
Go to Setup > CSP Trusted Sites.
Click on New Trusted Site and set up the site with a URL that is formatted as https://<bucketname>.s3.<region>.amazonaws.com where <bucketname> is your S3 bucket name and <region> is your bucket regions, such as us-east-1.
NOTE: If you’re not sure what your bucketname and region are, you can go to Setup-->Custom Settings-->SDriveConfig. Click “Manage” and click on AmazonRegion and BucketName to see the values.On the Content Security Policy Trusted Site page, make sure Allow site for frame-src and Active boxes are checked. The box below this one may be checked by default, so make sure the correct box
Click Save & New
Add a Trusted Site URL formatted as https://<bucketname>.s3.amazonaws.com
Make sure Allow site for frame-src and Active boxes are checked
Click Save & New
Add a Trusted Site URL as https://zip.cyangate.com.
Make sure Allow site for frame-src and Active boxes are checked
Click Save.
Go to Setup > Remote Site Settings and add your Community URL (e.g. https://sandbox-surl-development-developer-edition.cs87.force.com).
Click Save and New
Add https://zip.cyangate.com as a URL in your setting.
Click on Save.
STEP 8: Give Community User Profile access to the Connected App and PasswordlessLogin Class and Page
Go to Setup-->Profiles and edit the community user profile you created in STEP 2
Give the profile the following access
Under Connected Apps: give access to the connected app you created in STEP 4.
Under Apex Classes: give access to PasswordlessLoginController
Under Visualforce Pages: give access to PasswordlessLogin
STEP 9: Give the Community User access to the necessary objects through sharing rules
The community user needs access to objects in addition to the profile permissions given in STEP 2. If you used an internal user with View All permission, you can skip this step.
Access is given through Sharing Settings. Go to Setup->Sharing Settings.
For the SUrl object and each object you want to use for S-URL you can choose either option below.
Note: If using Preview & Thumbnail or File Activities, include sharing rules for Preview and File Activities.
Option 1: Make OWD public for external users (if using a Community license) for that object (or for the parent if the file object is a master-detail relationship, for example for Account if using S-URL for Account Files)
Option 2: Create a sharing rule to share all records of the object. You can use something as shown below, where the surl public group contains only the user you set up in STEP 2. Or you can share with Portal Roles and select the role of your user. Use criteria that is always true.
STEP 10: Enable S-URL
Go to Setup-->Custom Settings.
Find SDriveConfig and click Manage
Find EnableLegacySurl and click Edit. Change Value to false
Click Save
Go to S-Drive Configuration > Micro Services
Enable S-URL and type in the URL you set for the community (e.g. https://sandbox-surl-enhancements-developer-edition.cs81.force.com/surl)
(Optional) To customize the public share page, upload CSS static resource as “sdrivecustom” and Javascript static resource as “sdrivecustomscript” and change the initial part of static resource links with the community URL link before configuring the UI customization on the S-Drive Configuration page.
Example:Static resource URL: https://guls-demo-cg-dev-ed--c.visualforce.com/resource/1595934376000/sdrivecustom?
will be changed to
SURL Configuration URL: https://cyangate-comm-developer-edition.um5.force.com/surl/resource/1595934376000/sdrivecustom
You can choose to use S-URL for internal open and/or internal download (not recommended)
You can choose to require a password and/or an expiration date for Public Share links.
Click Save
Notes:
Community can NOT have guest user access. Make sure this box is unchecked. Error will be “Too many redirects”