Salesforce OAuth & AWS Lambda - a serverless story

If you get to the end of this blog, you will know enough about how to implement Salesforce OAuth using Nodejs on AWS Lambda.

Prerequisites

You may need some exposure & access to the following in order to understand the intent of this blog.

  • NodeJs
  • AWS Lambda
  • Some Salesforce org

Why did we take this approach?

As a self-bootstrapped startup(Notesally), we wanted to keep the operations costs for our MVP on the lower side. We deliberately wanted to stay away from PaaS solutions(such as Heroku which can help in building and running server apps) and use it only when we cannot do without it. While AWS Lambda helps us creating server side apps based on serverless architecture and it is also cost effective, as we incur cost only when there is traffic. This decision helped us reduce the operational cost significantly and to keep our code modular.

Let’s get started

The idea is to create a frontend application and integrate that with the Salesforce OAuth using the AWS Lambda APIs. We will authenticate the users using the Salesforce and upon receiving the accessToken, will be able to access the Salesforce APIs either directly from the UI Application using jsforce or else from the AWS Lambda APIs

Create a Connected App in Salesforce

To do OAuth, you must have a connected app, an external application that integrates with Salesforce using APIs and Identity, obtained using standard protocols such as SAML, OAuth and OpenID. I hope you have a Salesforce account, if not please sign up.

  1. Login into your Salesforce org
  2. Setup -> App Manager -> New Connected App Connected App
  3. Enable the OAuth settings. New Connected App
  4. Set the Callback URL to: https://localhost/oauthtoken. This is the URL Salesforce will redirect to upon successful validation of user credentials, along with the auth code. You would need auth code to get the access token. Don’t worry about the details yet, it is explained in detail as we go along
  • From Available OAuth Scopes, add Access and manage your data (api), and Perform requests on your behalf at any time (refresh_token, offline_access). These settings allow the connected app to access the logged-in user’s account using APIs
  • Once you have created your connected app, note down the consumer key and consumer secret and redirect_url, we will need those in next few steps.

New Connected App

Interaction sequence of various components involved

Salesforce oauth with aws lambda api

Dissection of sequence diagram

  1. User will initiate the interaction by clicking some UI element that will call the /login AWS Lambda api which will create a Salesforce connection using JSForce and will redirect us to the Salesforce login page using 302 response code along with the auth code that the Salesforce authentication requires.
  2. User enters the Salesforce username and password on the Salesforce login screen.
  3. There are two possibilities here.
    1. Upon successful validation of credentials(username & password) and if user authorises our connected app, then Salesforce will redirect the user to the redirectUrl defined while creating the connected app in the Salesforce platform. Since we have pointed our redirectUrl to the /oauthtoken AWS Lambda api, request will get into the lambda function and we will use pass the code to the jsforce api to get the accessToken and other information. Use the redirect response from the lambda api & pass the user back to your UI and pass on the token by some means(either as queryparams or in headers) and persist it on the client side.
    2. If the credentials are invalid, Salesforce display the appropriate error
  4. The user interacts with the UI, which internally uses the stored token to interact with Salesforce APIs

Going further with the code

/login Endpoint

module.exports.login = async (event) => {

    const oauth2 = new jsforce.OAuth2({
        // you can change loginUrl to connect to sandbox or prerelease env.
        loginUrl: 'https://login.salesforce.com',
        //clientId and clientSecret will be provided when you create
        //a new connected app in your SF developer account
        clientId: 'replace_it_with_your_consumerkey',
        clientSecret: 'replace_it_with_your_consumersecret',
        // should match your redirecturl in connected app setup
        redirectUri: 'http://localhost/oauthtoken'
    });

    //Redirect to Salesforce login/authorization page
    return {
        statusCode: 302,
        headers: {
            location: oauth2.getAuthorizationUrl({
                scope: 'api id web refresh_token'
            }),
            'Cache-Control': 'no-cache, no-store, must-revalidate',
            Pragma: 'no-cache',
            Expires: '0',
        },
    };

};

/oauthtoken Endpoint

module.exports.login = async (event) => {

    const oauth2 = new jsforce.OAuth2({
        // you can change loginUrl to connect to sandbox or prerelease env.
        loginUrl: 'https://login.salesforce.com',
        //clientId and clientSecret will be provided when you create
        //a new connected app in your SF developer account
        clientId: 'replace_it_with_your_consumerkey',
        clientSecret: 'replace_it_with_your_consumersecret',
        // should match your redirecturl in connected app setup
        redirectUri: 'http://localhost/oauthtoken'
    });

    const conn = new jsforce.Connection({
        oauth2: oauth2
    });
    const code = req.query.code;
    conn.authorize(code, function(err, userInfo) {
        if (err) {
            return console.error("This error is in the auth callback: " + err);
        }

        var accessToken = conn.accessToken;
        var instanceUrl = conn.instanceUrl;
        var refreshToken = conn.refreshToken;
        var string = encodeURIComponent('true');

        // redirect user back to your UI along with the details retrieved from
        // salesforce to perform further sf calls
        res.redirect('http://localhost:3000' + '/?valid=' + string +
            '&accessToken=' + accessToken + '&instanceUrl=' + instanceUrl + '&refreshToken=' + refreshToken);
    });

}

I would highly recommend reading jsforce documentation to understand the jsforce apis

We hope you found it useful!

Subscribe to Notesally Newsletter