I am beginner with token authorization and I am trying to create login system with react. The endpoints seem to require the token to be accessed, so I am redirected to /home, which is currently not happening. I am using this api https://app.swaggerhub.com/apis/warp/etn-device-checker-test/1.0#/default/post_login
Login.js
import React from 'react'
import axios from 'axios';
import { useState } from 'react';
import { Redirect } from "react-router-dom";
function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const onSubmit = (e) => {
e.preventDefault();
const getIn = {
"login":email,
"password":password,
};
axios
.post('https://js-test-api.etnetera.cz/api/v1/login', getIn,
{
headers: {
authorization: 'Auth-Token',
'content-type': 'application/json',
}
})
.then((res) => {
console.log(res.data);
return <Redirect to="/home" />
})
.catch((error) => console.log(error));
};
return (
<div>
<form >
<label>email</label> <input value={email}
onChange={(e) => setEmail(e.target.value)} type="text"/>
<label>password</label> <input type="text" value={password}
onChange={(e) => setPassword(e.target.value)}/>
<button onClick={onSubmit}>login</button>
</form>
</div>
)
}
export default Login
App.js
import './App.css';
import Login from './Login';
import MobileList from './mobileList';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route>
<Login path='/' exact/>
</Route>
<Route>
<MobileList path='/home' />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
headers: {"Authorization" : `Bearer ${token}`}
Maybe this answer will help you.
how-to-pass-header-jwt-token-with-axios-react
But in login I don't think that you need a token,
so you can remove the authorization part there.
Related
I have problem with react-routerand useEffect. When I want to login user, useEffecshould take userData from cookie, and ˙navigate` user to different component. But it stuck.
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
setUserData(response.data);
});
};
useEffect(() => {
if (userData) {
navigate("/");
}
}, [userData]);
This is error in console.
I hope you can help me, if you need anything else just let me know.
This is my code from all components.
<Login />
import React, { useState, useEffect } from "react";
import Axios from "axios";
import { useLocation, useNavigate } from "react-router-dom";
import classes from "./Login.module.css";
const Login = ({ setUserData, userData }) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
const location = useLocation();
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
setUserData(response.data);
window.location.reload(true);
});
};
useEffect(() => {
if (userData) {
navigate("/");
}
}, [userData]);
return (
<div className={classes.container}>
<div className={classes.slika}>
<img src="./utilities/scientisc.svg" />
</div>
<div className={classes.login_content}>
<form onSubmit={login}>
<div className={classes.formBorder}>
<img className={classes.avatar} src="utilities/test.png" />
<h2 className={classes.title}>Welcome</h2>
<div /* className={`${classes.input_div} ${classes.one}`}
*/>
<div className={`${classes.inputDiv} ${classes.one}`}>
<h5 className={classes.label}>Korisničko ime</h5>
<input
onChange={(e) => {
setUsername(e.target.value);
}}
type="text"
className={classes.input}
/>
</div>
</div>
<div /* className={`${classes.input_div} ${classes.pass}`}
*/>
<div /* className="div" */>
<h5 className={classes.label}>Lozinka</h5>
<input
onChange={(e) => {
setPassword(e.target.value);
}}
type="password"
className={classes.input}
/>
</div>
</div>
<button type="submit" className={classes.button}>
Prijavi se
</button>
</div>
</form>
<h3 className={classes.labelUpozorenja}>{userData?.message}
</h3>
</div>
</div>
);
};
export default Login;
<App /> component
import "./App.css";
import { BrowserRouter as Router, Routes, Route } from "react-
router-
dom";
import Login from "./pages/Login";
import PrivateRoutes from "./utils/PrivateRoutes";
import { useState, useEffect } from "react";
import Axios from "axios";
import SideMenu from "./components/SideMenu/SideMenu";
function App() {
const [userData, setUserData] = useState();
const [checking, setChecking] = useState(true);
useEffect(() => {
Axios.get("http://localhost:3001/login")
.then((response) => {
if (response.data.loggedIn == true) {
setUserData(response.data);
}
return;
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setChecking(false);
});
}, []);
const handleClick = async () => {
try {
await Axios.post("http://localhost:3001/logout", {
name: "userId",
});
window.location.reload(true);
} catch (error) {
console.error(error);
}
};
return (
<div className="App">
<Router>
<Routes>
<Route
element={<PrivateRoutes userData={userData} checking=
{checking} />}
>
<Route element={<SideMenu handleClick={handleClick}
userData={userData} />} path="/" exact />
{/* <Route element={<Products />} path="/products" />
*/}
</Route>
<Route
element={<Login setUserData={setUserData} userData=
{userData} />}
path="/login"
/>
</Routes>
</Router>
</div>
);
}
export default App;
<PrivateRoutes />
import { Outlet, Navigate } from "react-router-dom";
import React from "react";
const PrivateRoutes = ({ userData, checking }) => {
return checking ? (
<p>Checking...</p>
) : userData?.loggedIn ? (
<Outlet />
) : (
<Navigate to="/login" />
);
};
export default PrivateRoutes;
You are using wrong approach for this, why arent you using navigate() in login ?
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
username: username,
password: password,
}).then((response) => {
navigate('/')
});
};
This will redirect the user after successful login.
UPDATE -
Noted that you said in refreshing case it would fail.
Why are you using that e in login? Login doesn't really need an Event I guess !
May be that (e) is preventing the app from refreshing the state! can you try without it ?
Only redirect if the path is /login. This will not cause an infinite loop because it will not redirect unless the path is /login
import { useLocation, useNavigate } from "react-router-dom";
const location = useLocation();
useEffect(() => {
if (userData && location.pathname === "/login") {
navigate("/");
}
}, [userData, location.pathname]);
Note: your login in path might be different, if so just swap out "/login" to the path you're using.
in react i have tried to implement a solution to forward values with context.
but my context doesn't work in another page(component)
this is my MainPage where user logins in login part
what i want is ;
i want to check login here and if login res != null then i want to set these res data into context values and use them in the bookPage
import React from "react";
import{ useContext, useEffect, useState } from "react";
import { TextField } from "#mui/material";
import { Button } from "#mui/material";
import "./Pages.css";
import { AccountContext } from "./AccountProvider";
import { useHistory } from "react-router-dom";
import axios from "axios";
function MainPage() {
const [email, setEmail1] = useState("");
const [password, setPassword1] = useState("");
const [name, setName1] = useState("");
const [loginemail, setloginEmail] = useState("");
const [loginpassword, setloginPassword] = useState("");
const [registeremail, setregisterEmail] = useState("");
const [registerpassword, setregisterPassword] = useState("");
const [registername, setregisterName] = useState("");
const {
Name,
Mail,
Password,
UID,
setName,
setPassword,
setMail,
setUID,
} = useContext(AccountContext);
let history = useHistory();
const [data,setData] = useState([]);
const LoginClick = () => {
setEmail1(loginemail);
setPassword1(loginpassword);
axios
.get( `http://localhost:5000/getLogin/?email=${email}&password=${password}`)
.then((resp) => {
setUID(resp.data[0]._id)
setName(resp.data[0].name)
setMail(resp.data[0].email)
setPassword(resp.data[0].password)
console.log(UID)
if (resp.data[0]._id != null) {
history.push("/BookPage")
history.go()
}
})
.catch((e) => {
alert(e);
});
};
const RegisterClick = async (e) => {
setEmail1(registeremail);
setName1(registername);
setPassword1(registerpassword);
console.log(email, password)
let res = await fetch("http://localhost:5000/registerUser", {
method: "post",
body: JSON.stringify({ name, email, password }),
headers: {
"Content-Type": "application/json",
},
});
res = await res.json();
console.warn(res);
if (res) {
alert("Registered success");
setName1("");
setPassword1("");
setEmail1("");
history.push("/BookPage");
}
if (!res) {
alert("registered failed");
}
};
return (
<>
<div class="main-page">
<h1>Welcome to the Library Management System!!! </h1>
<div class="user-form">
<div class="login-form">
<h2>User Login </h2>
<TextField
id="loginemail"
label="email"
variant="filled"
type="email"
value={loginemail}
onChange={(e) => {setloginEmail(e.target.value)
setEmail1(e.target.value)
setMail(e.target.value)
}}
/>
<TextField
id="loginpassword"
label="password"
variant="filled"
type="password"
value={loginpassword}
onChange={(e) => {setloginPassword(e.target.value)
setPassword1(e.target.value)
setPassword(e.target.value)
}}
/>
<Button variant="outlined" onClick={LoginClick
}>
{" "}
Login
</Button>
</div>
<div class="register-form">
<h2>New User? Register Here!</h2>
<TextField
id="registername"
label="name"
variant="filled"
value={registername}
onChange={(e) => {setregisterName(e.target.value)
setName1(e.target.value)}}
/>
<TextField
id="regsiteremail"
label="email"
variant="filled"
type="email"
value={registeremail}
onChange={(e) => {setregisterEmail(e.target.value)
setEmail1(e.target.value)
}}
/>
<TextField
id="registerpassword"
label="password"
variant="filled"
value={registerpassword}
onChange={(e) => {setregisterPassword(e.target.value)
setPassword1(e.target.value)
}}
/>
<Button variant="outlined" onClick={RegisterClick}>
{" "}
Register
</Button>
</div>
</div>
</div>
</>
);
}
export default MainPage;
this is my bookPage and here i want to use my user data
even on title (h1) it doesn't shown in there
i tried to console.log but it doesn't worked either
somehow these values doesn't change or update
import React from "react";
import { AccountContext } from "./AccountProvider";
export default function BookPage() {
const { Id, Name, Email, Password, setID, setName, setEmail, setPassword } =
useContext(AccountContext); //this is where i expect to get values from mainPage
return (
<>
<div className="book-page">
<h1>
Welcome , {Id} {Email} {Name}
</h1>
</>
)
}
this is the context i try to use
import { createContext, useState } from "react";
import React from "react";
const AccountContext = createContext();
function AccountProvider(props) {
const [Mail, setMail] = useState("");
const [Password, setPassword] = useState("");
const [UID, setUID] = useState("");
const [Name,setName] = useState("");
return (
<AccountContext.Provider
value={{
Mail: Mail,
Password: Password,
UID: UID,
Name:Name,
setMail,
setPassword,
setUID,
setName,
}}
>
{props.children}
</AccountContext.Provider>
);
}
export {AccountContext, AccountProvider};
and this is the index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { AccountProvider } from "./PAGES/AccountProvider";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<AccountProvider>
<App />
</AccountProvider>
</React.StrictMode>
);
i have tried many solutions but i couldn't found any of them work
edit(1)
this is my routerPage
import React from "react";
import MainPage from "./MainPage";
import Deneme from "./deneme";
import { Router, Switch, Route } from "react-router-dom";
import BookPage from "./BookPage";
import { createBrowserHistory } from "history";
export default function RouterPage() {
let history= createBrowserHistory();
return (
<div>
<Router history={history}>
<Switch>
<Route exact path="/">
<MainPage />
</Route>
<Route exact path="/BookPage">
<BookPage />
</Route>
<Route path="/deneme">
<Deneme />
</Route>
</Switch>
</Router>
</div>
);
}
and in the app component
import React from 'react';
import RouterPage from "./PAGES/RouterPage.js";
function App() {
return (
<RouterPage/>
);
}
export default App;
I am making a simple SPA where you need to login before you can access other pages. I can successfully login and store the login data (firstname, lastname, etc.) cause I plan to use the data again later in the other pages. The problem is whenever I refresh the page, it always empty the state in the context which cause me to return to the login page. I am referring link for my SPA.
Do I need to do this? I would be thankful if someone can point out what I should change / improve. Thank you.
Here is my code.
App.js
import React, { useState } from "react";
import { BrowserRouter as Router, Link, Route } from "react-router-dom";
import { AuthContext } from "./context/auth";
import PrivateRoute from "./PrivateRoute";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Home from "./pages/Home";
import Admin from "./pages/Admin";
function App() {
const [authTokens, setAuthTokens] = useState();
const setTokens = (data) => {
// console.log("DATA ",data);
localStorage.setItem("tokens", JSON.stringify(data));
setAuthTokens(data);
}
// console.log(authTokens);
return (
<AuthContext.Provider value={{ authTokens, setAuthTokens: setTokens }}>
<Router>
<div className="app">
<ul>
<li><Link to="/">Home Page</Link></li>
<li><Link to="/admin">Admin Page</Link></li>
</ul>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/" component={Home} />
<PrivateRoute exact path="/admin" component={Admin} />
</div>
</Router>
</AuthContext.Provider>
);
}
export default App;
Login.js
import React, { useState } from "react";
import axios from "axios";
import { Link, Redirect } from "react-router-dom";
import { useAuth } from "../context/auth";
import { Card, Form, Input, Button, Error } from "../components/AuthForm";
const Login = () => {
const [isLoggedIn, setLoggedIn] = useState(false);
const [isError, setIsError] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const { setAuthTokens } = useAuth();
const handleLogin = () => {
axios
.post("LOGINLINK", {
email,
password,
})
.then((result) => {
if (result.status === 200) {
setAuthTokens(result.data);
setLoggedIn(true);
} else {
setIsError(true);
}
})
.catch((error) => {
setIsError(true);
});
};
if (isLoggedIn) {
return <Redirect to="/" />;
}
return (
<Card>
<Form>
<Input
type="email"
placeholder="Email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<Input
type="password"
placeholder="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<Button onClick={handleLogin}>Login</Button>
</Form>
<Link to="/signup">Don't have an account?</Link>
{isError && (
<Error>The username or password provided were incorrect!</Error>
)}
</Card>
);
};
export default Login;
Auth.js
import { createContext, useContext } from "react";
export const AuthContext = createContext();
export function useAuth() {
console.log("CONTEXT", useContext(AuthContext));
return useContext(AuthContext);
}
In your App component you need to fetch the data from localStorage when initializing your state so it has some data to start with.
const localToken = JSON.parse(localStorage.getItem("tokens"));
const [authTokens, setAuthTokens] = useState(localToken);
If user has already authenticated it will be available in localStorage else it's going to be null.
I also had same problem but I solved liked this Don't use localStorage directly use your state and if it is undefined then only use localStorage. cause directly manipulating state with localStorage is in contrast with react internal state and effects re-render .
const getToken = () => {
JSON.parse(localStorage.getItem('yourtoken') || '')
}
const setToken = (token) => {
localStorage.setItem('key' , token)
}
const [authTokens, setAuthTokens] = useState(getToken());
const setTokens = (data) => {
// console.log("DATA ",data);
setToken(token);
setAuthTokens(data);
}
login undefined
I am getting login as undefined, whenever I click the login button it was working fine before I created AppRoutes.js and moved some of the routes into that file.
TypeError: login is not a function
here is the code structure.
this is the main file where app is started and login route is put here inside the AppRoutes component.
app.js
import React, { Fragment, useEffect } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Navbar from './components/layout/Navbar'
import Landing from './components/layout/Landing'
import Alert from './components/layout/Alert'
import { loadUser } from './actions/auth'
import { Provider } from 'react-redux'
import setAuthToken from './utils/setAuthToken'
import store from './store'
import './App.css'
import AppRoutes from './components/routing/AppRoutes'
if (localStorage.token) {
setAuthToken(localStorage.token)
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser())
}, [])
return (
<Provider store={store}>
<Router >
<Fragment>
<Navbar />
<Alert />
<Switch>
<Route exact path='/' component={Landing} />
<Route component={AppRoutes} />
</Switch>
</Fragment>
</Router>
</Provider>
)
}
export default App
2.AppRoutes.js
import React, { Fragment } from 'react'
import { Register } from '../auth/Register'
import { Login } from '../auth/Login'
import Dashboard from '../dashboard/Dashboard'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import PrivateRoute from './PrivateRoute'
import { NotFound } from '../layout/NotFound'
import store from '../../store'
import { Provider } from 'react-redux'
const AppRoutes = () => {
return (<Fragment>
<Switch>
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<PrivateRoute path='/dashboard' component={Dashboard} >
</PrivateRoute>
<Route component={NotFound} />
</Switch>
</Fragment>
)
}
export default AppRoutes
auth.js
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const body = JSON.stringify({ email, password })
try {
const res = await axios.post('/api/auth', body, config)
if (res?.data?.token) {
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
})
dispatch(loadUser())
}
else {
dispatch(setAlert(res.data.msg, 'success'))
}
} catch (error) {
const errors = error.response.data.errors
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')))
}
dispatch({
type: LOGIN_FAIL
})
}
}
4.Login.js
import React, { Fragment, useState } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { login } from '../../actions/auth'
import { Link, Redirect } from 'react-router-dom'
import store from '../../store'
export const Login = ({ isAuthenticated, login }) => {
const [formData, setFormData] = useState({
email: '',
password: ''
})
const { email, password } = formData
const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value })
const onSubmit = e => {
e.preventDefault()
console.log(typeof login)
login(email, password)
}
//redirect if logged in
if (isAuthenticated) {
return <Redirect to='/dashboard' />
}
return (
<Fragment>
<div className="m-5">
<div className="row justify-content-center">
<div className="col-md-4">
<div className="card shadow-lg o-hidden border-0 my-5">
<div className="card-body p-0">
<div>
<div className="p-5">
<div className="text-center">
<h4 className="text-dark mb-4">Welcome Back!</h4>
</div>
<form className="user" onSubmit={e => onSubmit(e)}>
<div className="form-group">
<input className="form-control form-control-user" type="email" placeholder="Email Address" name="email" value={email} onChange={e => onChange(e)} required />
</div>
<div className="form-group">
<input className="form-control form-control-user" type="password"
placeholder="Password"
name="password"
minLength="6"
value={password} onChange={e => onChange(e)}
/>
</div>
<div className="form-group">
<div className="custom-control custom-checkbox small">
<div className="form-check">
<input className="form-check-input custom-control-input" type="checkbox" id="formCheck-1" />
<label className="form-check-label custom-control-label" htmlFor="formCheck-1">Remember Me</label>
</div>
</div>
</div><button className="btn btn-dark btn-block text-white btn-user" type="submit">Login</button>
<hr />
</form>
<div className="text-center"><Link to="/register" className="small" href="register.html">Create an Account!</Link></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</Fragment >
)
}
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
}
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
})
export default connect(mapStateToProps, { login })(Login)
I am not able to figure out whether it is related to the route structure that i changed or is there a bigger problem that I am missing out on.
You have imported Login a named import in AppRoutes, whereas the connected component with Login was a default export which is why you see the issue
Change
import { Login } from '../auth/Login'
to
import Login from '../auth/Login'
I have setup a login route in Express, which when used, it will set the global state of my application (using Context API), to include the users ID and a token, generated by JSONWebToken.
To be able to load todo items for the user, you need to have a valid token, which works when I'm sending it in the headers while doing the API request. The state is also being updated when doing this with Axios, but the problem is that I don't know how (or the best way for how) to forward to the protected route.
Here is my App.js, which includes the routes for my Welcome and Todo components. Inside of Welcome, there is a Login component, which updates the AppContext to contain the user ID and jsonwebtoken.
import React, { useContext } from 'react'
import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom'
import { Switch } from 'react-router'
import TodoApp from './components/TodoApp/TodoApp'
import Welcome from './components/Welcome/Welcome'
import TodoProvider from './components/TodoApp/TodoContext'
import { AppContext } from './AppContext'
export default function App () {
const context = useContext(AppContext)
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
context.loggedIn ? <Component {...props} /> : <Redirect to="/" />
}
/>
)
return (
<Router>
<Switch>
<Route exact path="/" component={Welcome} />
<TodoProvider>
<PrivateRoute exact path="/todo" component={TodoApp} />
</TodoProvider>
</Switch>
</Router>
)
}
Notice the context.loggedIn property it checks for, which you can see how it's changed in the last code block of this post.
Here is my Login component which is inside of a simple Welcome component
import React, { useState, useContext } from 'react'
import { AppContext } from '../../../AppContext'
export default function Login (props) {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const context = useContext(AppContext)
const handleSubmit = async e => {
e.preventDefault()
try {
// Method which logs in using the /api/login route, and returns users ID and token to the global state (context)
await context.handleLogin(email, password)
} catch {
throw Error('Login error')
}
}
return (
<form handleSubmit={handleSubmit}>
<div className="input-wrapper">
<label htmlFor="email" />
<input
type="text"
id="login_email"
placeholder="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div className="input-wrapper">
<label htmlFor="password" />
<input
type="password"
id="login_password"
placeholder="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div className="input-wrapper">
<button
type="submit"
onClick={handleSubmit}
>
Log in
</button>
</div>
</form>
)
}
And finally, the method context.handleLogin which updates the state to contain the users ID and token. Taken from AppContext.js
handleLogin = (email, password) => {
axios
.post('http://localhost:4000/api/login/', {
email,
password
})
.then(response => {
this.setState({
loggedIn: response.httpStatus === 200,
userID: response.data.data.user._id,
token: response.data.data.token
})
})
.catch(err => {
this.setState({
httpStatus: err.response.status,
loginError: 'Incorrect email/password'
})
})
}
I'm new at React, so any help would be greatly appreciated. Thanks in advance!
I solved it by using withRouter() from react-router, and then pushing the history to the Private route.
Read more about it here:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md