Using Bot for data manipulation use case in your company requires need of implementing some extra security layer to make it spoof proof.
One of exciting salesforce feature we have is the well known Einstein Bot which many companies have implemented. I am going to cover detail step-by-step implementation of User validation use case using encrypted token.
STEP-1: Create a Site & Site User
go to setup > Sites & Domains > Sites
Create a Site and make your user as "Site Contact". This is a prerequisites for live agent or Embedded Service setup.
STEP-2 : Create a Embedded Service(formerly snap-ins)
go to Service setup > Embedded Service
Create a new Embedded Service Deployment record and copy the embedded service code snipped generated in a notepad.
STEP-3 : Create a Visualforce page to test the chatbot (it will simulate the actual web portal which is going to consume the embedded service snipped.)
BotController.apxc
BotPage
One of exciting salesforce feature we have is the well known Einstein Bot which many companies have implemented. I am going to cover detail step-by-step implementation of User validation use case using encrypted token.
STEP-1: Create a Site & Site User
go to setup > Sites & Domains > Sites
Create a Site and make your user as "Site Contact". This is a prerequisites for live agent or Embedded Service setup.
STEP-2 : Create a Embedded Service(formerly snap-ins)
go to Service setup > Embedded Service
Create a new Embedded Service Deployment record and copy the embedded service code snipped generated in a notepad.
STEP-3 : Create a Visualforce page to test the chatbot (it will simulate the actual web portal which is going to consume the embedded service snipped.)
BotController.apxc
public class BotController{
public String FirstName{get;set;}
public String LastName{get;set;}
public String Email{get;set;}
public BotController(){
User u = [Select Id,FirstName, LastName, Email from User where Id=:UserInfo.getUserId()];
FirstName = u.FirstName;
LastName = u.LastName;
Email = u.Email;
}
public String getToken(){
String keyToSign = FirstName+LastName+Email;
if(keyToSign == null){
return null;
}
return EncodingUtil.base64Encode(
Crypto.signWithCertificate(
'RSA-SHA256',
Blob.valueOf(keyToSign),
'bot_chat'
)
);
}
}
public String FirstName{get;set;}
public String LastName{get;set;}
public String Email{get;set;}
public BotController(){
User u = [Select Id,FirstName, LastName, Email from User where Id=:UserInfo.getUserId()];
FirstName = u.FirstName;
LastName = u.LastName;
Email = u.Email;
}
public String getToken(){
String keyToSign = FirstName+LastName+Email;
if(keyToSign == null){
return null;
}
return EncodingUtil.base64Encode(
Crypto.signWithCertificate(
'RSA-SHA256',
Blob.valueOf(keyToSign),
'bot_chat'
)
);
}
}
BotPage
<apex:page showHeader="false" controller="BotController" >
<body>
<script type='text/javascript' src='https://service.force.com/embeddedservice/5.0/esw.min.js'></script>
<script type='text/javascript'>
var initESW = function(gslbBaseURL) {
embedded_svc.settings.displayHelpButton = true; //Or false
embedded_svc.settings.language = ''; //For example, enter 'en' or 'en-US'
embedded_svc.settings.enabledFeatures = ['LiveAgent'];
embedded_svc.settings.entryFeature = 'LiveAgent';
var firstname = '{!FirstName}';
var lastname = '{!LastName}';
var email = '{!email}';
var token = '{!token}';
embedded_svc.settings.extraPrechatFormDetails = [{
"label": "First Name",
"transcriptFields": ["FirstName__c"],
"value": firstname
}, {
"label": "Last Name",
"transcriptFields": ["LastName__c"],
"value": lastname
}, {
"label": "Email",
"transcriptFields": ["Email__c"],
"value":email
},{
"label": "token",
"transcriptFields": ["token__c"],
"value":token
}
];
embedded_svc.init(
'https://developerOrg-dev-ed.my.salesforce.com',
'https://developerOrg-edition.ap16.force.com/',
gslbBaseURL,
'00DXXXXXXXXXX',
'AI_Chat',
{
baseLiveAgentContentURL: 'https://c.la2-c2-ukb.salesforceliveagent.com/content',
deploymentId: '572XXXXXXXXXXX',
buttonId: '573XXXXXXXXXXX',
baseLiveAgentURL: 'https://d.la2-c2-ukb.salesforceliveagent.com/chat',
eswLiveAgentDevName: 'EmbeddedServiceLiveAgent_ParentXXXXXXXXXXX_16XXXXXXXXX',
isOfflineSupportEnabled: false
}
);
};
if (!window.embedded_svc) {
var s = document.createElement('script');
s.setAttribute('src', 'https://developerOrg-dev-ed.my.salesforce.com/embeddedservice/5.0/esw.min.js');
s.onload = function() {
initESW(null);
};
document.body.appendChild(s);
} else {
initESW('https://service.force.com');
}
</script>
</body>
</apex:page>
<body>
<script type='text/javascript' src='https://service.force.com/embeddedservice/5.0/esw.min.js'></script>
<script type='text/javascript'>
var initESW = function(gslbBaseURL) {
embedded_svc.settings.displayHelpButton = true; //Or false
embedded_svc.settings.language = ''; //For example, enter 'en' or 'en-US'
embedded_svc.settings.enabledFeatures = ['LiveAgent'];
embedded_svc.settings.entryFeature = 'LiveAgent';
var firstname = '{!FirstName}';
var lastname = '{!LastName}';
var email = '{!email}';
var token = '{!token}';
embedded_svc.settings.extraPrechatFormDetails = [{
"label": "First Name",
"transcriptFields": ["FirstName__c"],
"value": firstname
}, {
"label": "Last Name",
"transcriptFields": ["LastName__c"],
"value": lastname
}, {
"label": "Email",
"transcriptFields": ["Email__c"],
"value":email
},{
"label": "token",
"transcriptFields": ["token__c"],
"value":token
}
];
embedded_svc.init(
'https://developerOrg-dev-ed.my.salesforce.com',
'https://developerOrg-edition.ap16.force.com/',
gslbBaseURL,
'00DXXXXXXXXXX',
'AI_Chat',
{
baseLiveAgentContentURL: 'https://c.la2-c2-ukb.salesforceliveagent.com/content',
deploymentId: '572XXXXXXXXXXX',
buttonId: '573XXXXXXXXXXX',
baseLiveAgentURL: 'https://d.la2-c2-ukb.salesforceliveagent.com/chat',
eswLiveAgentDevName: 'EmbeddedServiceLiveAgent_ParentXXXXXXXXXXX_16XXXXXXXXX',
isOfflineSupportEnabled: false
}
);
};
if (!window.embedded_svc) {
var s = document.createElement('script');
s.setAttribute('src', 'https://developerOrg-dev-ed.my.salesforce.com/embeddedservice/5.0/esw.min.js');
s.onload = function() {
initESW(null);
};
document.body.appendChild(s);
} else {
initESW('https://service.force.com');
}
</script>
</body>
</apex:page>
There is a token generated from visualforce controller using the key personal identifiable information (PII) and a certificate which is generated from salesforce org where Bot is implemented and is imported by the web portal and use the encryption with certificate to generate the token.
Here are the key implementation points with respect to the token validation.
1. Create a hidden field on “Embedded Service” JS file and Portal will populate the key information in the field which must be encrypted, it will be saved to Chat Transcript object in Salesforce.
2. At salesforce end we will create a webservice to query the transcript object and decrypt the details stored in the secret key field, if there is any manipulation done like changing the email, Firstname and other key information, the service will return failure (Boolean) .
3. The API will be called from the Einstein Bot to validate the authenticity.
STEP-4 : Create a self-signed certificate with name "bot_chat" as mentioned in the BotController class above.
STEP-5 : Create a Bot
go to Service Setup> Service Cloud Einstein > Einstein Bot
Setup the bot and create a apex action to validate the token. Pass the context variables like firstname , lastname & email as input parameter before calling the "Validate Token" action.
ValidateToken.apxc
public class ValidateToken{
public static String getToken(String keyToSign){
if(keyToSign == null){
return null;
}
return EncodingUtil.base64Encode(
Crypto.signWithCertificate(
'RSA-SHA256',
Blob.valueOf(keyToSign),
'Bot_Chat'
)
);
}
public class TokenRequest{
@InvocableVariable
public string firstname;
@InvocableVariable
public string lastname;
@InvocableVariable
public string email;
@InvocableVariable
public String token;
}
public class TokenResponse{
@InvocableVariable
public Boolean isValid;
}
@InvocableMethod(label='Validate Token' description='validate user details through certificate')
public static List<TokenResponse> isValidToken(List<TokenRequest> data){
TokenRequest req = data[0];
String genToken = getToken(req.FirstName+req.LastName+req.email);
TokenResponse res = new TokenResponse();
res.isValid = genToken.equals(req.token);
return new List<TokenResponse>{res};
}
}
public static String getToken(String keyToSign){
if(keyToSign == null){
return null;
}
return EncodingUtil.base64Encode(
Crypto.signWithCertificate(
'RSA-SHA256',
Blob.valueOf(keyToSign),
'Bot_Chat'
)
);
}
public class TokenRequest{
@InvocableVariable
public string firstname;
@InvocableVariable
public string lastname;
@InvocableVariable
public string email;
@InvocableVariable
public String token;
}
public class TokenResponse{
@InvocableVariable
public Boolean isValid;
}
@InvocableMethod(label='Validate Token' description='validate user details through certificate')
public static List<TokenResponse> isValidToken(List<TokenRequest> data){
TokenRequest req = data[0];
String genToken = getToken(req.FirstName+req.LastName+req.email);
TokenResponse res = new TokenResponse();
res.isValid = genToken.equals(req.token);
return new List<TokenResponse>{res};
}
}
The token from Bot at server side and the one sent from client side (Web portal) should match to Bot interaction to start , any manipulation with the PII data at client side by any spoofing elements will fail to engage bot.
Nice and good article
ReplyDeleteLearn CPQ Salesforce
Salesforce CPQ learning path
Thanks Bhanu
DeleteVery useful post! The steps for user authentication in Einstein Bot are explained really well. Thanks for making it easy to understand..... Salesforce chatbot
ReplyDelete