403 Forbidden on API requests

Issue

I am following a guide on a full-stack login/auth app. It’s originally written with class components and I’m recreating it with functional components/hooks. I can successfully post requests with Postman but not via React/redux. I understand this could be a cors issue but I’ve tried implementing cors and it just isn’t working. I’ve also tried adding headers to some of my axios posts with no luck either. Here’s what some of it looks like right now

authActions.js -> my function for post request to register a user

...
axios.defaults.headers.post['Accept'] = 'application/json';
axios.defaults.headers.post['Content-Type'] = 'application/json';

//register user
export const registerUser = (userData, history) => (dispatch) => {
    axios.post('/api/users/register', userData).then(res => history.push('/login')).catch((err)=>{
        dispatch({
            type: GET_ERRORS,
            payload: err.response.data
        })
    });
};
...

server.js

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
require('dotenv').config();
const passport = require('passport');
const cors = require('cors');

const users = require('./routes/api/users');

const app = express();

app.use(cors());

//body-parser middleware
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());

//connect to db
const URI = process.env.URI;
mongoose.connect(URI, {useNewUrlParser: true}).then(()=>console.log('MongoDB connected successfully')).catch(err => console.log(err));

//Passport middleware
app.use(passport.initialize());

//Passport config
require("./config/passport")(passport);

//Routes
app.use('/api/users', users);

//Print to server that its listening at given port
const port = process.env.PORT;
app.listen(port, ()=> console.log(`Server listening on port ${port}`));

users.js
I’ve tried adding cors with different options in this post request here as well. also tried router.use(cors()) with no luck.

...
router.post('/register', (req, res)=>{
    const {errors, isValid} = validateRegisterInput(req.body);

    //check validation
    if(!isValid){
        return res.status(400).json(errors);
    }

    User.findOne({email: req.body.email}).then(user => {
        if(user){
            return res.status(400).json({email: "Email already exists"});
        } else {
            const newUser = new User({
                name: req.body.name,
                email: req.body.email,
                password: req.body.password
            });

            //hash password before saving to database
            bcrypt.genSalt(10, (err, salt)=>{
                bcrypt.hash(newUser.password, salt, (err, hash)=>{
                    if(err) throw err;
                    newUser.password = hash;
                    newUser.save().then(user=>res.json(user)).catch(err=>console.log(err));
                });
            });
        }
    });
});
...

passport.js

const jwtStrategy = require('passport-jwt').Strategy;
const extractJwt = require('passport-jwt').ExtractJwt;
const mongoose = require('mongoose');
const user = mongoose.model('User');
require('dotenv').config();

const options = {};

options.jwtFromRequest = extractJwt.fromAuthHeaderAsBearerToken();
options.secretOrKey = process.env.secretKey;

module.exports = passport => {
    passport.use(
        new jwtStrategy(options, (jwt_payload, done)=>{
            user.findById(jwt_payload.id).then(user=>{
                if(user){
                    return done(null, user);
                }
                return done(null, false);
            }).catch(err => console.log(err));
        })
    )
};

Thank you!

I think it may be the way I’m writing these functional components, I’m still learning how to do this. I think it may have something to do with my props and my store

function withRouter(Component){
    function ComponentWithRouterProp(props){
        let location = useLocation();
        let navigate = useNavigate();
        let params = useParams();
        return(
            <Component
            {...props} router={{location, navigate, params}}
            />
        );
    }
    return ComponentWithRouterProp;
}

const Register = (props) =>{
    const [state, setState] = useState({
        name: "",
        email: "",
        password: "",
        password2: "",
        errors: {}
    });

    useEffect(()=>{
        if(props.errors){
            setState({
                errors: props.errors
            })
        }
    }, [props])

    useEffect(()=>{
        if(props.auth.isAuthenticated){
            props.history.push('/dashboard');
        }
    }, [props]);

    const onChange = (e) =>{
        setState({
            ...state,
            [e.target.name]: e.target.value
        });
    }

    const submit = (e) =>{
        e.preventDefault();

        const newUser = {
            name: state.name,
            email: state.email,
            password: state.password,
            password2: state.password2
        };
        
        props.registerUser(newUser, props.history)

        console.log(newUser);
    };

    return(
        <div className='container'>
            <div className='row'>
                <div className='col s8 offset-s2'>
                    <Link to="/" className='btn-flat waves-effect'>
                        <i className='material-icons left'>keyboard_backspace</i>Back to home
                    </Link>
                    <div className='col s12'>
                        <h4>
                            <b>Register</b> below
                        </h4>
                        <p className='grey-text text-darken-1'>
                            Already have an account? <Link to="/login">Log in</Link>
                        </p>
                    </div>

                    <form noValidate onSubmit={submit}>
                        <div className='input-field col s12'>
                            <input name="name" className={classnames("", {invalid: props.errors.name})} onChange={onChange} defaultValue={state.name} error={state.errors.name} id="name" type="text" />
                            <label htmlFor='name'>Name</label>
                            <span className="red-text">{props.errors.name}</span>
                        </div>

                        <div className='input-field col s12'>
                            <input name="email" className={classnames("", {invalid: props.errors.email})} onChange={onChange} defaultValue={state.email} error={state.errors.email} id="email" type="text" />
                            <label htmlFor='email'>Email</label>
                            <span className="red-text">{props.errors.email}</span>
                        </div>

                        <div className='input-field col s12'>
                            <input name="password" className={classnames("", {invalid: props.errors.password})} onChange={onChange} defaultValue={state.password} error={state.errors.password} id="password" type="password" />
                            <label htmlFor='password'>Password</label>
                            <span className="red-text">{props.errors.password}</span>
                        </div>

                        <div className='input-field col s12'>
                            <input name="password2" className={classnames("", {invalid: props.errors.password2})} onChange={onChange} defaultValue={state.password2} error={state.errors.password2} id="password2" type="password" />
                            <label htmlFor='password2'>Confirm Password</label>
                            <span className="red-text">{props.errors.password2}</span>
                        </div>

                        <div className='col s12'>
                            <button type='submit' className='btn btn-large waves-effect waves-light hoverable blue accent-3'>Sign up</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    )
const mapStateToProps = state => ({
    auth: state.auth,
    errors: state.errors
});

export default connect(
    mapStateToProps,
    { registerUser })(withRouter(Register));

Solution

I was getting a 403 Forbidden error because my proxy wasn’t set up properly. I initially had it set up for port 5000 in my client’s package.json proxy but when I had run my server on 5000, it told me I already had 5000 in use. I changed it in my .env file but not in my proxy settings in my client’s package.json. Once I changed it to match my server I was able to successfully post

Answered By – cnelson720

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published