Friday, January 31, 2020

How to identify root, intermediate and server certs.

Lot of us gets confuse about the certificate chain to identify which is root, intermediate and server certificate.  I tried to demonstrate it with pictorial representation to get some insight on this.

Primarily to see any domain certificate follow the process specified in the below image.



Root: The Root CA Certificate is always signed by the CA itself. The signatures of all certificates in the chain must be verified up to the Root CA Certificate.






If we notice that in the above picture Issued to and Issued by both are same because the Root CA Certificate is always signed by the CA itself.  If the issued by and Issued to both are same,then it is root cert. By decoding it we can easily identify it is root certificate.

Intermediate : Any certificate that sits between the SSL Certificate and the Root Certificate is called a chain or Intermediate Certificate. The Intermediate Certificate is the signer/issuer of the SSL  Certificate. The Root CA Certificate is the signer/issuer of the Intermediate Certificate. If the Intermediate Certificate is not installed on the server (where the SSL certificate is installed) it may prevent some browsers, mobile devices, applications, etc. from trusting the SSL certificate. 





In order to make the SSL certificate compatible with all clients, it is necessary that the Intermediate Certificate be installed.

Server/Domain certServer certificates (SSL certificates) are used to authenticate the identity of a server. When installed on a website, an SSL certificate turns the protocol on the website from HTTP to HTTPS and installs indicators that vouch for the authenticity of the website






If we notice in the above picture, the Issued by is pointing to the Intermediate cert(DigiCert SHA Secure Server CA) and Issued is pointing to Domain or Sever certificate.

This is how we establish the chaining for any certificates.

 Please find below step by to step  process to create a certificate chain.

1. Get CA signed certificate for domain.
2. Import or Download that certificate as base64.
3. Do the same for all the intermediate certificates (if more than one) and the root certificate.
4. Now create a new file. Example:  certificate_chain.crt.
5. Open that file in text editor and stack all 3 certificates on after the other and save.
1. Order of the certificates is starting from the domain and up towards the root
  • Domain cert
  • Intermediate cert 1 above domain
  • Intermediate cert 2 above that and so on
  • Root cert

2. You must include all certificates up to and including root

Example of merging certificates

-----BEGIN CERTIFICATE-----
MIIGvTCCBaWgAwIBAgIQBsyeRo2C7ECRbEpmpu+mazANBgkqhkiG9w0BAQUFADBI
[TRUNCATE]
MDEyMDAwMFowgYcxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDESMBAG
v+PMGxmcJcqnBrJT3yOyzxIZow==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh
[TRUNCATE]
slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh
v+PMGxmcJcqnBrJT3yOyzxIZow==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
[TRUNCATE]
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

 If you want to decode certificates on your own computer, run this OpenSSL command:
openssl x509 -in certificate.crt -text -noout

Monday, January 13, 2020

Salesforce oAuth 2.0 flows with Node Js Integration.

Tried to wrap all the oAuth flows in one place and able to complete it. Sharing it to help to accelearte
the progress of your poc/project. Please tweak below code to use any flow with node
Js integration.



var sf = require('node-salesforce');
var http = require('http');
var url = require('url');
var fs = require('fs');
request = require('request');
path = require("path"),
https = require('https'), 
fs = require('fs'),  
base64url = require('base64-url'), 
nJwt = require('njwt')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;

let client_Id ='3MVG9d8..z.hDcPKuO0qkcUX0IqWf.AIAsDF_dkcBYL80RvSIKADdH0svSMV_gKhpJAh1HwI4tRNfimBeuwrO';
let client_Secret='67D3847C8AC12050F369457935933CD1205A120C70E69E370B169BFC8CC3AE27';
let  sfdcURL = 'https://login.salesforce.com/services/oauth2/authorize' ;
let  tokenurl = 'https://login.salesforce.com/services/oauth2/token' ;
  
//
// OAuth2 client information can be shared with multiple connections.
//
var express = require('express');
var app = express();
var oauth2 = new sf.OAuth2({
    loginUrl : 'https://login.salesforce.com',
    clientId:client_Id,
    clientSecret:client_Secret,
    redirectUri : 'http://localhost:8000/oauth2/code'
});
//
// Get authz url and redirect to it.

//
app.get('/oauth2/auth', function(req, res) {
  res.redirect(oauth2.getAuthorizationUrl());
});

app.get('/', function (req, res) {
    res.send(`<html>
    <head>
    <style>
    ul {
      list-style-type: none;
      margin: 0;
      padding: 0;
      overflow: hidden;
      background-color: #333333;
    }
    li {
      float: left;
    }
    
    li a {
      display: block;
      color: white;
      text-align: center;
      padding: 16px;
      text-decoration: none;
    }
    
    li a:hover {
      background-color: #111111;
    }
    </style>
    </head>
    <body>
    
    <ul>
      <li><a href="http://localhost:8000/jwt">JWT Flow</a></li>
      <li><a href="http://localhost:8000/oauth2/auth">Web server</a></li>
      <li><a href="http://localhost:8000/username_password">User name and Password</a></li>
      <li><a href="http://localhost:8000/useragent">User agent</a></li>
      <li><a href="http://localhost:8000/refresh">Refresh token flow</a></li>
      <li><a href="http://localhost:8000/samlassertionaccesstoken">SAML Assertion</a></li>
      <li><a href="http://localhost:8000/device">Device flow</a></li>
      <li><a href="https://asset-tokens.herokuapp.com/">Asset token flow</a></li>
    </ul>
    
    </body>
    </html>`);
    
});
app.get('/oauth2/code', function(req, res) {
  var q = url.parse(req.url, true);
  request({   url : tokenurl+'?client_id='+
  client_Id
    +'&redirect_uri='+
          'http://localhost:8000/'+'&grant_type=authorization_code&code='+
          q.query.code+'&client_secret='+client_Secret,  
         method:'POST' 
       },
       function(err, remoteResponse, remoteBody) {
       
         res.send(remoteBody);
       } 
     );

  
});

app.get('/useragent', function (req,res){  
request({   url : sfdcURL+'?client_id='+client_Id+'&redirect_uri='+'http://localhost:8000/'+'&response_type=token',  
        method:'GET' 
      }).pipe(res); 
   
} );

/**
*  Username Password oAuth Flow
*/
app.get('/username_password', function (req,res){  

  var sfdcURL = 'https://login.salesforce.com/services/oauth2/token' ;

 let uname='David@abc.com';
 let pwd='passwordonly'

  var upurl = sfdcURL+
  '?client_id='+ client_Id+
   '&grant_type=password'+
   '&client_secret='+client_Secret+
   '&username='+uname+
   '&password='+pwd ;
 

   request({  url : upurl,  
        method:'POST' 
      },
      function(err, remoteResponse, remoteBody) {
        var sfdcResponse = JSON.parse(remoteBody); 
        res.send(sfdcResponse);
      } 
    );  
} );


function encryptUsingPrivateKey_nJWTLib (claims) {
  var absolutePath = path.resolve("pykey.pem");   
    var cert = fs.readFileSync(absolutePath );  
  var jwt_token = nJwt.create(claims,cert,'RS256'); 
  console.log(jwt_token); 
  var jwt_token_b64 = jwt_token.compact();
  console.log(jwt_token_b64);
 
  return jwt_token_b64;     
};

function getJWTSignedToken_nJWTLib(){ 

  var claims = {
    iss: '3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX',   
    sub: 'david@lightning601.com',     
    aud: 'https://login.salesforce.com',
    exp: (Math.floor(Date.now() / 1000) + (60*3))
   
  }

  return encryptUsingPrivateKey_nJWTLib(claims);
}
app.get('/jwt', function (req,res){  
  
  var sfdcURL = 'https://login.salesforce.com/services/oauth2/token' ;
  
  var sfdcUserName = 'david@lightning601.com';
  var token = getJWTSignedToken_nJWTLib(); 
    
  var paramBody = 'grant_type='+base64url.escape('urn:ietf:params:oauth:grant-type:jwt-bearer')+'&assertion='+token ; 
  var req_sfdcOpts = {  url : sfdcURL,  
              method:'POST', 
              headers: { 'Content-Type' : 'application/x-www-form-urlencoded'} ,
              body:paramBody 
            };
        
  request(req_sfdcOpts, 
    function(err, remoteResponse, remoteBody) {
      res.send(remoteBody);
    } 
  ); 
} );

/**
 * Device Authentication Flow
 */
app.get('/device', function (req,res){  


  let deviceurl = tokenurl+
  '?client_id='+ client_Id+
   '&response_type=device_code&redirect_uri=http://localhost:8000/' ;
 

   request({  url : deviceurl,  
        method:'POST' 
      },
      function(err, remoteResponse, remoteBody) {
        
        console.log(remoteBody) ;
        var sfdcResponse = JSON.parse(remoteBody); 
        var jsondata={
          "verification_uri" : sfdcResponse.verification_uri,
          "user_code" : sfdcResponse.user_code,
          "device_code" : sfdcResponse.device_code
        
        };
        console.log('@@@@@@@',jsondata) ;
        var hrefs=`http://localhost:8000/devicePol?device_code=${sfdcResponse.device_code}`;
        var step2redirect=`<html><head>${JSON.stringify(jsondata)} </head><body><a href=${hrefs}> Post authorizing in different browser/device click here: Device flow step 2 </a> </body></html>`;
        console.log(step2redirect) ;
        //res.send(JSON.parse(JSON.stringify(step2redirect)));
//${jsondata}
        res.send(step2redirect);
      } 
    );  
} ); 

/**
 *  Keep polling till device is verified using code
 */

app.get('/devicePol', function (req,res){ 
  var device_code = req.query.device_code;
  console.log('@@@@@@@@@device code@@@@@@@@@@@@',device_code);
  var devicePolurl = tokenurl+'?client_id='+ client_Id+'&code='+device_code+'&grant_type=device';
 //console.log('@@@@@@devicePolurl@@@@@@@',devicePolurl)
   request({  url : devicePolurl,  
      method:'POST' 
    },
    function(err, remoteResponse, remoteBody) {
      
      //console.log(remoteBody) ;
      var sfdcResponse = JSON.parse(remoteBody); 
      res.send(sfdcResponse);   
    } 
  );  
} ); 

 

  app.get('/samlassertionaccesstoken', function (req,res){ 
   
    let assertionvalue='PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIERlc3RpbmF0aW9uPSJodHRwczovL2xpZ2h0NjAxZGVwLWRldi1lZC5teS5zYWxlc2ZvcmNlLmNvbT9zbz0wMEQ3RjAwMDAwNEIxRXYiIElEPSJfNzAyNjBhYzItMjg0MTI0MmMiIElzc3VlSW5zdGFudD0iMjAxOS0xMi0yNlQxOToyMDozMy4xODdaIiBWZXJzaW9uPSIyLjAiPjxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPkF4aW9tPC9zYW1sMjpJc3N1ZXI';   tokenurl='https://light601dep-dev-ed.my.salesforce.com/services/oauth2/token';
    var samlurl = tokenurl+'?grant_type=assertion&assertion_type=urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser&assertion='+assertionvalue;
    console.log(samlurl);
      request({   url : samlurl,  
         method:'POST' 
       },
       function(err, remoteResponse, remoteBody) {
         
         //console.log(remoteBody) ;
         var sfdcResponse = JSON.parse(remoteBody); 
         console.log(sfdcResponse)
         res.send(sfdcResponse);    
       } );
      });
      app.get('/refresh', function(req, res) {
       
        let refresh_token = '5Aep8613hy0tHCYdhyHUIj3Fev55mM_VcyWVY6uxHslAKE.FvcRJitylwr4jQhZcx9Hwo7SA6lbd6f.AWbyKtfE';
        var q = url.parse(req.url, true);
        request({   url : tokenurl+'?client_id='+client_Id+'&redirect_uri='+
                'http://localhost:8000/'+'&grant_type=refresh_token&refresh_token='+
                refresh_token,  
               method:'POST' 
             },
             function(err, remoteResponse, remoteBody) {
             
               res.send(remoteBody);
             } 
           );
      
  
    
  });
   
var server = app.listen(8000, function () {
  
});

All Salesforce oAuth 2.0 flows in one place with python integration

Below code covers most of the oAuth 2.0 flows of Salesforce. Tweaking below code helps you
to implement any oAuth 2.0 flow within no time. All the Best !!

import flask
import requests
import tkinter as tk
import json
from json2html import *
from flask import request, jsonify
from base64 import urlsafe_b64encode
from datetime import datetime, timedelta
import Crypto
from flask import (
    Flask,
    render_template,
    jsonify,
)

import os
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

app = flask.Flask(__name__)
app = Flask(__name__)
app.config["DEBUG"] = TrueJWT_HEADER = '{"alg":"RS256"}'JWT_CLAIM_ISS = "3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX"JWT_CLAIM_SUB = "david@lightning601.com"JWT_CLAIM_AUD = "https://login.salesforce.com"JWT_AUTH_EP = "https://login.salesforce.com/services/oauth2/token"'''===========================Home page ============================================='''

@app.route('/', methods=["GET"])
def home():
    return "<h1> This blog is a great place to learn advanced concepts </h1>"

##############webserver first step ###########################
@app.route('/sfconnect', methods=["GET"])
def sfconnect():
    # return "<a href='https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsalesforcedata'> Webserver flow </a>"    allflows = "<div style='background-color: #fad0c4;background-image: linear-gradient(315deg, #fad0c4 0%, #f1a7f1 74%);height:570px;width:1260px' ><div style='padding: 1em;position: absolute;'><ul style='list-style-type:disc;color: green;'>" + "<li style='color: green;font-size: 150%'>" + "<a href='https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsalesforcedata'> Webserver flow </a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsfconnect'> User agent flow </a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/unamepwd'>User name & Password flow </a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/refresh'>Refresh token flow</a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/jwtflow'>JWT Bearer flow</a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/Deviceflow'>Device flow step 1</a>" + "</li>" + \
               "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/samlassertionflow'>Saml Assertion flow</a>" + "</li>"\
               "<li style='color: green;font-size: 150%'>" + "<a href='https://asset-tokens.herokuapp.com/' target = '_blank' >Asset Token flow</a>" + "</li>"+ "</ul></div></div>"
    return allflows


############################################################################################ Get token ###############################################################################################
@app.route('/salesforcedata', methods=["GET"])
def sfdata():
    print(request.args)
    if 'code' in request.args:
        code = request.args["code"]

    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"    # r = requests.post(url=AUTHORIZE_ENDPOINT, data=dataauthorize)
    datafortoken = {'grant_type': 'authorization_code',
                    'client_id': '3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX',
                    'client_secret': 'B682A1156FF3A2CCF0E83764552CD1DD0FD2A4510577DDE69C408D80FB9F4A76',
                    'code': code,
                    'redirect_uri': 'http://localhost:5000/salesforcedata'                    }

    r = requests.post(url=API_ENDPOINT, data=datafortoken)
    sfdcres = r.text
    print("The pastebin URL is:%s" % sfdcres)

    dicdata = json.loads(sfdcres)

    # print('#############',jsondata,'$$$$$$$$$$',dicdata);
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)

    # place your own domain name.    return getdata(dicdata["access_token"])


############################################################################# User name and password flow####################################################################################
@app.route('/unamepwd', methods=["GET"])
def username_pwd():
    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"
    clientid = '3MVG9G9pzCUSkzZvyptQFNKyeFZLuFEuMqSNrj_gLpwyqgHTmUSLK0qb0EvWiNm6VYzLixRXeULtJZlm3LevO'    clientsec = '32B4AFDB1FD3E9B8FD62FD2333ACC9B33A65778C35634F8D618745746A2A6C86'    # data to be sent to api    data = {'grant_type': 'password',
            'client_id': clientid,
            'client_secret': clientsec,
            'username': 'David@abc.com',
            'password': 'pwdsecuritytoken'            }

    # String reqbody = 'grant_type=password&client_id='+clientid+'&client_secret='+clientsec+'&username='+'David@lightning601.com'+'&password='+'Welcome@18G841Lq4OPaBc3nvtMcGmhC5GH';
    # sending post request and saving response as response object    r = requests.post(url=API_ENDPOINT, data=data)

    # extracting response text    # sfdcres = json.dumps(r.text)    resp = json.loads(r.text)
    # return json2html.convert(json=json.loads(r.text))
    return getdata(resp["access_token"])


############################################################################# Refresh token flow#############################################################################################
@app.route('/refresh', methods=["GET"])
def refrehflow():
    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"
    clientid = '3MVG9G9pzCUSkzZvyptQFNKyeFZLuFEuMqSNrj_gLpwyqgHTmUSLK0qb0EvWiNm6VYzLixRXeULtJZlm3LevO'    refresh_token = '5Aep8613hy0tHCYdhyHUIj3Fev55mM_VcyWVY6uxHslAKE.Fvf2x7ni.qVWP4HfxXN7ieW.wzJFXROLFe4F8cR9'  # data to be sent to api    data = {'grant_type': 'refresh_token',
            'client_id': clientid,
            'refresh_token': refresh_token
            }

    # String reqbody = 'grant_type=password&client_id='+clientid+'&client_secret='+clientsec+'&username='+'David@lightning601.com'+'&password='+'Welcome@18G841Lq4OPaBc3nvtMcGmhC5GH';
    # sending post request and saving response as response object    r = requests.post(url=API_ENDPOINT, data=data)

    # extracting response text    sfdcres = r.text
    # return json2html.convert(json=json.loads(r.text))    resp = json.loads(r.text)
    # return json2html.convert(json=json.loads(r.text))
    return getdata(resp["access_token"])


############re usable method #########################
def getdata(acctkn):
    END_POINT = "https://light601dep-dev-ed.my.salesforce.com/services/apexrest/contactsfordatabase/";

    data = {"Authorization": "Bearer " + acctkn,
            "Content-Type": "application/json"            }

    r = requests.get(END_POINT, headers=data)

    print("contacts data from salesforce:%s" % r.content)

    # dict = json.loads(r.content)
    # return r.content    return json2html.convert(json=json.loads(r.content))


def jwt_claim():
    '''    Function to package JWT Claim data in a base64 encoded string    :return:    base64 encoded jwt claims data    '''
    claim_template = '{{"iss": "{0}", "sub": "{1}", "aud": "{2}", "exp": {3}}}'    claim = urlsafe_b64encode(JWT_HEADER.encode()).decode()
    claim += "."
    # expiration_ts = (datetime.now(tz=timezone.utc) + timedelta(minutes=5)).timestamp()    expiration_ts = int((datetime.now() + timedelta(seconds=300)).timestamp())
    payload = claim_template.format(JWT_CLAIM_ISS, JWT_CLAIM_SUB, JWT_CLAIM_AUD, expiration_ts)
    print(payload)

    claim += urlsafe_b64encode(payload.encode()).decode()
    return claim


def sign_data(data):
    f = open('pykey.pem', 'r')
    print('$$$$$$$file$$$$$$$$', f)
    rsa1key = RSA.importKey(f.read())
    signer = PKCS1_v1_5.new(rsa1key)
    digest = SHA256.new()
    digest.update(data.encode())
    sign = signer.sign(digest)
    return urlsafe_b64encode(sign).decode()


def do_auth(endpoint, data):
    '''    Function to POST JWT claim to SFDC /oauth/token endpoint and receive an access_token    :return:    access token    '''
    r = requests.post(endpoint, data=data)
    return r


@app.route('/jwtflow', methods=["GET"])
def jwtflow():
    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    claim = jwt_claim().replace("=", "")

    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    signed_claim = sign_data(claim).replace("=", "")

    target_payload = claim + "." + signed_claim

    auth_payload = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": target_payload}
    response = do_auth(JWT_AUTH_EP, data=auth_payload)

    #  convert the text dictionary to data structure so it can be rendered as a json properly    response_text = eval(response.text)

    response_headers = eval(str(response.headers))

    return_dict = {"claim": claim, "signed_claim": signed_claim, "target_payload": target_payload,
                   "response_text": response_text, "response_headers": response_headers}
    # return jsonify(return_dict)    sfdcres = response.text
    print("The SFDC response:%s" % sfdcres)

    dicdata = json.loads(sfdcres)

    # print('#############',jsondata,'$$$$$$$$$$',dicdata);
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)

    # place your own domain name.    return getdata(dicdata["access_token"])


#####################Device flow#######################################
@app.route('/Deviceflow', methods=["GET"])
def deviceflow():
    print(request.args)
    Deviceflowclientid = '3MVG9d8..z.hDcPKuO0qkcUX0IvjSlDS5xKC7RazmseJKnAbeQxg3zIE55yk7zvsy3NpjLIayWxNoUU3RfYz0';

    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"
    getdevicecode = {'response_type': 'device_code',
                     'client_id': Deviceflowclientid,
                     'client_secret': 'B682A1156FF3A2CCF0E83764552CD1DD0FD2A4510577DDE69C408D80FB9F4A76',
                     'redirect_uri': 'http://localhost:5000/Deviceflowstep2'                     }

    r = requests.post(url=API_ENDPOINT, data=getdevicecode)
    sfdcres = r.text

    dicdata = json.loads(sfdcres)
    devicecode = dicdata["device_code"]
    parseddata = ''    for i in dicdata:
        parseddata = parseddata+"<tr><th>" + str(i) + "</th><td>" + str(dicdata[i]) + "</td></tr>"    parseddata = "<table border='1' style='color:blue;background-color:gold;'><tbody>" + parseddata + "</tbody></table>"    stepsbeforegettingtoken="Before clicking below link, take the verification uri <b style='color:red'>{}</b> and enter it into another device(Mobile/Deskstop)/n and then enter User code <b style='color:red'>{}</b>".format(str(dicdata['verification_uri']),str(dicdata['user_code']))
    step2link = "<br/><br/><b style='color:Green'>"+stepsbeforegettingtoken+"</b><br/><h2><a href = " + "http://127.0.0.1:5000/Deviceflowstep2?devicecode=" + devicecode + "> Device flow step 2 (Get access token) " + "</h2>"    parseddata = parseddata + step2link
    return parseddata

###################################
#####################Device flow#######################################
@app.route('/Deviceflowstep2', methods=["GET"])
def deviceflowstep2():
    print('####################', request.args)
    # return request.args.devicecode
    dicdata = json.dumps(request.args)
    # devicecode = dicdata["devicecode"]    print(dicdata)

    dicdata = json.loads(dicdata)
    Deviceflowclientid = '3MVG9d8..z.hDcPKuO0qkcUX0IvjSlDS5xKC7RazmseJKnAbeQxg3zIE55yk7zvsy3NpjLIayWxNoUU3RfYz0';

    API_ENDPOINT = "https://login.salesforce.com/services/oauth2/token"    # r = requests.post(url=AUTHORIZE_ENDPOINT, data=dataauthorize)
    getdevicecode = {'grant_type': 'device',
                     'client_id': Deviceflowclientid,
                     'code': dicdata['devicecode']
                     }

    r = requests.post(url=API_ENDPOINT, data=getdevicecode)
    sfdcres = r.text

    dicdata = json.loads(sfdcres)
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)


    return json2html.convert(json=json.loads(r.content))


#########################################################################################



def samlflow(entries):

    assertion=str(entries['assertion'].get())
    API_ENDPOINT = "https://light601dep-dev-ed.my.salesforce.com/services/oauth2/token"    # r = requests.post(url=AUTHORIZE_ENDPOINT, data=dataauthorize)    import base64

    data=str(entries['assertion'].get())
    getdevicecode = {'grant_type': 'assertion',
                     'assertion_type': 'urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser',
                     'assertion': data}
    r = requests.post(url=API_ENDPOINT, data=getdevicecode)
    sfdcres = r.text
    print('##################',sfdcres)
    dicdata = json.loads(sfdcres)
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)

    return json2html.convert(json=json.loads(r.content))


def makeform(root, fields):
    entries = {}
    for field in fields:
        print(field)
        row = tk.Frame(root)
        lab = tk.Label(row, width=22, text=field+": ", anchor='w')
        ent = tk.Entry(row)
        ent.insert(0, "0")
        row.pack(side=tk.TOP,
                 fill=tk.X,
                 padx=5,
                 pady=5)
        lab.pack(side=tk.LEFT)
        ent.pack(side=tk.RIGHT,
                 expand=tk.YES,
                 fill=tk.X)
        entries[field] = ent
    return entries

@app.route('/samlassertionflow', methods=["GET"])
def samlassertionflow():
    app = flask.Flask(__name__)
    app = Flask(__name__)
    root = tk.Tk()
    fields = ('assertion', 'End point')
    ents = makeform(root, fields)
    b1 = tk.Button(root, text='Saml assertion flow',
           command=(lambda e=ents: samlflow(e)))
    b1.pack(side=tk.LEFT, padx=5, pady=5)

    root.mainloop()

    return 'Proceed with the generated input interface'


#########################################################################################
if __name__ == "__main__":
    port = int(os.getenv("PORT", 5000))
    # Run the app, listening on all IPs with our chosen port number    app.run(host="0.0.0.0", port=port, debug=True, use_reloader=True)

app.run()

Salesforce JWT Flow with Python to retrieve and insert data into SQL.

We can use below code to fetch the data from SF via JWT flow from python and insert it into 
Database.In case if you want to schedule your python program to retrieve the data from SF to
take the backup of your data you can refer this link 
"https://datatofish.com/python-script-windows-scheduler/" to learn how to schedule 
any python program from Windows. 

Note: You need to tweak your code according to your Salesforce connected app settings like 
consumerkey and certificates and username of your SF.



import flask
import requests
import tkinter as tk
import json
from json2html import *
import mysql.connector
from flask import request, jsonify
from base64 import urlsafe_b64encode
from datetime import datetime, timedelta
from mysql.connector import Error
from mysql.connector import errorcode
import Crypto
from flask import (
    Flask,
    render_template,
    jsonify,
)

import os
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

app = flask.Flask(__name__)
app = Flask(__name__)
app.config["DEBUG"] = TrueJWT_HEADER = '{"alg":"RS256"}'JWT_CLAIM_ISS = "3MVG9d8..z.hDcPKuO0qkcUX0IncIWJdRX90Yc5BHsYUmbsHh8eeRweKXys8392v5gcuiK1_WHF3_zSS9rfoX"JWT_CLAIM_SUB = "david@abc.com"JWT_CLAIM_AUD = "https://login.salesforce.com"JWT_AUTH_EP = "https://login.salesforce.com/services/oauth2/token"
@app.route('/', methods=["GET"])
def home():
    return "<h1> This blog is a great place to learn advanced concepts </h1>"

@app.route('/sfconnect', methods=["GET"])
def sfconnect():
    # return "     allflows = "<div style='background-color: #fad0c4;background-image: linear-gradient(315deg, #fad0c4 0%, #f1a7f1 74%);height:570px;width:1260px' ><div style='padding: 1em;position: absolute;'><ul style='list-style-type:disc;color: green;'>" + \
   "<li style='color: green;font-size: 150%'>" + "<a href='http://127.0.0.1:5000/jwtflow'>JWT Bearer flow</a>" + "</li>" + "</ul></div></div>"     return allflows

############re usable method #########################
def getdata(acctkn):
    END_POINT = "https://light601dep-dev-ed.my.salesforce.com/services/apexrest/contactsfordatabase/";

    data = {"Authorization": "Bearer " + acctkn,
            "Content-Type": "application/json"            }

    r = requests.get(END_POINT, headers=data)
    r2= json.loads(r.content)
    print("###test#### get typeof from salesforce:%s", type(r2))
    sf_data = r.content
    data=r2
    print("###test123####contacts data from salesforce:%s",data)
    mydb = mysql.connector.connect(
        host="127.0.0.1",
        user="root",
        passwd="password",
        database="salesforcedata",auth_plugin='mysql_native_password')
    mycursor = mydb.cursor()
    for x in data:
        sql = "INSERT INTO Contactsf (FirstName, LastName) VALUES (%s, %s)"        val = (x['FirstName'], x['LastName'])
        mycursor.execute(sql, tuple(val))
        mydb.commit()
        continue
    mycursor.close()  ## here all loops done    mydb.close()  ## close db connection
    return json2html.convert(json=json.loads(r.content))


def jwt_claim():
    '''    Function to package JWT Claim data in a base64 encoded string    :return:    base64 encoded jwt claims data    '''
    claim_template = '{{"iss": "{0}", "sub": "{1}", "aud": "{2}", "exp": {3}}}'    claim = urlsafe_b64encode(JWT_HEADER.encode()).decode()
    claim += "."
    # expiration_ts = (datetime.now(tz=timezone.utc) + timedelta(minutes=5)).timestamp()    expiration_ts = int((datetime.now() + timedelta(seconds=300)).timestamp())
    payload = claim_template.format(JWT_CLAIM_ISS, JWT_CLAIM_SUB, JWT_CLAIM_AUD, expiration_ts)
    print(payload)

    claim += urlsafe_b64encode(payload.encode()).decode()
    return claim


def sign_data(data):
    f = open('pykey.pem', 'r')
    print('$$$$$$$file$$$$$$$$', f)
    rsa1key = RSA.importKey(f.read())
    signer = PKCS1_v1_5.new(rsa1key)
    digest = SHA256.new()
    digest.update(data.encode())
    sign = signer.sign(digest)
    return urlsafe_b64encode(sign).decode()


def do_auth(endpoint, data):
    '''    Function to POST JWT claim to SFDC /oauth/token endpoint and receive an access_token    :return:    access token    '''
    r = requests.post(endpoint, data=data)
    return r


@app.route('/jwtflow', methods=["GET"])
def jwtflow():
    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    claim = jwt_claim().replace("=", "")

    # Keeping with JWS spec, we need to remove the padding "=" characters from base64 encoded string    signed_claim = sign_data(claim).replace("=", "")

    target_payload = claim + "." + signed_claim

    auth_payload = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": target_payload}
    response = do_auth(JWT_AUTH_EP, data=auth_payload)

    #  convert the text dictionary to data structure so it can be rendered as a json properly    response_text = eval(response.text)

    response_headers = eval(str(response.headers))

    return_dict = {"claim": claim, "signed_claim": signed_claim, "target_payload": target_payload,
                   "response_text": response_text, "response_headers": response_headers}
    # return jsonify(return_dict)    sfdcres = response.text
    print("The SFDC response:%s" % sfdcres)

    dicdata = json.loads(sfdcres)

    # print('#############',jsondata,'$$$$$$$$$$',dicdata);
    acctkn = dicdata["access_token"]

    print('parsed access token', acctkn)


    return getdata(acctkn)

if __name__ == "__main__":
    port = int(os.getenv("PORT", 8001))
    # Run the app, listening on all IPs with our chosen port number    app.run(host="0.0.0.0", port=port, debug=True, use_reloader=True)

app.run()

Friday, December 27, 2019

Get current release of the Salesforce with the Rest API

>> Some times when integrating with salesforce, we are unsure what is the current release of the org which we are trying to integrate.

>> Even after logging into the salesforce, we can only see the image of the current release and version number but in none of the places, we can see whether it is winter, spring or summer release.

>> To get the information, login into the workbench and execute the below URL to get the releases data, the last record is the current version/release of your instance. From the below picture, you can identify the current release of my org is Winter 20

/services/data/
















If you wish to get the same information in the any other integration system to check if there is any release that took place recently, use the below URL after getting access token/session Id.


It also provides the latest version/release of the organization which you are trying to hit.

Note: Salesforce runs on multiple server instances. The examples in this guide use yourInstance in place of a specific instance. Replace that text with the instance for your org


Thursday, July 25, 2019

Remove Lightning web components Play ground footer for better Playing.

When I'm accessing the Play ground to test few things on the lightning web components on the component library documentation,  I have found the practice is quite annoying because of  the existing large footer on the page which always compels me to scroll down the page unnecessarily which in turn consuming the time.  I thought of removing certain parts of the page so that I can focus on what matters the most and test the code accordingly.  Eventually came up with the below code to skip the long footer and some part of the header to create proper platform for your testing and reading the lightning web component guide.

Open your browser console and execute below code to skip the long footer and some part of the header to create proper platform for your testing and reading the web component guide with ease.


var footer = document.querySelector('footer'); footer.parentNode.removeChild(footer);

var compoheader = document.querySelector('.bottom'); compoheader.parentNode.removeChild(compoheader);

Before executing code, your page looks like below.


After executing code in the console, your page looks like below.




Note: Even while reading the lightning web component library, paste above code and read the whole document with luxury until you refresh your url completely.




Wednesday, May 15, 2019

Override or append to the existing SLDS Classes.

You might wonder is there any way to override or append something to the existing SLDS classes generated by the salesforce. Yes there is a way to do it, that is what we are discussing here.

When ever you use lightning prefix components, behind the scene's there would be lot of internal slds classes generated.
We can extend these generated classes functionality by appending/overriding our custom css to the existing classes.

------------
component code
------------

<aura:component description="search form">
    <aura:attribute name="searchterm" type="string" default="dave"/>
 
      <lightning:input value="{!v.searchterm}" name="search term" label="Search term(s)" class="myclass"/>

</aura:component>

Out put as follows.




Now we want different border color for the input field. To do that, follow the below steps

inspect the code generated by the framework as below.

And then create your own css class append to the auto generated slds class.

-----------
Css code
------------
.THIS.myclass .slds-input {
    border-color:purple;
    border-width :8px;
}

Now you will see the expected  border with different color and border width.


Overall, we were able to append our own css to the existing lightning:input element.

Note: you cannot override standard styling of slds classes in LWC due to the Shadow DOM and the way that CSS works in LWC. Currently developers do not have a way to override/customize CSS that is derived from Base Components. Salesforce team is working on giving developers more options, but at this point of time they can't commit to a timeline.

help link : https://success.salesforce.com/answers?id=9063A000000ePZSQA2