-
Notifications
You must be signed in to change notification settings - Fork 139
/
Copy pathindex.js
219 lines (187 loc) · 7.57 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
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
//
// All the following values must match configuration settings in your app.
// They will be used to build the OAuth URL, which users visit to begin
// installing. If they don't match your app's configuration, users will
// see an error page.
// Replace the following with the values from your app auth config,
// or set them as environment variables before running.
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
// Scopes for this app will default to `crm.objects.contacts.read`
// To request others, set the SCOPE environment variable instead
let SCOPES = ['crm.objects.contacts.read'];
if (process.env.SCOPE) {
SCOPES = (process.env.SCOPE.split(/ |, ?|%20/)).join(' ');
}
// On successful install, users will be redirected to /oauth-callback
const REDIRECT_URI = `http://localhost:${PORT}/oauth-callback`;
//===========================================================================//
// Use a session to keep track of client ID
app.use(session({
secret: Math.random().toString(36).substring(2),
resave: false,
saveUninitialized: true
}));
//================================//
// Running the OAuth 2.0 Flow //
//================================//
// Step 1
// Build the authorization URL to redirect a user
// to when they choose to install the app
const authUrl =
'https://app.hubspot.com/oauth/authorize' +
`?client_id=${encodeURIComponent(CLIENT_ID)}` + // app's client ID
`&scope=${encodeURIComponent(SCOPES)}` + // scopes being requested by the app
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`; // where to send the user after the consent page
// Redirect the user from the installation page to
// the authorization URL
app.get('/install', (req, res) => {
console.log('');
console.log('=== Initiating OAuth 2.0 flow with HubSpot ===');
console.log('');
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');
});
// Step 2
// The user is prompted to give the app access to the requested
// resources. This is all done by HubSpot, so no work is necessary
// on the app's end
// Step 3
// Receive the authorization code from the OAuth 2.0 Server,
// and process it based on the query parameters that are passed
app.get('/oauth-callback', async (req, res) => {
console.log('===> Step 3: Handling the request sent by the server');
// Received a user authorization code, so now combine that with the other
// required values and exchange both for an access token and a refresh token
if (req.query.code) {
console.log(' > Received an authorization token');
const authCodeProof = {
grant_type: 'authorization_code',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI,
code: req.query.code
};
// Step 4
// Exchange the authorization code for an access token and refresh token
console.log('===> Step 4: Exchanging authorization code for an access token and refresh token');
const token = await exchangeForTokens(req.sessionID, authCodeProof);
if (token.message) {
return res.redirect(`/error?msg=${token.message}`);
}
// Once the tokens have been retrieved, use them to make a query
// to the HubSpot API
res.redirect(`/`);
}
});
//==========================================//
// Exchanging Proof for an Access Token //
//==========================================//
const exchangeForTokens = async (userId, exchangeProof) => {
try {
const responseBody = await request.post('https://api.hubapi.com/oauth/v1/token', {
form: exchangeProof
});
// Usually, this token data should be persisted in a database and associated with
// a user identity.
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`);
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 the access token has expired, retrieve
// a new one using the refresh token
if (!accessTokenCache.get(userId)) {
console.log('Refreshing expired access token');
await refreshAccessToken(userId);
}
return accessTokenCache.get(userId);
};
const isAuthorized = (userId) => {
return refreshTokenStore[userId] ? true : false;
};
//====================================================//
// Using an Access Token to Query the HubSpot API //
//====================================================//
const getContact = async (accessToken) => {
console.log('');
console.log('=== 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);
}
};
//========================================//
// Displaying information to the user //
//========================================//
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>`);
};
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}`);