I'm working on an SPA for a local store with React and I want to use Nodemailer (with Google APIs - OAuth2) so that users can send an email to the store's gmail through a contact form at /contacto. I've tried different solutions mentioned in different places but none has worked for my case.
EDIT: I'm getting this error:
GaxiosError: unauthorized_client
at Gaxios._request (C:\Users\pc\OneDrive\Escritorio\lynstore\node_modules\gaxios\build\src\gaxios.js:129:23)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async OAuth2Client.refreshTokenNoCache (C:\Users\pc\OneDrive\Escritorio\lynstore\node_modules\google-auth-library\build\src\auth\oauth2client.js:174:21)
at async OAuth2Client.refreshAccessTokenAsync (C:\Users\pc\OneDrive\Escritorio\lynstore\node_modules\google-auth-library\build\src\auth\oauth2client.js:198:19)
at async OAuth2Client.getAccessTokenAsync (C:\Users\pc\OneDrive\Escritorio\lynstore\node_modules\google-auth-library\build\src\auth\oauth2client.js:227:23)
at async sendMail (C:\Users\pc\OneDrive\Escritorio\lynstore\src\api\mailer.js:33:27)
This is the code and it is located at src/api/mailer.js:
require('dotenv').config();
const nodemailer = require('nodemailer');
const { google } = require('googleapis');
const express = require('express');
const path = require('path');
const app = express();
const cors = require('cors');
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cors());
app.post('/send', (req, res) => {
console.log(req.body.formData);
const userInfo = req.body.formData;
const EMAIL = process.env.REACT_APP_EMAIL;
const CLIENT_ID = process.env.REACT_APP_CLIENT_ID;
const CLIENT_SECRET = process.env.REACT_APP_CLIENT_SECRET;
const REDIRECT_URI = process.env.REACT_APP_REDIRECT_URI;
const REFRESH_TOKEN = process.env.REACT_APP_REFRESH_TOKEN;
const oAuth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URI
);
oAuth2Client.setCredentials({ refresh_token: REFRESH_TOKEN });
const sendMail = async () => {
try {
const accessToken = await oAuth2Client.getAccessToken();
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: EMAIL,
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
refreshToken: REFRESH_TOKEN,
accessToken: accessToken,
},
});
let mailOptions = {
from: `${userInfo.firstName} ${userInfo.lastName} <${userInfo.email}>`,
to: EMAIL,
subject: `Email sent by ${userInfo.firstName} ${userInfo.lastName} from the website`,
text: `${userInfo.message}`,
html: `
<p>You have a new email sent from the website!</p>
<h3>Contact details:</h3>
<ul>
<li>Name: ${userInfo.firstName} ${userInfo.lastName}</li>
<li>Email: ${userInfo.email}</li>
</ul>
<h3>Message:</h3>
<p>${userInfo.message}</p>
`,
};
const result = await transporter.sendMail(mailOptions);
return result;
} catch (err) {
console.log('Error!', err);
}
};
sendMail()
.then((result) => res.status(200).send('Email sent!'))
.catch((error) => {
console.log('Email sending failed!', error.message);
});
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server is running on port: ${PORT}`);
});
And finally, this is the code used to submit the form in my React ContactForm component:
const ContactForm = () => {
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
message: '',
});
const handleForm = (e) => {
e.preventDefault();
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
const submitForm = async (e) => {
e.preventDefault();
console.log(formData);
try {
const response = await fetch('http://localhost:3001/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ formData }),
});
const data = await response.json();
console.log('response data', data);
} catch (e) {
console.log('Error!', e);
}
};
Note: I have also tried adding "proxy": "http://localhost:3001" to the package.json file, but it didn't change anything.
Thanks in advance!