Sunday, April 21, 2024

OAuth 2.0 Web Server Flow to integrate two salesforce orgs

This flow is used to integrate an external web app with the Salesforce API, it implements the OAuth 2.0 authorization code grant type. With this flow, the server hosting the web app must be able to protect the connected app’s identity, defined by the client ID and client secret.

Throughout this topic, we'll dive into the key concepts of the Web Server Flow,  and provide a step-by-step guide on how to implement it between two Salesforce orgs say ORG A and ORG B.

By the end of this session, you'll have a clear understanding of how to leverage the Web Server Flow to create robust integrations that facilitate smooth collaboration and data sharing between Salesforce environments. Let's get started!

The below steps explain the process involved in webserver flow.

  • Request an Authorization Code
  • Authenticate the User and Grant Access to the App
  • Receive a Callback
  • Request an Access Token
  • Receive an Access Token

  • Post Request


Let's start,

ACTIVITIES WE NEED TO DO IN ORG B.

1) Create a Connected App in ORG B.

2) Create a Webservice in ORG B which will be invoked from ORG A.

Create A Connected App In ORG B:

The connected app configured above under "How to configure a Connected App for the OAuth 2.0 Client Credentials Flow?" will work for web server flow as well as we have also enabled "Require Secret for Web Server Flow" check box. The below image also highlights callback URL of ORG A.






Create A Webservice As Shown Below In ORG B:

The createAccountRecord class is an Apex REST service that allows external systems to create Account records in a Salesforce org. It is exposed as a REST resource with the URL mapping '/createAccountRecord/*', meaning it can be accessed via HTTP requests.

This service defines a POST method, createAccount(), which expects JSON data containing information about the Accounts to be created. Upon receiving a request, the method deserializes the JSON payload, creates Account records based on the provided data, and attempts to insert them into the database.

If the insertion is successful, the method returns 'Success'. Otherwise, it returns 'Failure'.

The incoming JSON payload should adhere to the structure defined by the responseWrapper inner class, which includes fields such as Name, Description, Type, and Industry for each Account record.

This REST service provides a simple yet effective means for external systems to programmatically create Account records in the Salesforce org, facilitating seamless integration and data synchronization.

@RestResource(urlMapping='/createAccountRecord/*')

   global with sharing class createAccountRecord {

     @HttpPost

      global Static string createAccount(){

      RestRequest req = RestContext.request;

      RestResponse res = Restcontext.response;

      string jsonString=req.requestBody.tostring();

      system.debug('jsonString'+jsonString);

      boolean successCheck;

      List<responseWrapper> wRespList=(List<responseWrapper>) JSON.deserialize(jsonString,List<responseWrapper>.class);

      Account obj=new Account();

      List<Account> accList= new List<Account>();

      for(responseWrapper wResp: wRespList){

      obj.Name=wResp.Name;

      obj.Description=wResp.Description;

      obj.Type=wResp.Type;

      obj.Industry=wResp.Industry;

      accList.add(obj);

      }

      try{

      if(accList.size() > 0){

      Insert accList;

      successCheck=true;

      }

      }

      catch(Exception e){

      successCheck=false;

      }

      if(successCheck)

      return 'Success';

      else

      return 'Failure';

      }

 

      global class responseWrapper{

 

       global string Name;

       global string Description;

       global string Type;

       global string Industry;

 

      }

 

   }


ACTIVITIES WE NEED TO DO IN ORG A.

Request an Authorization Code:

Hit the below URL to get the authorization code.

https://farukh-dev-ed.lightning.force.com/services/oauth2/authorize?
client_id=Client_Id_Of_ConnectedApp_From_OrgB&
redirect_uri=https://myknowndomain-dev-ed.my.salesforce.com/services/oauth2/callback&
response_type=code

If you’re not familiar with these types of calls, don’t worry. Let’s break it down into its individual components.

Component 1:

https://farukh-dev-ed.lightning.force.com/services/oauth2/authorize

This address is the Salesforce instance’s OAuth 2.0 authorization endpoint of ORG B. It’s the endpoint where your connected apps send OAuth authorization requests.

Component 2:

client_id=Client_Id_Of_ConnectedApp_From_OrgB

The client ID is the connected app’s consumer key from ORG B.

Component 3:

redirect_uri=https://myknowndomain-dev-ed.my.salesforce.com/services/oauth2/callback

The redirect URI is where users are redirected after a successful authorization. In our case it corresponds to ORG A callback URL. The redirect URI is the connected app’s callback URL, which you can also find on the connected app’s Manage Connected Apps page.

This image shows the callback URL that corresponds with the code samples.



Component 4:

response_type=code

The response type tells Salesforce which OAuth 2.0 grant type the connected app is requesting. The response type of code indicates that the connected app is requesting an authorization code.

After you successfully hit the URL, you will be asked to login into Salesforce ORG B. One you logged in with ORG B credentials you will be redirected to below URL as shown below. As you can see the below URL has auth code as highlighted.

https://myknowndomain-dev-ed.my.salesforce.com/services/oauth2/callback?code=aPrxsh*********************************************8Xg%3D%3D

Now, next step is to get the access token and do callout to ORG B to POST account creation request using the auth code obtained above.

The provided code demonstrates an integration scenario where an Apex class interacts with Salesforce REST APIs to perform various operations, such as obtaining an access token, querying data, and making callouts to another Salesforce org.

Here's a breakdown of the key functionalities:

responseWrapper Class: Defines a wrapper class to represent the response from the OAuth token request. It includes fields such as id, access_token, and instance_url.

cKey and cSecret: Variables to store the client ID and client secret obtained from a Custom Metadata Type named Store_Cred__mdt.

Connection Parameter Retrieval: Retrieves the client ID and client secret from the Store_Cred__mdt Custom Metadata Type.

Authentication Code and URI: Defines the authorization code and redirect URI required for the OAuth token request.

Request Body Construction: Constructs the request body for the OAuth token request including the grant type, client ID, client secret, authorization code, and redirect URI.

OAuth Token Request: Sends a POST request to the OAuth token endpoint to obtain an access token using the constructed request body.

Deserialization: Deserializes the response from the OAuth token request into the responseWrapper class.

API Callout: Constructs a callout request to another Salesforce org's Apex REST endpoint (createAccountRecord) to create an Account record.

Setting Headers and Body: Sets the necessary headers (Authorization and Content-Type) and request body for the callout.

Sending Callout Request: Sends the callout request to create an Account record in the target Salesforce org.

Handling Response: Checks the response status code to determine if the callout was successful.

This code demonstrates a typical integration flow where Salesforce Apex code interacts with Salesforce REST APIs to authenticate, exchange data, and perform operations across different Salesforce orgs.

Copy paste the below code in execute anonymous window in ORG A and replace the authCode with the authCode obtained above during authorization request.

public class responseWrapper {

        public string id;

        public string access_token;

        public string instance_url;

    }

public string cKey;

public string cSecret;

List < Store_Cred__mdt > connectionParam = [SELECT Id, MasterLabel, client_id__c, client_secret__c from Store_Cred__mdt];

        if(connectionParam.size() >0){

        cKey=connectionParam[0].client_id__c;

        cSecret=connectionParam[0].client_secret__c;

        }

        System.debug('Store_Cred__mdt' + connectionParam);

        String authCode= 'aPrxsh*********************************************8Xg%3D%3D';

         // The below URI is the callback URL of ORG A

        String uri='https://myknowndomain-dev-ed.my.salesforce.com/services/oauth2/callback';

        String reqBody = 'grant_type=authorization_code&client_id=' + cKey + '&client_secret=' + cSecret + '&code=' + authCode + '&redirect_uri=' + uri ;

        Http h = new Http();

        HttpRequest req = new HttpRequest();

        req.setBody(reqBody);

        req.setMethod('POST');

       // Token end point of  ORG B

        req.setEndpoint('https://farukh-dev-ed.my.salesforce.com/services/oauth2/token');

        HttpResponse hresp = h.send(req);

        System.debug('hresp is'+hresp);

        System.debug('hresp body is'+hresp.getBody());

        responseWrapper wResp = (responseWrapper) JSON.deserialize(hresp.getBody(), responseWrapper.class);

        system.debug('reqBody '+reqBody );

        system.debug('wResp'+wResp );

        system.debug('Instance url' + wResp.instance_url);

        system.debug('session id' + wResp.access_token);

 

         List<Account> accList = new List<Account>();

// End point of ORG B where we will send POST request for account record creation

string endPoint = 'https://farukh-dev-ed.my.salesforce.com'+'/services/apexrest/createAccountRecord';

             accList=[SELECT Id,Name,Description,Type,Industry from account Order By createddate DESC Limit 1];

         Http h1 = new Http();

            HttpRequest req1 = new HttpRequest();

            req1.setHeader('Authorization', 'Bearer ' + wResp.access_token);

            req1.setHeader('Content-Type', 'application/json');

            req1.setMethod('POST');

            req1.setEndpoint(endPoint);

            req1.setBody(JSON.serialize(accList));

            HttpResponse hresp1 = h1.send(req1);

            system.debug('hresp1'+hresp1 );

            system.debug('hresp1.getStatusCode()'+hresp1.getStatusCode());

             system.debug('hresp1.getBody()'+hresp1.getBody());

            if (hresp1.getStatusCode() == 200) {

                system.debug('Callout Success');

            }


After executing the above code from developer console, the below image shows the log. You can also verify the account record will be created in ORG B.


Account record created in ORG B.



No comments:

Post a Comment