0

I am completely new to Salesforce and its integration via REST API. I am writing a NodeJS server app to fetch and sync accounts and contacts data. I have configured my connected app with the following settings to get the refresh and access tokens (I need these to fetch account and contacts data via API) through the web server workflow of Salesforce. I have selected these OAuth scopes based on this link

enter image description here


enter image description here

The code I am trying where I should be able to get refresh_token and access_token, I should be able to get a new access_token using the refresh_token and I should be able to get accounts & contacts data with the access_token

Code - 1

const express = require('express');
const got = require('got');
const querystring = require('querystring');
const app = express();
const port = 3555;

app.get('/authorize', (req, res) => {
  getAuthorizeCode(req, res);
})

app.get('/callback', (req, res) => {
    processCallback(req, res);
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

module.exports.getAuthorizeCode = async (req, res) => {
    const opts = {
        client_id: encodeURI(`<ClientId>`),
        redirect_uri: `https://5499-103-170-110-158.ngrok.io/callback`,
        response_type: `code`,
        scope: encodeURI(`refresh_token`)
    }
    const installUrl = `<DomainURL>/services/oauth2/authorize?${querystring.stringify(
        opts
    )}`;
    res.redirect(installUrl);
}

module.exports.processCallback = async (req, res) => {
    const url = `<DomainURL>/services/oauth2/token`;
    const headers = {
        accept: 'application/json'
    };
    const code = req.query.code;
    const opts = {
        grant_type: `authorization_code`,
        code,
        client_id: encodeURI(`<ClientId>`),
        client_secret: `<ClientSecret>`,
        redirect_uri: `https://5499-103-170-110-158.ngrok.io/callback`
    }
    const result = await got.post(url, {
        headers,
        searchParams: opts,
        responseType: 'json',
    });
    const accessToken = await getAccessToken(result.body.refresh_token);
    const accountsUrl = `<DomainURL>/services/data/v55.0/sobjects/Account`;
    const accountsHeader = {
        Authorization: `Bearer ${accessToken}`,
    }
    const accountsList = await got(accountsUrl, {
        headers: accountsHeader,
        responseType: 'json',
    })
    console.log(accountsList.body);
    res.redirect(result.body.instance_url)
}

async function getAccessToken(refToken) {
    const url = `https://login.salesforce.com/services/oauth2/token`;
    const headers = {
        accept: 'application/json',
        Authorization: `Basic`
    };
    const opts = {
        grant_type: `refresh_token`,
        client_id: encodeURI(`<ClientId>`),
        client_secret: `<ClientSecret>`,
        refresh_token: encodeURI(refToken)
    }
    const result = await got.post(url, {
        headers,
        searchParams: opts,
        responseType: 'json',
    });
    return result.body.access_token;
}

Using the code - 1, I could get the refresh_token and access_token, also I could get a new access_token with the refresh_token.

But when I try to get the account data using the below endpoint, I get "This session is not valid for use with the REST API" with status code 401.

<DomainURL>/services/data/v55.0/sobjects/Account

Error detail of the above request

[
    {
        "message": "This session is not valid for use with the REST API",
        "errorCode": "INVALID_SESSION_ID"
    }
]

Interestingly, if I try not to get the refresh_token and when tried to fetch accounts data using the access_token that I got with the code below, it works and gives me the accounts data without any issue.

Code - 2

const express = require('express')
const got = require('got');
const querystring = require('querystring');
const app = express()
const port = 3555

app.get('/authorize', (req, res) => {
  getAuthorizeCode(req, res);
})

app.get('/callback', (req, res) => {
    processCallback(req, res);
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

module.exports.getAuthorizeCode = async (req, res) => {
    const opts = {
        client_id: encodeURI(`<ClientId>`),
        redirect_uri: `https://5499-103-170-110-158.ngrok.io/callback`,
        response_type: `code`
    }
    const installUrl = `<DomainURL>/services/oauth2/authorize?${querystring.stringify(
        opts
    )}`;
    res.redirect(installUrl);
}

module.exports.processCallback = async (req, res) => {
    const url = `<DomainURL>/services/oauth2/token`;
    const headers = {
        accept: 'application/json'
    };
    const code = req.query.code;
    const opts = {
        grant_type: `authorization_code`,
        code,
        client_id: encodeURI(`<ClientId>`),
        client_secret: `<ClientSecret>`,
        redirect_uri: `https://5499-103-170-110-158.ngrok.io/callback`
    }
    const result = await got.post(url, {
        headers,
        searchParams: opts,
        responseType: 'json',
    });
    const accessToken = result.body.access_token;
    const accountsUrl = `<DomainURL>/services/data/v55.0/sobjects/Account`;
    const accountsHeader = {
        Authorization: `Bearer ${accessToken}`,
    }
    const accountsList = await got(accountsUrl, {
        headers: accountsHeader,
        responseType: 'json',
    })
    console.log(accountsList.body);
    res.redirect(result.body.instance_url)
}

Code - 2 works fine. But I need refresh_token in order to renew the access_token when it is expired. Also, I need the refresh token that will not be expired until revoked.

I have tried some solutions suggested in the below links. But nothing seems to work for my case

Can someone help me to understand what I am doing wrong or what I am missing?

1

1 Answer 1

0

In your initial request, you do:

    scope: encodeURI(`refresh_token`)

Which means your effective scope is id,refresh_token. To get all the scopes you need, don't include this parameter, or make sure you specify all the scopes you need, such as:

scope: 'refresh_token,full'

I don't think you need to encode the URI parameter, either, should work fine without encoding (but, it shouldn't hurt, either).

1
  • Hi @sfdcfox, I have tried giving such as: scope: 'refresh_token,full' But, I got the below error Unable to Access Page The value of the "scope" parameter contains a character that is not allowed or the value exceeds the maximum allowed length. Remove the character from the parameter value or reduce the value length and resubmit. If the error still persists, report it to our Customer Support team. Provide the URL of the page you were requesting as well as any other related information. But, when I removed the scope prop, it worked like a charm. Thanks @sfdcfox Aug 28, 2022 at 14:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .