GaxiosError: unauthorized_client. React + Nodemailer + Google APIs

Viewed 407

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!

0 Answers
Related