Cannot upload image to Imgur API from render frontend

Hi guys,

I made a React + Express + mySQL website. I got it perfectly working on pre-production, however I am now having some problems to get my website correctly run on Render. The error happens when registering the a new user. I want that the user can upload a picture. The picture is stored on the Imgur API and the url is sent to mySQL database. The issue I face is the following:

Access to XMLHttpRequest at 'https://api.imgur.com/3/image' from origin 'https://proyectoescrache.onrender.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Register.js:40 Wt {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}
(anonymous) @ Register.js:40
await in (anonymous) (async)
onClick @ Register.js:49
De @ react-dom.production.min.js:54
ze @ react-dom.production.min.js:54
(anonymous) @ react-dom.production.min.js:55
qr @ react-dom.production.min.js:105
Ir @ react-dom.production.min.js:106
(anonymous) @ react-dom.production.min.js:117
cu @ react-dom.production.min.js:274
Pe @ react-dom.production.min.js:52
Yr @ react-dom.production.min.js:109
Gt @ react-dom.production.min.js:74
Wt @ react-dom.production.min.js:73
Show 11 more frames
Show less
Register.js:31 
        
        
       POST https://api.imgur.com/3/image net::ERR_FAILED
(anonymous) @ xhr.js:258
xhr @ xhr.js:49
Un @ dispatchRequest.js:51
_request @ Axios.js:170
request @ Axios.js:40
(anonymous) @ Axios.js:209
(anonymous) @ bind.js:5
(anonymous) @ Register.js:31
onClick @ Register.js:49
De @ react-dom.production.min.js:54
ze @ react-dom.production.min.js:54
(anonymous) @ react-dom.production.min.js:55
qr @ react-dom.production.min.js:105
Ir @ react-dom.production.min.js:106
(anonymous) @ react-dom.production.min.js:117
cu @ react-dom.production.min.js:274
Pe @ react-dom.production.min.js:52
Yr @ react-dom.production.min.js:109
Gt @ react-dom.production.min.js:74
Wt @ react-dom.production.min.js:73
Show 18 more frames
Show less
Register.js:62 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'data')
    at onClick (Register.js:62:37)

This is my code. First, the frontend for registering:

import pe from "../images/login/pe.jpg";
import { useState } from "react"
import axios from "axios"
import { useNavigate } from "react-router-dom"

export default function Register() {

    const [inputs, setInputs] = useState({
        username:"",
        email:"",
        password:"",
        img: null,
    });
    
    const [error, setError] = useState(null);
    const navigate = useNavigate();
    
    const handleChange = (e) => {
        if (e.target.name === "image") {
            setInputs({ ...inputs, img: e.target.files[0] });
        } else {
            setInputs({ ...inputs, [e.target.name]: e.target.value });
        }
    };

    const uploadImg = async (image) => {
        const formData = new FormData();
        formData.append("image", image);
        
        try {
            const response = await axios.post("https://api.imgur.com/3/image", formData, {
                headers: {
                    Authorization: `Client-ID ${process.env.REACT_APP_IMGUR_CLIENT_ID}`,
                    Accept: "application/json",
                },
            });
            
            return response.data.data.link;
        } catch (error) {
            console.error(error);
            throw new Error("Failed to upload image to Imgur.");
        }
    };
    
    const handleSubmit = async (e) => {
        e.preventDefault();

        try {
            const imgUrl = await uploadImg(inputs.img);
            
            const formData = new FormData();
            formData.append("username", inputs.username);
            formData.append("email", inputs.email);
            formData.append("password", inputs.password);
            formData.append("image", imgUrl);
            
            const response = await axios.post("https://proyecto-escrache.onrender.com/api/auth/register", formData);
            if (response.data) {
                navigate("/login");
            }
        } catch (error) {
            setError(error.response.data);
        }
    };
    
    return (
        <div className="signup-container">
            <form className="signupinfo-container">
                <img src={pe} alt="log in" />
                <input type="text" placeholder="Enter username" required name="username" onChange={handleChange}/>
                <input type="email" placeholder="Enter email" required name="email" onChange={handleChange}/>
                <input type="password" placeholder="Enter password" required name="password" onChange={handleChange}/>
                <input type="file" required name="image"  onChange={handleChange}/>
                <button onClick={handleSubmit} type="submit">Create a new account</button>
                {error && <p>{error}</p>}
            </form>
        </div>

This is the index.js in my express backend:

const express = require("express");
const cors = require("cors");
require("dotenv/config");
const postRoutes = require("./routes/posts");
const authRoutes = require("./routes/auth");
const cookieParser = require("cookie-parser");
const contactRoutes = require("./routes/contact");
const liveRoutes = require("./routes/live");
const fileUpload = require("express-fileupload")



const app = express();

app.use(cors({
  origin: ["https://proyectoescrache.onrender.com", "https://api.imgur.com/3/image", "http://localhost:3000"],
  credentials: true,
  exposedHeaders: ["Access-Control-Allow-Origin"]
}));
app.use(cookieParser());
app.use(express.json());
app.use(fileUpload())

app.use("/api/auth", authRoutes);
app.use("/api/posts", postRoutes);
app.use("/api/contact", contactRoutes);
app.use("/api/live", liveRoutes);


const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
  console.log("Server is running on port", PORT);
});

And this is the controllers for the registering in the backend:

const db = require("../db");
const bcrypt = require("bcrypt")
const jwt = require("jsonwebtoken")
const nodemailer = require("nodemailer");
const imgur = require('imgur');
const fs = require('fs');
const axios = require("axios")


const register = async (req, res) => {
    try {
        const { username, email, password, image } = req.body;

        const checkQ = "SELECT * FROM users WHERE email=? OR username = ?";
        db.query(checkQ, [email, username], (err, data) => {
            if (err) return res.status(500).json({ error: err });

            if (data.length) return res.status(409).json("User already exists");

            const salt = bcrypt.genSaltSync(10);
            const hash = bcrypt.hashSync(password, salt);

            const insertQ = "INSERT INTO users(`username`, `email`, `password`, `img`) VALUES (?, ?, ?, ?)";
            const values = [username, email, hash, image];

            db.query(insertQ, values, (err, data) => {
                if (err) return res.status(500).json({ error: err });
                return res.status(200).json("User has been created successfully");
            });
        });
    } catch (error) {
        console.error(error);
        return res.status(500).json({ error: 'Failed to upload image to Imgur.' });
    }
};

const login = (req, res) => {

    const q = "SELECT * FROM users WHERE username = ?";
    db.query(q, [req.body.username], (err, data) => {
        if (err) return res.status(500).json(err);
        
        if (data.length === 0) {
            return res.status(404).json("User not found. Please register first!");
        }

        const isPasswordCorrect = bcrypt.compareSync(req.body.password, data[0].password);
        if (!isPasswordCorrect) return res.status(400).json("Incorrect password. Please, try again!");

        const token = jwt.sign({ id: data[0].id }, process.env.JWT_KEY);
        const { password, ...other } = data[0];

        res.cookie("access_token", token, { httpOnly: true }).status(200).json(other);
    });
};


const logout = (req, res) => {
    res.clearCookie("access_token", {
        sameSite:"none",
        secure:true
    }).status(200).json("User has been logout")
}

const forgotPassword = (req, res) => {

    const q = "SELECT * FROM users WHERE email = ?";
    db.query(q, [req.body.email], (err, data) => {
        if (err) return res.status(500).json(err);

        if (data.length === 0) {
            return res.status(404).json("User not found. Please register first!");
        }

        const resetToken = jwt.sign({ email: req.body.email }, process.env.RESET_TOKEN_SECRET, { expiresIn: "1h" });

        const transporter = nodemailer.createTransport({
            service: "gmail",
            auth: {
                user: process.env.EMAIL,
                pass: process.env.PASSWORD,
            },
        });

        const mailOptions = {
            from: process.env.EMAIL,
            to: req.body.email,
            subject: "Password Reset Request",
            html: `
                <p>Hello ${data[0].username},</p>
                <p>You requested a password reset. Click the link below to reset your password:</p>
                <a href="https://proyectoescrache.onrender.com/reset-password?token=${resetToken}">Reset Password</a>
            `,
        };

        transporter.sendMail(mailOptions, (error, info) => {
            if (error) {
                console.log(error);
                return res.status(500).json("Failed to send reset email.");
            }
            console.log("Email sent: " + info.response);
            return res.status(200).json("Password reset email sent successfully.");
        });
    });
};

const resetPassword = (req, res) => {
    const { token, password } = req.body;

    jwt.verify(token, process.env.RESET_TOKEN_SECRET, async (err, decodedToken) => {
        if (err) {
            return res.status(400).json("Invalid or expired token. Please try again.");
        }

        const salt = bcrypt.genSaltSync(10);
        const hashedPassword = bcrypt.hashSync(password, salt);

        const updateQuery = "UPDATE users SET password = ? WHERE email = ?";
        db.query(updateQuery, [hashedPassword, decodedToken.email], (updateErr, updateResult) => {
            if (updateErr) {
                return res.status(500).json({ error: "Failed to send reset email.", details: error.message });
            }
            return res.status(200).json("Password updated successfully.");
        });
    });
};

module.exports = { register, login, logout, forgotPassword, resetPassword};

I have been working on these issues for uploading images to Imgur API during the last 3 days and I am desperate for some help. Would appreaciate any help!

Thanks

Hi,

The first message in your logs appears to point to the issue:

Access to XMLHttpRequest at 'https://api.imgur.com/3/image' from origin 'https://proyectoescrache.onrender.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

The Imgur API has Access-Control-Allow-Origin set to * according to their docs: Imgur API

So, requesting with credentials will be blocked, as the message says.

Alan

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.