SyncMadeEasy/force-app/main/default/classes/salesforceAuthenticationCon...

348 lines
20 KiB
OpenEdge ABL

public with sharing class salesforceAuthenticationController {
list<HIC_ShopifySync__Configuration__c> listofConfiguration = new list<HIC_ShopifySync__Configuration__c>();
String clientIdProduction;
String clientSecretproduction;
String salesforceUrl = system.url.getsalesforcebaseurl().toexternalform();
String myURL = Url.getOrgDomainUrl().toExternalForm();
String Viewmode;
public boolean isConnectionSuccessful{get;set;}
public Pagereference handleAuthentication(){
PageReference pr;
if(ShopifyController.isSiteSupportedOrg()){
System.debug('>>> THIS IS SITE SUPPORTED ORG');
pr = authenticate();
}else{
System.debug('>>> THIS IS NON SITE SUPPORTED ORG');
pr = authenticateSalesforce();
}
return pr;
// return null;
}
public pagereference authenticate(){
system.debug('>>> SITE SUPPORTED ORG METHOD');
String isLwcApp = 'false';
String query = 'Select '+CheckAccessibility.isAccessible('id,name,HIC_ShopifySync__ClientId__c,HIC_ShopifySync__ClientSecret__c','HIC_ShopifySync__Configuration__c')+' from HIC_ShopifySync__Configuration__c where '+CheckAccessibility.isAccessible('name','HIC_ShopifySync__Configuration__c')+' = \'Default\' LIMIT 1';
String finalQuery = String.escapeSingleQuotes(query).remove('\\');
listofConfiguration = database.query(finalQuery);
//listofConfiguration = [select id,name,HIC_Shopi fySync__ClientId__c,HIC_ShopifySync__ClientSecret__c from HIC_ShopifySync__Configuration__c where name = 'Default' limit 1];
if(listofConfiguration.isEmpty()){
return null;
}
clientIdProduction = listofConfiguration[0].HIC_ShopifySync__ClientId__c;
clientSecretproduction = listofConfiguration[0].HIC_ShopifySync__ClientSecret__c;
if(apexpages.currentpage().getparameters().containsKey('viewmode') && apexpages.currentpage().getparameters().get('viewmode') != null && apexpages.currentpage().getparameters().get('viewmode') != '' ){
ViewMode = apexpages.currentpage().getparameters().get('viewmode');
}
if(apexpages.currentpage().getparameters().containsKey('islwcapp') && apexpages.currentpage().getparameters().get('islwcapp') != null && apexpages.currentpage().getparameters().get('islwcapp') != '' ){
isLwcApp = 'true';
}
String mainOrgUrl = 'https://hicshopifymadeeasy-dev-ed.my.salesforce-sites.com/thankYouPageSuccessfulAuthentication';
if(!apexpages.currentpage().getparameters().containsKey('code') ){
Organization orgRecord;
if(Schema.sObjectType.organization.isAccessible() &&
Schema.sObjectType.organization.fields.issandbox.isAccessible() &&
Schema.sObjectType.organization.fields.id.isAccessible() ){
orgRecord = [select id,issandbox from organization LIMIT 1];
}
// PKCE: generate verifier, derive challenge, and pack verifier into state so it
// can be recovered at token-exchange time (cross-org via publisher Site).
String verifier = PkceUtil.generateCodeVerifier();
String challenge = PkceUtil.generateCodeChallenge(verifier);
String pkceParams = '&code_challenge=' + challenge + '&code_challenge_method=S256';
if(orgRecord.issandbox){
if(isLwcApp == 'true'){
String state = PkceUtil.embedVerifierInState(salesforceUrl+'test'+ViewMode+'islwcapp', verifier);
pagereference p = new pagereference(salesforceUrl+'/services/oauth2/authorize?response_type=code&client_id='+clientIdProduction+'&redirect_uri='+mainOrgUrl+'&islwcapp=true&state='+EncodingUtil.urlEncode(state, 'UTF-8')+pkceParams);
return p;
}else{
// NEW SALESFORCE URL
String state = PkceUtil.embedVerifierInState(salesforceUrl+'test'+ViewMode, verifier);
pagereference p = new pagereference(salesforceUrl+'/services/oauth2/authorize?response_type=code&client_id='+clientIdProduction+'&redirect_uri='+mainOrgUrl+'&islwcapp=true&state='+EncodingUtil.urlEncode(state, 'UTF-8')+pkceParams);
return p;
}
}else{
if(isLwcApp == 'true'){
//NEW SALESFORCE URL
String state = PkceUtil.embedVerifierInState(salesforceUrl+'login'+ViewMode+'islwcapp', verifier);
pagereference p = new pagereference(salesforceUrl+'/services/oauth2/authorize?response_type=code&client_id='+clientIdProduction+'&redirect_uri='+mainOrgUrl+'&state='+EncodingUtil.urlEncode(state, 'UTF-8')+pkceParams);
return p;
}else{
String state = PkceUtil.embedVerifierInState(salesforceUrl+'login'+ViewMode, verifier);
pagereference p = new pagereference(salesforceUrl+'/services/oauth2/authorize?response_type=code&client_id='+clientIdProduction+'&redirect_uri='+mainOrgUrl+'&state='+EncodingUtil.urlEncode(state, 'UTF-8')+pkceParams);
return p;
}
}
}
return null;
}
// Method that will run for Non Site Supporting Org
public Pagereference authenticateSalesforce(){
system.debug('>>> NON SITE SUPPORTED ORG METHOD');
//System.debug('SalesforceAuthorizationController called');
String code = ApexPages.currentPage().getParameters().get('code');
// String state = ApexPages.currentPage().getParameters().get('state');
String storeName;
// if (state != null) {
// List<String> redirectParams = state.split('_');
// // Ensure that 'parts' has at least two elements before accessing 'parts[1]'
// if (redirectParams.size() > 1 && redirectParams[1] != null) {
// List<String> subParts = redirectParams[1].split('=');
// if (subParts.size() > 1 && subParts[1] != null) {
// storeName = subParts[1];
// }
// }
// }
Organization orgRecord;
if(Schema.sObjectType.organization.isAccessible() &&
Schema.sObjectType.organization.fields.issandbox.isAccessible() &&
Schema.sObjectType.organization.fields.id.isAccessible() ){
orgRecord = [select id,issandbox from organization LIMIT 1];
}
String endPoint;
Map<String , String> headerMap = new Map<String , String>();
String query = 'Select '+CheckAccessibility.isAccessible('id,name,HIC_ShopifySync__ClientId__c,HIC_ShopifySync__Salesforce_Site_URL__c,HIC_ShopifySync__ClientSecret__c','HIC_ShopifySync__Configuration__c')+' from HIC_ShopifySync__Configuration__c where '+CheckAccessibility.isAccessible('name','HIC_ShopifySync__Configuration__c')+' = \'Default\' LIMIT 1';
String finalQuery = String.escapeSingleQuotes(query).remove('\\');
listofConfiguration = database.query(finalQuery);
//listofConfiguration = [select id,name,HIC_Shopi fySync__ClientId__c,HIC_ShopifySync__ClientSecret__c from HIC_ShopifySync__Configuration__c where name = 'Default' limit 1];
if(listofConfiguration.isEmpty()){
System.debug('@@@ Checking List of configuration :: '+listofConfiguration);
// return null;
}
if(orgRecord.isSandbox){
clientIdProduction = '3MVG9G9pzCUSkzZv4NsQV0kOphuwhNCDbV2xJ4wazQQkD_nyOa73sUwa0Z0DtHz1t5woA9d9AVNwcHvqbQy45';
clientSecretproduction = 'FE5D62CA3C108BF836DDD3DB01C6B063E66F28FFAC5EC3BF43BC6EB248466F4A';
}else{
clientIdProduction = '3MVG9G9pzCUSkzZv4NsQV0kOphm0Il0eDJK5e0YicZLcCZKQcDHS0FORFMYHW8LzqJWPeMxzGUxeyshwEBOin';
clientSecretproduction = '2CF544F99E959C43977763BFBFA5999D4488E0C69757A99149D101EEB62E92B3';
}
String middlewareUrl = listofConfiguration[0].HIC_ShopifySync__Salesforce_Site_URL__c;
// clientIdProduction = listofConfiguration[0].HIC_ShopifySync__ClientId__c;
// clientSecretproduction = listofConfiguration[0].HIC_ShopifySync__ClientSecret__c;
// clientSecretproduction = listofConfiguration[0].HIC_ShopifySync__ClientSecret__c;
if(apexpages.currentpage().getparameters().containsKey('viewmode') && apexpages.currentpage().getparameters().get('viewmode') != null && apexpages.currentpage().getparameters().get('viewmode') != '' ){
ViewMode = apexpages.currentpage().getparameters().get('viewmode');
}
if(apexpages.currentpage().getparameters().containsKey('storeName') && apexpages.currentpage().getparameters().get('storeName') != null && apexpages.currentpage().getparameters().get('storeName') != '' ){
storeName = apexpages.currentpage().getparameters().get('storeName');
}
if(apexpages.currentpage().getparameters().containsKey('storeName') && apexpages.currentpage().getparameters().get('storeName') != null && apexpages.currentpage().getparameters().get('storeName') != '' ){
storeName = apexpages.currentpage().getparameters().get('storeName');
}
try {
System.debug('@@@ Inside Try Method :: ');
if(code != null && code != ''){
System.debug('@@@ Found Code :: '+code);
// PKCE: recover the code_verifier that was packed into state at /authorize time.
String stateParam = ApexPages.currentPage().getParameters().get('state');
String codeVerifier = PkceUtil.extractVerifier(stateParam);
headerMap.put('Content-Type' , 'application/x-www-form-urlencoded');
headerMap.put('Accept' , 'application/json');
headerMap.put('Content-length' , '307');
//String body = 'grant_type=authorization_code&code='+code + '&redirect_uri=https://hic192-dev-ed.develop.my.salesforce-sites.com/thankYouPageSuccessfulAuthentication';
String body = 'grant_type=authorization_code&code='+code + '&redirect_uri=';
// String mainOrgUrl = 'https://hicshopifymadeeasy-dev-ed.my.salesforce-sites.com/thankYouPageSuccessfulAuthentication';
if(orgRecord.isSandbox){
body += 'https://test.salesforce.com/apex/HIC_ShopifySync__SalesforceAuthentication';
//body += 'https://hic192-dev-ed.develop.my.salesforce-sites.com/thankYouPageSuccessfulAuthentication';
//endPoint for sandbox
endPoint = 'https://test.salesforce.com/services/oauth2/token';
body += '&client_id=' +clientIdProduction+ '&client_secret=' +clientSecretproduction+'&islwcapp=true&state=test'+ViewMode+'islwcapp';
}else{
body+='https://login.salesforce.com/apex/HIC_ShopifySync__SalesforceAuthentication';
// body+=mainOrgUrl;
//endPoint for Production
endPoint = 'https://login.salesforce.com/services/oauth2/token';
body += '&client_id=' +clientIdProduction+ '&client_secret=' +clientSecretproduction+'&islwcapp=true&state=prod'+ViewMode+'islwcapp';
}
if(String.isNotBlank(codeVerifier)){
body += '&code_verifier=' + codeVerifier;
}
HttpResponse tokenResp = makeCallout(body, endpoint, headerMap, 'POST');
if(tokenResp != null){ System.debug('@@@ Token exchange status :: '+tokenResp.getStatusCode()); }
// Null-check: makeCallout returns null on any callout failure (timeout/DNS/cert).
if(tokenResp != null && tokenResp.getStatusCode() == 200){
Map<String , Object> jsonMap = (Map<String , Object>) JSON.deserializeUntyped(tokenResp.getBody());
String newAccess = (String) jsonMap.get('access_token');
String newRefresh = (String) jsonMap.get('refresh_token');
// A 200 that lacks a FULL token pair is NOT a successful auth. Never report
// 'connected' with nothing persisted — that strands the org (this is the recovery
// path for a dead org, and the OAuth code is single-use). Fail instead of redirecting.
if(String.isBlank(newAccess) || String.isBlank(newRefresh)){
isConnectionSuccessful = false;
System.debug('@@@ token exchange returned 200 but missing access/refresh — treating as failure');
return null;
}
// Best-effort push to the middleware. This is a CALLOUT, so it runs BEFORE the token
// DML (never a callout-after-DML) and it must NEVER gate the authoritative local save
// — if the middleware is down the org must still keep its freshly issued token.
try {
Map<String , Object> qbDataPassToDataBase = new Map<String , Object>();
qbDataPassToDataBase.put('storeId','salesforceUrl');
qbDataPassToDataBase.put('accessToken' , newAccess);
qbDataPassToDataBase.put('refreshToken' , newRefresh);
qbDataPassToDataBase.put('isSandbox' , orgRecord.isSandbox);
qbDataPassToDataBase.put('orgId' , UserInfo.getOrganizationId());
qbDataPassToDataBase.put('instanceUrl' , URL.getOrgDomainURL().toExternalForm());
Map<String , String> mwHeaders = new Map<String , String>();
mwHeaders.put('Content-Type' , 'application/json');
mwHeaders.put('Accept' , '*/*');
makeCallout(JSON.serialize(qbDataPassToDataBase), middlewareUrl+'/api/auth/callback', mwHeaders, 'POST');
} catch(Exception mwEx) {
System.debug('@@@ Middleware push failed (non-fatal) :: '+mwEx.getMessage());
}
// AUTHORITATIVE local save — the LAST durable action, independent of the middleware.
// updateCredentials throws if it cannot persist, so we never falsely report success.
updateCredentials(newAccess, newRefresh);
isConnectionSuccessful = true;
PageReference ref = new PageReference('/lightning/n/HIC_ShopifySync__SME_Settings');
ref.setRedirect(true);
return ref;
}
}
}
catch (Exception ex) {
System.debug('@@@@ ERROR OCCURED WHILE AUTHORIZING ! at line :: '+ex.getLineNumber()+' with message :: '+ex.getMessage());
}
return null;
}
// public static HttpResponse saveTokenToMiddleWare(Map<String , Object> qbDataPassToDataBase, String endpoint){
// Map<String , String> headerMap = new Map<String , String>();
// headerMap.put('Content-Type' , 'application/json');//;charset=UTF-8');
// // headerMap.put('Accept' , 'application/json');
// headerMap.put('Accept' , '*/*');
// HttpResponse httpResponse = makeCallout(JSON.serialize(qbDataPassToDataBase), endpoint, headerMap, 'POST');
// return httpResponse;
// }
public PageReference redirectToApp(){
PageReference ref = new PageReference('/lightning/n/HIC_ShopifySync__SME_Settings');
ref.setRedirect(true);
return ref;
}
public Static void updateCredentials(String accessToken, String refreshToken){
if(String.isBlank(accessToken) || String.isBlank(refreshToken)) return;
// Serialize on the Default row (FOR UPDATE) and FAIL LOUD: a freshly issued token that cannot
// be saved must surface as an error, never a silent success. The OAuth code is single-use, so
// a swallowed save here would leave the org permanently without a token.
Exception last;
for(Integer attempt = 0; attempt < 3; attempt++){
try {
List<HIC_ShopifySync__Configuration__c> rows = [
SELECT Id FROM HIC_ShopifySync__Configuration__c WHERE Name = 'Default' LIMIT 1 FOR UPDATE
];
if(rows.isEmpty()) return;
update new HIC_ShopifySync__Configuration__c(
Id = rows[0].Id,
HIC_ShopifySync__Access_Token__c = accessToken,
HIC_ShopifySync__Refresh_Token__c = refreshToken
);
return;
} catch(Exception e){
last = e;
}
}
if(last != null) throw last; // never report success after failing to persist the token
}
// Updating Tokens on server
public static void updateAppServer(){
List<HIC_ShopifySync__Configuration__c> cyptokeyquery = ShopifyController.getCryptoKey();
if(cyptokeyquery == null || cyptokeyquery.isEmpty()){
return;
}
Organization orgRecord;
if(Schema.sObjectType.organization.isAccessible() &&
Schema.sObjectType.organization.fields.issandbox.isAccessible() &&
Schema.sObjectType.organization.fields.id.isAccessible() ){
orgRecord = [select id,issandbox from organization LIMIT 1];
}
String middlewareUrl = cyptokeyquery[0].HIC_ShopifySync__Salesforce_Site_URL__c;
// Setting Body to sent to Server
Map<String , Object> qbDataPassToDataBase = new Map<String , Object>();
qbDataPassToDataBase.put('storeId','salesforceUrl');
qbDataPassToDataBase.put('accessToken' , cyptokeyquery[0].HIC_ShopifySync__Access_Token__c);
qbDataPassToDataBase.put('refreshToken' , cyptokeyquery[0].HIC_ShopifySync__Refresh_Token__c);
qbDataPassToDataBase.put('isSandbox' ,orgRecord.isSandbox);
qbDataPassToDataBase.put('orgId' , (UserInfo.getOrganizationId()));
String sfdcBaseURL = URL.getOrgDomainURL().toExternalForm();
qbDataPassToDataBase.put('instanceUrl' , sfdcBaseURL);
Map<String , String> headerMap = new Map<String , String>();
String endPoint = middlewareUrl+'/api/auth/callback';
// Setting Headers
headerMap = new Map<String , String>();
headerMap.put('Content-Type' , 'application/json');
headerMap.put('Accept' , '*/*');
HttpResponse httpResponse = makeCallout(JSON.serialize(qbDataPassToDataBase), endpoint, headerMap, 'POST');
}
public static HttpResponse makeCallout(String body , String endpoint , Map<String , String> headers , String method){
System.debug('@@@ Inside Cllout Method :: '+endpoint);
try{
System.debug('@@@ body : '+body);
System.debug('@@@ endpoint : '+endpoint);
System.debug('@@@ headers : '+headers);
System.debug('@@@ method : '+method);
HttpRequest httpRequestObject = new HttpRequest();
httpRequestObject.setEndpoint(endpoint);
httpRequestObject.setMethod(method);
httpRequestObject.setTimeout(120000);
if(String.isNotEmpty(method)){
if(method.equalsIgnoreCase('POST')){
httpRequestObject.setBody(body);
}
if(method.equalsIgnoreCase('PUT')){
httpRequestObject.setBody(body);
}
}
if(headers != null && !headers.isEmpty()){
for(String headerKey : headers.keySet()){
httpRequestObject.setHeader(headerKey, headers.get(headerKey));
}
}
Http httpObject = new Http();
HttpResponse httpResponseObject = httpObject.send(httpRequestObject);
return httpResponseObject;
}
catch (Exception ex) {
System.debug('@@@ Exception OCcured at line : '+ex.getLineNumber()+' with message :: '+ex.getMessage());
return null;
}
}
}