Back

How to build a public Salesmate integration: Building the Auth Flow

Sep 15, 20248 minute read

Hey there, fellow JavaScript enthusiasts! Ready to dive into the world of Salesmate integrations? Today, we're going to walk through building a rock-solid authorization flow for your user-facing Salesmate integration. Buckle up, because we're about to make API magic happen!

Introduction

Salesmate's API is a powerful tool for extending your CRM capabilities, but before we can tap into that goldmine, we need to set up a secure authorization flow. This is crucial for protecting user data and ensuring smooth API interactions. Let's get started!

Prerequisites

Before we jump in, make sure you've got:

  • A Salesmate developer account (if you don't have one, go grab it!)
  • Node.js and npm installed on your machine
  • A solid grasp of OAuth 2.0 (don't worry, we'll refresh your memory as we go)

Setting up the project

First things first, let's get our project off the ground:

mkdir salesmate-integration cd salesmate-integration npm init -y npm install express axios dotenv

Great! Now we've got our basic structure and dependencies in place.

Configuring Salesmate API credentials

Head over to your Salesmate developer dashboard and grab your Client ID and Client Secret. These are your keys to the kingdom, so handle them with care!

Create a .env file in your project root and add these credentials:

SALESMATE_CLIENT_ID=your_client_id_here
SALESMATE_CLIENT_SECRET=your_client_secret_here

Implementing the authorization flow

Now for the fun part! Let's build our auth flow:

require('dotenv').config(); const express = require('express'); const axios = require('axios'); const app = express(); const SALESMATE_AUTH_URL = 'https://api.salesmate.io/oauth/authorize'; const SALESMATE_TOKEN_URL = 'https://api.salesmate.io/oauth/token'; app.get('/auth', (req, res) => { const authUrl = `${SALESMATE_AUTH_URL}?client_id=${process.env.SALESMATE_CLIENT_ID}&response_type=code&redirect_uri=http://localhost:3000/callback`; res.redirect(authUrl); }); app.get('/callback', async (req, res) => { const { code } = req.query; try { const response = await axios.post(SALESMATE_TOKEN_URL, { grant_type: 'authorization_code', client_id: process.env.SALESMATE_CLIENT_ID, client_secret: process.env.SALESMATE_CLIENT_SECRET, code, redirect_uri: 'http://localhost:3000/callback' }); const { access_token, refresh_token } = response.data; // Store these tokens securely (more on this later) res.send('Authorization successful!'); } catch (error) { console.error('Error exchanging code for tokens:', error); res.status(500).send('Authorization failed'); } }); app.listen(3000, () => console.log('Server running on http://localhost:3000'));

Token management

Now that we've got our tokens, we need to store them securely. In a production environment, you'd want to encrypt these and store them in a database. For now, let's keep it simple with in-memory storage:

let tokens = { access_token: null, refresh_token: null, expires_at: null }; // Add this to your /callback route tokens = { access_token: response.data.access_token, refresh_token: response.data.refresh_token, expires_at: Date.now() + response.data.expires_in * 1000 };

Making authenticated API requests

With our tokens in hand, let's make some API calls:

async function makeApiRequest(endpoint) { if (Date.now() > tokens.expires_at) { await refreshToken(); } try { const response = await axios.get(`https://api.salesmate.io${endpoint}`, { headers: { Authorization: `Bearer ${tokens.access_token}` } }); return response.data; } catch (error) { console.error('API request failed:', error); throw error; } } async function refreshToken() { try { const response = await axios.post(SALESMATE_TOKEN_URL, { grant_type: 'refresh_token', client_id: process.env.SALESMATE_CLIENT_ID, client_secret: process.env.SALESMATE_CLIENT_SECRET, refresh_token: tokens.refresh_token }); tokens = { access_token: response.data.access_token, refresh_token: response.data.refresh_token, expires_at: Date.now() + response.data.expires_in * 1000 }; } catch (error) { console.error('Token refresh failed:', error); throw error; } }

Error handling and edge cases

We've covered the basics of error handling in our code above, but in a production environment, you'd want to add more robust error handling and logging.

Best practices and security considerations

  • Always use HTTPS in production
  • Implement PKCE (Proof Key for Code Exchange) for added security
  • Never expose your client secret on the client-side
  • Use secure storage solutions for tokens in production

Testing the integration

Set up a test route to try out your new API calls:

app.get('/test', async (req, res) => { try { const data = await makeApiRequest('/v1/deals'); res.json(data); } catch (error) { res.status(500).send('API request failed'); } });

Conclusion

And there you have it! You've just built a solid foundation for your Salesmate integration. From here, you can expand on this base to create powerful, custom integrations that leverage the full potential of the Salesmate API.

Remember, the world of API integrations is vast and exciting. Keep exploring, keep coding, and most importantly, keep having fun with it!

Happy coding, and may your integrations be ever smooth and your tokens always fresh!