Search code examples
node.jsreactjsfrontendpostman

401 Authorization on API


I'm new to react and Node.js and I have a problem, I hope you can help me. I made some apis and some of them need to be authenticated others not. In Postman when I authenticate everything works fine, I can call the APIs that require authentication without any problem. Except that in my frontend, when I authenticate to my LoginPage page, it works fine, but when I try, for example, to use the API to create a project (which requires authentication), I get a 401 Unauthorized, and the same goes for all the APIs that require authentication, even though I'm logged in. I don't understand why.

Here is my LoginPage

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { setLoggedIn } from '../store';

const LoginPage = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [showPassword, setShowPassword] = useState(false);
  const [error, setError] = useState('');
  const navigate = useNavigate();
  const dispatch = useDispatch();

  const handleEmailChange = (e) => {
    setEmail(e.target.value);
  };

  const handlePasswordChange = (e) => {
    setPassword(e.target.value);
  };

  const togglePasswordVisibility = () => {
    setShowPassword(!showPassword);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    const formData = {
      email,
      password
    };

    try {
      const response = await fetch('http://localhost:5000/user/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(formData)
      });

      if (response.ok) {
        const data = await response.json();
        console.log(data.message);
        setError(data.message);
        dispatch(setLoggedIn(true)); // Mise à jour de l'état de connexion
      
        // Stocker les informations de l'utilisateur dans le localStorage
        localStorage.setItem('user', JSON.stringify(data.user));
      
        navigate(`${process.env.PUBLIC_URL}`);
        // Redirigez l'utilisateur vers la page d'accueil ici
      }
      
    } catch (error) {
      console.log(error.message);
      setError(error.message);
      // Traitez les erreurs d'exception ici
    }
  };

  return (
    <div className="container-fluid px-1 py-5 mx-auto">
      <div className="row d-flex justify-content-center">
        <div className="col-xl-7 col-lg-8 col-md-9 col-11 text-center">
          <div className="card" style={{ maxWidth: '400px', margin: '0 auto' }}>
            <h5 className="text-center mb-4">Login</h5>
            <form className="form-card" onSubmit={handleSubmit}>
              <div className="row justify-content-center text-center">
                <div className="form-group col-md-12 flex-column d-flex">
                  <label className="form-control-label px-3">Email<span className="text-danger"> *</span></label>
                  <input type="text" id="email" name="email" placeholder="Enter your email" value={email} onChange={handleEmailChange} />
                </div>
              </div>
              <div className="row justify-content-center text-center">
                <div className="form-group col-md-12 flex-column d-flex">
                  <label className="form-control-label px-3">Password<span className="text-danger"> *</span></label>
                  <div className="password-input-wrapper d-flex align-items-center">
                    <input type={showPassword ? 'text' : 'password'} id="password" name="password" placeholder="Enter your password" value={password} onChange={handlePasswordChange} className="form-control" />
                    <button type="button" id="togglePassword" className="toggle-password-btn" onClick={togglePasswordVisibility}>
                      <i className="fas fa-eye"></i>
                    </button>
                  </div>
                </div>
              </div>
              <div className="row justify-content-center">
                <div className="form-group col-md-12">
                  <button type="submit" className="btn-block btn-primary">Login</button>
                </div>
              </div>
            </form>
            {error && <div className="alert alert-info" role="alert">{error}</div>}
          </div>
        </div>
      </div>
    </div>
  );
};

export default LoginPage;

Here is my CreateProject

import React from 'react';
import HeaderOne from '../common/header/HeaderOne';
import Breadcrumb from '../common/breadcrumb/Breadcrumb';
import TeamDetailsContent from '../components/team-details/TeamDetailsContent';
import FooterOne from '../common/footer/FooterOne';

const CreateProject = () => {
    return (
        <>
            <HeaderOne />
            <Breadcrumb 
                heading="Créer un projet"
                currentPage="Create Project" 
            />
            <TeamDetailsContent />
            <FooterOne />
        </>
    )
}

export default CreateProject;

And his components where I fetch my Apis

import React from 'react';
import { useNavigate } from 'react-router-dom';

export default class TeamDetailsContent extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault();

    const formData = new FormData(event.target);

    // Récupérer les informations d'utilisateur du localStorage
    const user = JSON.parse(localStorage.getItem('user'));

    // Ajouter les informations d'email et de mot de passe à formData
    formData.append('email', user.email);
    formData.append('password', user.password);

    fetch('http://localhost:5000/project/create', {
      method: 'POST',
      body: formData,
      credentials: 'include',
    })
      .then((response) => response.json())
      .then((data) => {
        console.log(data); // Gérer la réponse de l'API ici
      })
      .catch((error) => {
        console.error('Error creating project:', error);
      });
  };

  render() {
    let publicUrl = process.env.PUBLIC_URL + '/';
    return (
      <>
        <section className="join-team">
          <div className="container">
            <div className="row">
              <div className="col-xl-6 col-lg-6">
                <div className="join-team__Left">
                  <div className="join-team__images">
                    <div className="row">
                      <div className="col-xl-6 col-lg-6 col-md-6">
                        <div className="join-team__img-single">
                          <img src={publicUrl + 'assets/images/team/join-team-img-1.jpg'} alt="" />
                        </div>
                      </div>
                      <div className="col-xl-6 col-lg-6 col-md-6">
                        <div className="join-team__img-single">
                          <img src={publicUrl + 'assets/images/team/join-team-img-2.jpg'} alt="" />
                        </div>
                      </div>
                    </div>
                  </div>
                  <div className="join-team__content">
                    <h3 className="join-team__title">Requirements</h3>
                    <p className="join-team__text">
                      Aliquam hendrerit a augue insu image pellentes que id erat quis sollicitud null mattis Ipsum is
                      simply dummy typesetting industry. Alienum phaedrum torquatos nec eu, vis detraxit periculis ex,
                      nihil expetendis in meifn pericula euripidis.
                    </p>
                    <ul className="list-unstyled join-team__points">
                      <li>Nsectetur cing do not elit.</li>
                      <li>Suspe ndisse suscipit sagittis in leo.</li>
                      <li>Entum estibulum dignissim lipsm posuere.</li>
                    </ul>
                    <div className="join-team__contact">
                      <p>
                        <a href="tel:1307776-0608" className="join-team__phone">
                          + 1 (307) 776-0608
                        </a>
                        <a href="mailto:needhelp@company.com" className="join-team__email">
                          needhelp@company.com
                        </a>
                      </p>
                    </div>
                  </div>
                </div>
              </div>
              <div className="col-xl-6 col-lg-6">
                <div className="join-team__right">
                  <form className="join-team__form" onSubmit={this.handleSubmit}>
                    <div className="row">
                      <div className="col-xl-12">
                        <div className="join-team__input">
                          <input type="text" placeholder="Nom du projet" name="title" />
                        </div>
                      </div>
                      <div className="col-xl-12">
                        <div className="join-team__input">
                          <textarea type="text" placeholder="Description du projet" name="description"></textarea>
                        </div>
                      </div>
                      <div className="col-xl-12">
                        <div className="join-team__input">
                          <input type="text" placeholder="Somme à atteindre" name="targetAmount" />
                        </div>
                      </div>
                      <div className="col-xl-12">
                        <div className="join-team__input">
                          <input type="text" placeholder="Nombre de jours" name="duration" />
                        </div>
                      </div>
                      <div className="col-xl-6">
                        <div className="join-team__input">
                          <input type="text" placeholder="Catégorie" name="category" />
                        </div>
                      </div>
                      <div className="col-xl-12">
                        <div className="join-team__input">
                          <input type="file" accept="image/*" name="coverPhoto" />
                        </div>
                      </div>
                      <div className="col-xl-12">
                        <div className="join-team__btn-box">
                          <button type="submit" className="thm-btn join-team__btn">
                            Créer le projet
                          </button>
                        </div>
                      </div>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
        </section>
        {/* Join Team End */}
      </>
    );
  }
}

When I log on to LoginPage, I want to be able to access the other APIs that require authentication, because that's how I log on when I test them on Postman.


Solution

  • If you want to implement an OAuth2, you should consider that the main key for communication between server and client is token.

    In simple way when you call login API of your OAuth2 service, it provide a unique token for you that can be use as a key for authentication in future API calls.

    So you should get that token and attach it to header of each request for retrieving data in all APIs that need authentication.

    fetch('http://localhost:5000/project/create', {
          method: 'POST',
          body: formData,
          credentials: 'include',
          headers: {
          'Authorization': 'Bearer ' + token,
         }
        })