Hi, I know there are some posts about the “missing or unknown auth code” error in the forum but I feel like I’m missing the solution here.
require('dotenv').config();
const express = require('express');
const request = require('request-promise-native');
const NodeCache = require('node-cache');
const session = require('express-session');
const opn = require('open');
const app = express();
const PORT = 3000;
const refreshTokenStore = {};
const accessTokenCache = new NodeCache({deleteOnExpire: true});
if (!process.env.CLIENT_ID || !process.env.CLIENT_SECRET) {
throw new Error('Missing CLIENT_ID or CLIENT_SECRET environment variable.')
}
//===========================================================================//
// HUBSPOT APP CONFIGURATION
//===========================================================================//
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
let SCOPES = ['crm.objects.contacts.read', 'crm.objects.contacts.write', 'forms'];
if (process.env.SCOPE) {
SCOPES = process.env.SCOPE.split(/ |, ?|%20/).join(' ');
}
const REDIRECT_URI = `http://localhost:${PORT}/oauth-callback`;
//===========================================================================//
app.use(session({
secret: Math.random().toString(36).substring(2),
resave: false,
saveUninitialized: true
}));
const authUrl =
'https://app-eu1.hubspot.com/oauth/authorize' +
`?client_id=${encodeURIComponent(CLIENT_ID)}` +
`&scope=${encodeURIComponent(SCOPES)}` +
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;
console.log("authurl url => ", authUrl);
//================================//
// OAuth 2.0 Flow //
//================================//
app.get('/install', (req, res) => {
console.log('\n=== Initiating OAuth 2.0 flow with HubSpot ===\n');
console.log("===> Step 1: Redirecting user to your app's OAuth URL");
res.redirect(authUrl);
console.log('===> Step 2: User is being prompted for consent by HubSpot');
});
app.get('/oauth-callback', async (req, res) => {
console.log('===> Step 3: Handling the request sent by the server');
if (req.query.code) {
console.log(' > Received an authorization token --> ', req.query.code);
const authCodeProof = {
grant_type: 'authorization_code',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI,
code: req.query.code
};
console.log('===> Step 4: Exchanging authorization code for an access token and refresh token');
try {
const token = await exchangeForTokens(req.sessionID, authCodeProof);
if (token.message) {
return res.redirect(`/error?msg=${token.message}`);
}
res.redirect(`/`);
} catch (error) {
console.error('Error exchanging authorization code for access token:', error);
res.redirect(`/error?msg=Error exchanging authorization code`);
}
} else {
console.error('Authorization code missing');
res.redirect('/error?msg=Authorization code missing');
}
});
app.get('/', async (req, res) => {
res.setHeader('Content-Type', 'text/html');
res.write(`<h2>HubSpot OAuth 2.0 Quickstart App</h2>`);
if (isAuthorized(req.sessionID)) {
const accessToken = await getAccessToken(req.sessionID);
const contact = await getContact(accessToken);
res.write(`<h4>Access token: ${accessToken}</h4>`);
displayContactName(res, contact);
} else {
res.write(`<a href="/install"><h3>Install the app</h3></a>`);
}
res.end();
});
app.get('/error', (req, res) => {
res.setHeader('Content-Type', 'text/html');
res.write(`<h4>Error: ${req.query.msg}</h4>`);
res.end();
});
app.listen(PORT, () => console.log(`=== Starting your app on http://localhost:${PORT} ===`));
opn(`http://localhost:${PORT}`);
//===========================================================================//
// Functions Definitions //
//===========================================================================//
const exchangeForTokens = async (userId, exchangeProof) => {
try {
const data = {
url: 'https://api.hubapi.com/oauth/v1/token',
form: exchangeProof,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
};
const responseBody = await request.post('https://api.hubapi.com/oauth/v1/token', data)
console.log("exchange post responsebody:", responseBody);
const tokens = JSON.parse(responseBody);
refreshTokenStore[userId] = tokens.refresh_token;
accessTokenCache.set(userId, tokens.access_token, Math.round(tokens.expires_in * 0.75));
console.log(' > Received an access token and refresh token');
return tokens.access_token;
} catch (e) {
console.error(` > Error exchanging ${exchangeProof.grant_type} for access token`);
console.error(e.response.body);
return JSON.parse(e.response.body);
}
}
;
const refreshAccessToken = async (userId) => {
const refreshTokenProof = {
grant_type: 'refresh_token', client_id: CLIENT_ID, client_secret: CLIENT_SECRET, redirect_uri: REDIRECT_URI, refresh_token: refreshTokenStore[userId]
};
return await exchangeForTokens(userId, refreshTokenProof);
};
const getAccessToken = async (userId) => {
if (!accessTokenCache.get(userId)) {
console.log('Refreshing expired access token');
await refreshAccessToken(userId);
}
return accessTokenCache.get(userId);
};
const isAuthorized = (userId) => {
return !!refreshTokenStore[userId];
};
const getContact = async (accessToken) => {
console.log('\n=== Retrieving a contact from HubSpot using the access token ===');
try {
const headers = {
Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json'
};
console.log('===> Replace the following request.get() to test other API calls');
console.log('===> request.get(\'https://api.hubapi.com/contacts/v1/lists/all/contacts/all?count=1\')');
const result = await request.get('https://api.hubapi.com/contacts/v1/lists/all/contacts/all?count=1', {
headers: headers
});
return JSON.parse(result).contacts[0];
} catch (e) {
console.error(' > Unable to retrieve contact');
return JSON.parse(e.response.body);
}
};
const displayContactName = (res, contact) => {
if (contact.status === 'error') {
res.write(`<p>Unable to retrieve contact! Error Message: ${contact.message}</p>`);
return;
}
const {firstname, lastname} = contact.properties;
res.write(`<p>Contact name: ${firstname.value} ${lastname.value}</p>`);
};
{“status”:“BAD_AUTH_CODE”,“message”:“missing or unknown auth code”,“correlationId”:“abcd1234etc etc etc”}
Scratching my head here…