I have been trying to solve this issue wherein, I can grab the accessToken after login only? I have been stuck on this
The main problem is, the Token is already looking for it on localstorage, but I'm not even logged in yet. How can I make the user to login first before looking to the local storage
requestMethod.js
import axios from 'axios'
const BASE_URL = 'http://localhost:5000/api'
const TOKEN = JSON.parse(JSON.parse(localStorage.getItem('persist:root')).user)
.currentUser.accessToken
export const publicRequest = axios.create({
baseURL: BASE_URL,
})
export const userRequest = axios.create({
baseURL: BASE_URL,
headers: { token: `Bearer ${TOKEN}` },
})
Login.jsx
import { useEffect } from "react"
import { useState } from "react"
import { useDispatch } from "react-redux"
import { useHistory } from "react-router"
import { login } from "../../redux/apiCalls"
import { userRequest } from "../../requestMethod"
const Login = () => {
let history = useHistory()
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const dispatch = useDispatch()
const handleClick = (e) =>{
e.preventDefault()
login(dispatch,{username,password})
history.push('/home')
}
useEffect(() =>{
try {
} catch (error) {
}
},[])
return (
<div style={{display: 'flex', alignItems:"center", justifyContent:"center", height: '100vh', flexDirection: "column"}}>
<form action="">
<input style={{padding: 10, marginBottom:20}} value={username} type="text" placeholder="username" onChange={e => setUsername(e.target.value)} />
<input style={{padding: 10, marginBottom:20}} value={password} type="password" placeholder="password" onChange={e => setPassword(e.target.value)} />
<button style={{padding: 10, width: 100}} onClick={handleClick}>Login</button>
</form>
</div>
)
}
export default Login
apicalls.js
import { loginFailure, loginStart, loginSuccess } from './userRedux'
export const login = async (dispatch, user) => {
dispatch(loginStart())
try {
const res = await publicRequest.post('/auth/login', user)
const TOKEN = JSON.parse(
JSON.parse(localStorage.getItem('persist:root')).user
).currentUser.accessToken
dispatch(loginSuccess(res.data))
} catch (error) {
dispatch(loginFailure())
}
}
You have to lookup on localstorage first.
Just check if localstorage contains token or not. If not, render some message and redirect user to login page.
Use optional chaining(?.):
const TOKEN = JSON.parse(JSON.parse(localStorage.getItem('persist:root'))?.user)
?.currentUser?.accessToken;
Related
EDIT: Just saw that the post was very long so sorry and thanks a lot to whom can help me.
I'm using react as front in a project and I have a problem that I couldn't solve and didn't find anything on the internet.
So the problem is that I'm using jwt for login and when I log in on my page it's like it removes the state by himself, I'll show you:
(by the way I just started so there is no css)
login page:
logged in:
Then if I refresh the page or do anything like click on a button it will come back to the initial state like I never logged in and throw me to the login page because it doesn't see me logged in.
Here is the code:
authProvider.js:
import { createContext, useState } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState(null);
console.log(auth);
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
useAuth.js:
import { useContext } from "react";
import AuthContext from "../context/authProvider";
const useAuth = () => useContext(AuthContext);
export default useAuth;
login.js:
import React, { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import useAuth from '../../hooks/useAuth';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { auth, setAuth } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || '/';
const handleSubmit = (e) => {
e.preventDefault();
fetch('http://localhost:4000/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email,
password: password
})
}).then(res => res.json())
.then(data => {
if (data.message) {
document.querySelector('#error').innerHTML = data.message;
}
setAuth({ email, password, token: data.token, refreshToken: data.refreshToken, role: data.role });
console.log(auth);
navigate(from, { replace: true });
});
}
return (
<>
<h1>Login</h1>
<span id='error'></span>
<form onSubmit={handleSubmit}>
<label>Email</label>
<input id="email" type="email" required onChange={(e) => { setEmail(e.target.value) }} />
<label>Password</label>
<input id="password" type="password" required onChange={(e) => { setPassword(e.target.value) }} />
<button type='submit'>se connecter</button>
</form>
</>
);
};
export default Login;
the image for the logs:
and nav.js just so you know how I know it works but state doesn't stay:
import { Link } from 'react-router-dom';
import useAuth from '../hooks/useAuth';
const Nav = () => {
const { auth } = useAuth();
return (
<>
<nav>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
{
!auth ?
<>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/signup">Signup</Link>
</li>
</>
: null
}
{
auth?.role === 'user' ?
<li>
<Link to="/profile">Profile</Link>
</li>
: null
}
{
auth?.role === 'admin' ?
<li>
<Link to="/admin">Admin</Link>
</li>
: null
}
</ul>
</div>
</nav>
</>
)
}
export default Nav;
I don't know if you need more but just say me if needed.
Trying to POST request to send data to database but it always shows 400 bad request.
The AuthReducer file is as follow. Data is not getting posted when done in Frontend but its properly working when tested in postman api.
export const Register = (username, email , password) => async(disptach) => {
try{
disptach({type:REGISTER_USER_REQUEST})
const config = {
Headers:{
'Content-Type':'application/json'
}
}
const { data } = await axios.post('/EShop/register', {username ,email , password} , config)
disptach({
type:REGISTER_USER_SUCCESS,
payload:data.user
})
}catch(error){
disptach({
type:REGISTER_USER_FAIL,
payload : error.response.data.message
})
}
}
The register form is as follow..
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"
import { useSelector, useDispatch } from "react-redux"
import Loader from "./loader";
import { Register } from "../actions/UserAction";
import "../css-files/Account.css"
export default function register() {
const [email, setemail] = useState('');
const [password, setpassword] = useState('');
const [username, setusername] = useState('');
const disptach = useDispatch();
const navigate = useNavigate();
const { isAuthenticated, error, loading } = useSelector(state => state.user)
useEffect(() => {
if (isAuthenticated) {
navigate('/account/login')
}
}, [disptach, isAuthenticated])
const SubmitRegisterHandler = (e) => {
e.preventDefault()
disptach(Register(username, email, password))
}
return (
<div id="Account_container">
{loading ?
<Loader />
:
<div id="wrapper">
<div id="Wrap_First">
<img src="https://res.cloudinary.com/dqxozrie1/image/upload/v1659935744/eshop/online-registration-sign-up_gfb2gs.webp" />
</div>
<div id="Wrap_Second">
<div id="login">
<h2>Create New Account</h2>
<form onSubmit={SubmitRegisterHandler}>
<input
required
type="text"
placeholder="Username"
value={username}
onChange={(e) => { setusername(e.target.value) }}
/>
<input
required
type="email"
placeholder="Email"
value={email}
onChange={(e) => { setemail(e.target.value) }}
/>
<input
required
type="password"
placeholder="Password"
value={password}
onChange={(e) => { setpassword(e.target.value) }}
/>
<button>Register</button>
</form>
<p id="existing_acc"><Link to="/account/login">Already have an account !</Link></p>
</div>
</div>
</div>
}
</div>
);
}
It gives 400 bad request always. Please help me how to resolve this issue.
thanks for your patience. I'm implementing Firebase on my site but when I call the signup function in FormUp.js (declared in AuthContext.js) it doesn't refer to the function definition. This causes the function called in FormUp.js, not to call its own function defined in AuthContext.js, but falls into the catch branch ('Failed to create an account'). I don't understand why. Hope someone can help me, thanks!
Error:
TypeError: _firebase__WEBPACK_IMPORTED_MODULE_1__.auth.createUserWithEmailAndPassword is not a function
at signup (bundle.js:4226:56)
at handleSubmit (bundle.js:2575:13)
at HTMLUnknownElement.callCallback (bundle.js:41062:18)
at Object.invokeGuardedCallbackDev (bundle.js:41111:20)
at invokeGuardedCallback (bundle.js:41171:35)
at invokeGuardedCallbackAndCatchFirstError (bundle.js:41186:29)
at executeDispatch (bundle.js:45421:7)
at processDispatchQueueItemsInOrder (bundle.js:45453:11)
at processDispatchQueue (bundle.js:45466:9)
at dispatchEventsForPlugins (bundle.js:45477:7)
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
at Navbar (http://localhost:3000/static/js/bundle.js:3361:76)
at Home
Code:
AuthContext.js
import React, { useContext, useState, useEffect } from "react";
import { auth } from "../firebase";
import { createUserWithEmailAndPassword } from "firebase/auth";
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password);
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password);
}
function logout() {
return auth.signOut();
}
function resetPassword(email) {
return auth.sendPasswordResetEmail(email);
}
function updateEmail(email) {
return currentUser.updateEmail(email);
}
function updatePassword(password) {
return currentUser.updatePassword(password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
//Hidden
};
const app = initializeApp(firebaseConfig);
const auth = getAuth();
export { app, auth };
FormUp.js
import React, { useRef, useState } from "react";
import { Link } from "react-router-dom";
import {
FormUser,
Input,
Label,
Subtitle,
TextWrapper,
TopLine,
FormButton,
Credentials,
HomePage,
SignInLink,
SignInText,
RedirectSignIn,
Credential,
} from "./Form.elements";
import { FaAngleLeft } from "react-icons/fa";
import { useAuth } from "../../contexts/AuthContext";
import { Alert } from "bootstrap";
const FormUp = ({
primary,
lightBg,
lightTopLine,
lightTextDesc,
buttonLabel,
description,
topLine,
}) => {
const emailRef = useRef();
const passwordRef = useRef();
const passwordConfirmRef = useRef();
const { signup } = useAuth();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Password do not match");
}
try {
setError("");
setLoading(true);
await signup(emailRef.current.value, passwordRef.current.value);
} catch {
setError("Failed to create an account");
}
setLoading(false);
}
return (
<>
<style>
#import
url('https://fonts.googleapis.com/css2?family=Poppins:wght#100;200;300;400;500;600;700;800;900&display=swap');
</style>
<FormUser lightBg={lightBg} onSubmit={handleSubmit}>
<TextWrapper>
<HomePage href="/">
<FaAngleLeft />
<TopLine lightTopLine={lightTopLine}>{topLine}</TopLine>
</HomePage>
<Subtitle lightTextDesc={lightTextDesc}>{description}</Subtitle>
{error && (
<h5 style={{ color: "red", paddingBottom: "30px" }}>{error}</h5>
)}
<Credentials>
<Credential id="email">
<Label>Email</Label>
<Input
ref={emailRef}
required
type="email"
placeholder="Email..."
></Input>
</Credential>
<Credential id="password">
<Label>Password</Label>
<Input
ref={passwordRef}
required
type="password"
placeholder="Password..."
></Input>
</Credential>
<Credential id="password-confirm">
<Label>Password confirmation</Label>
<Input
ref={passwordConfirmRef}
required
type="password"
placeholder="Password confirmation..."
></Input>
</Credential>
</Credentials>
<FormButton disabled={loading} type="submit" big primary={primary}>
{buttonLabel}
</FormButton>
<RedirectSignIn>
<SignInText>Already have an account?</SignInText>
<SignInLink href="/sign-in">Log in</SignInLink>
</RedirectSignIn>
</TextWrapper>
</FormUser>
</>
);
};
export default FormUp;
So this problem I am facing for quite some time. Basically Registering user work to some extent. This is what is happening, upon the user being created and me getting the Auth token displayed in the dev-tools/Application and me being pushed to the home as I should.
When I refresh the page I get this error × Error: A cross-origin error was thrown. React doesn't have access to the actual error object in development. See https://reactjs.org/link/crossorigin-error for more information. I can fix this by clearing the localStorage. So if every time a new user is created, and if it wants to refresh the page, I will have to clear local storage every time. Which is totally not convenient.
There is another error, The logout handler is bugging, when I press the logout button it is displaying this error TypeError: Cannot read property 'push' of undefined and it pointing out the error is located in the UserContext. I would really appreciate some help really.
User Context
import React from "react";
import { useHistory } from "react-router-dom"
const UserContext = React.createContext();
function getUserFromLocalStorage() {
return localStorage.getItem("authToken")
? JSON.parse(localStorage.getItem("authToken"))
: { username: null, token: null };
}
function UserProvider({ children }) {
const [user, setUser] = React.useState(getUserFromLocalStorage());
const routerHistory = useHistory()
const logoutHandler = () =>{
localStorage.removeItem("authToken");
setUser(user);
routerHistory.push("/")
}
return (
<UserContext.Provider
value={{ user, setUser, logoutHandler }}
>
{children}
</UserContext.Provider>
);
}
export { UserContext, UserProvider };
Login
import React, { useState, } from "react";
import { useHistory } from "react-router-dom"
import axios from "axios";
import { Link } from "react-router-dom";
import "./Signin.css";
import { UserContext } from "../../context/user";
const Login = () => {
const { user, setUser } = React.useContext(UserContext);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const history = useHistory()
const loginHandler = async (e) => {
e.preventDefault();
const config = {
header: {
"Content-Type": "application/json",
},
};
try {
const { data } = await axios.post(
"http://localhost:5000/api/auth/login",
{ email, password },
config
);
user.authToken = data.token;
setUser(user)
history.push("/");
} catch (error) {
if (error.response) {
setError(error.response.data.error);
}
setTimeout(() => {
setError("");
}, 5000);
}
};
return (
<div className="signin">
<Link to='/'>
<img src='/audible/logo.png' />
</Link>
<form onSubmit={loginHandler} className="form__container">
<h3>Sign in with your Amazon account</h3>
{error && <span className="error__message">{error}</span>}
<span className='labels'>Email </span>
<input
className="input__field"
type="email"
required
id="email"
onChange={(e) => setEmail(e.target.value)}
value={email}
tabIndex={1}
/>
<span className='signin__forgot'>Password{" "}
<Link className='links' to="/forgotpassword" >
Forgot Password?
</Link>
</span>
<input
className="input__field"
type="password"
required
id="password"
autoComplete="true"
onChange={(e) => setPassword(e.target.value)}
value={password}
tabIndex={2}
/>
<button type="submit" className="signin__btn">
Sign In
</button>
<span className='new__sign'>New to Amazon? </span>
<Link to="/register">
<button className='signin__register'>
Create your amazon account
</button>
</Link>
</form>
</div>
);
};
export default Login;
Register
import { useState, } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom"
const Register = () => {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmpassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
const routerHistory = useHistory()
const registerHandler = async (e) => {
e.preventDefault();
const config = {
header: {
"Content-Type": "application/json",
},
};
if (password !== confirmpassword) {
setPassword("");
setConfirmPassword("");
setTimeout(() => {
setError("");
}, 5000);
return setError("Passwords do not match");
}
try {
const { data } = await axios.post(
"http://localhost:5000/api/auth/register",
{
username,
email,
password,
},
config
);
localStorage.setItem("authToken", data.token);
routerHistory.push('/login')
} catch (error) {
if (error.response) {
setError(error.response.data.error);
}
setTimeout(() => {
setError("");
}, 5000);
}
};
return (
<div className="signin">
<Link to='/'>
<img src='/audible/logo.png' />
</Link>
<form onSubmit={registerHandler} className="form__container">
<h3>Create account</h3>
{error && <span className="error__message">{error}</span>}
<span className='labels'>Username</span>
<input
type="text"
className="input__field"
required
id="name"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<span className='labels'>Email</span>
<input
type="email"
className="input__field"
required
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<span className='labels'>Password</span>
<input
type="password"
required
id="password"
className="input__field"
autoComplete="true"
placeholder="At least 6 characters"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<span className='labels'>Re-enter password</span>
<input
type="password"
required
id="confirmpassword"
className="input__field"
autoComplete="true"
value={confirmpassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<button type="submit" className="register__btn">
Create your Amazon account
</button>
<span >
Already have an account? <Link className='links' to="/login">Login</Link>
</span>
</form>
</div>
);
};
export default Register;
Login Link
import React from "react";
import { Link, useHistory } from "react-router-dom";
import { CartContext } from "../../context/cart";
import { UserContext } from "../../context/user";
export default function LoginLink() {
const { user, logoutHandler } = React.useContext(UserContext);
const { clearCart } = React.useContext(CartContext);
if (user.authToken) {
return (
<button
onClick={() => {
logoutHandler();
clearCart()
}}
className="login-btn"
>
logout
</button>
);
}
return <Link to="/login">Signin</Link>;
}
My app calls a Login api and returns me a token,i stored the token in localStorage so my problem is how i validade if the user has a token to do the login. what can i do to do that?
this is my login Page where i used to add the token to the localStorage
import React, { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { login } from '../services/login.services';
function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const history = useHistory();
const loginHandler = async () => {
const result = await login(email, password);
console.log(result);
localStorage.setItem('token', result?.response?.result?.token);
localStorage.getItem('token');
};
return (
<section className='layout'>
<div className='wrp-login'>
<div className='container'>
<h1 color='white'>Login Page</h1>
<div className='col-sm-6 offset-sm-3'>
<input
type='text'
placeholder='email'
onChange={(e) => setEmail(e.target.value)}
className='input-wrapper'
/>
<br />
<input
type='password'
placeholder='password'
onChange={(e) => setPassword(e.target.value)}
className='input-wrapper'
/>
<br />
<button onClick={() => loginHandler()} className='button'>
Login
</button>
</div>
</div>
</div>
</section>
);}export default Login;
this is my Login Service wher i do the api call and return data
export const login = async (email, password) => {
try {
const result = await fetch(
'teste.com',
{
method: 'Post',
body: JSON.stringify({ login: email, senha: password }),
headers: {
'Content-Type': 'application/json',
},
mode: 'cors',
cache: 'default',
},
);
return await result.json();
} catch (err) {
return err;
}};
Someone could help me?
You can validate and redirect users by creating a wrapper for protected routes with react-router-dom to the login page if they don't have the token stored this way:
const ProtectedRoute = (props) => {
const token = localStorage.getItem('token');
if (token == null) {
return <Redirect to={Routes.LOGIN} />;
}
return <>{props.children}</>;
};