TL;DR I'm making a MERN app and want to access the passport user from React. How?
I'm trying to make a simple to-do app with the MERN stack and can't get user auth down. I'm using React on the front end with a proxy to an express api. I want to grab the req.user object from React so I can update the navbar based on whether the user is logged in or not. When I post to /api/login I can log the user object from the api route.
React login form:
class Login extends Component {
//..
async login(event) {
event.preventDefault();
await api.login({
username: this.state.username,
password: this.state.password
});
this.setState({
username: '',
password: ''
});
}
//..
}
Axios:
export async function login({username, password}) {
axios.post('/api/login', {username, password});
}
Server:
router.post('/login', passport.authenticate('local'), helpers.login);
exports.login = (req, res) => {
res.json({ message: 'You have been logged in!' });
};
From this exports.login function I can console.log(req.user) but when I try to access the user from any other route it is undefined.
Here's what I want to do:
React:
class App extends Component {
//..
componentDidMount() {
this.isLoggedIn();
}
componentDidUpdate() {
this.isLoggedIn();
}
async isLoggedIn() {
const user = await api.isLoggedIn();
}
//..
}
Which will make a request that returns the user object.
I somewhat understand how the proxy may get in the way and how the session works but the pieces haven't clicked yet. Hoping someone can help me out.
EDIT So um... the problem just kinda went away. I'll update if something breaks again.
Possible duplicate here But to check out the accepted answer by #Molda there was a link to the Github Repository that gave a very good example of how to handle the authentication on the client. You will have to change some of the token authentication to the user that is returned from the server when you login.
Basically you need to store the user somewhere on the client like the browsers local storage.
Related
There are already many answers but I couldn't use them according to my scenario
in which my login component is a child component and i am using react-router-dom of 5.1.2 version
currently i am using local storage to save authToken but I know it is not a good approach. Where should I save the auth token then such that whenever someone tries to hit login route manually or by button they are redirected because token was expired
my route that i am currently using in app.js
<Route path={process.env.PUBLIC_URL + "/login-register"} component={(!isVerified)?LoginRegister:HomeFashion}/>
and this is the child component login
const login = async()=>{
try{
const options = {
username:username,
password:password
}
var response = await axios.post(CONFIG.url+'api/login',options)
localStorage.setItem("__user",JSON.stringify(response.data))
history.push({
pathname: '/',
// search: '?update=true', // query string
state: { // location state
user: response.data,
},
});
}catch(err){
isError(true)
}
}
React.useEffect(()=>{
if(location.state!==undefined){
setMsg(location.state.msg)
if(msg)
{
alert(msg)
}
}
},[location])
what i am doing is when i am successfully logged in i get token and user data in response form backend I store it in localStorage and redirects things according to it
what is the right way to do so?
I know the title of this question might sound confusing, but my problem is actually simple. I have these two handlers for /login get and post requests:
loginRender(req, res) {
let options = { title: 'Login', layout: 'auth.hbs' }
res.render('login', options)
}
login (req,res){
let user = Routes.findUser(req.body.username)
let passwordCorrect = Routes.hashCompare(
req.body.password,
user.password
)
if (passwordCorrect) {
let token = Routes.jwtsign(req.body.username)
let refreshToken = Routes.jwtRefreshToken(req.body.username)
Routes.authRedirect(res, token, refreshToken)
} else {
Routes.badRequestRedirect(res, '/login')
}
}
authRedirect(res, token, refreshToken )
{
let options = {
cssPath: 'styles/querystyle.css',
}
res.cookie('access_token', `${token}`, { httpOnly: true })
res.cookie('refresh_token', `${refreshToken}`, { httpOnly: true })
res.status(200).render('query', options)
}
// app.use(urlencoded)
// app.use(cookieParser)
// app.post('/login', login)';
// app.get('/login', loginRender)
Please, ignore all unrelated stuff.
So, everytime I complete login, I get my webpage rendered and I can actually open inspector and see this:
Page Inspector
Address line
How can I fix that? I want my user to be redirected to dashboard-like page and not to receive his sensitive data in insecure form.
UPD
there's also auth middleware that only appends req.username in case we did parse jwt successfully, and there's a little bit of interaction with it, but it does not appear on page until I go to this page manually by writing the address in address line.
If you don't send the data to the Express server, then you can't read it in you login function and you can't authenticate the user.
It is not a problem is the user can use the tools in their own browser to inspect the data that they entered.
You need it to be encrypted in transport (i.e. use HTTPS and not plain HTTP, at least in production) but you don't need to worry about the user finding out their own password.
I'm pretty new to Vue.js, I followed lots of tutorials, but one thing bothers me.
Let's say I'm building a SPA. The user can log in and then access data using a RESTful API.
So the first step would be to send my login information (username, password) to the API and receive a token. The token would be saved to my vuex and also to the localstorage/cookie so the user stays logged in after a page refresh.
All of the other requests to the API would be signed by the token.
My routes are set up:
const routes = [
{
path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { requiresAuth: true }
},
{
path: '/login', name: 'Login', component: Login,
},
]
I'm using a route guard to protect the /dashboard page from displaying to a non-logged in user:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.loggedIn) {
next({ path: "/login" })
} else {
next();
}
} else {
next();
}
})
My concern is with the loggedIn getter from vuex store which is implemented this way:
const getters = {
loggedIn(state) {
return state.token !== null;
}
};
Everything is working as it supposed to. I understand that without a access token I won't be able to get data from a server.
But...
I can open the developer tools, put an access_token to my localstorage with a random value, refresh the page and suddenly I can access the /dashboard page.
So my question is, how to avoid this scenario?
My first idea was, to have 2 pages - the login page and the second one which contains the SPA. The authentication would be done via session server-side and the SPA page could be accessed only to a logged in user.
Or is there some standard way to to this, so my SPA can handle this?
as said in the comments, i would create a checkLogin() like this:
checkLogin() {
axios
.get('/webapi/check')
.then(() => {})
.catch(err => {
if (err.response.status === 401) {
// use a mutation to toggle your "loggedIn" state
this.$store.commit('loggedIn', false)
if (this.$route.path !== '/login') {
this.$router.push('/login')
}
}
})
}
and there for u can use ur routeGuard to use the function on each change
and ofc ur backend should't allow valid backend responses without the token
UPDATED
u need a state for loggedIn
then u do a mutation to toggle this state depending on how your backend response.
I'm working on a project using Sapper, and been struggling with something for a bit now – can't quite figure it out. I'm using Polka for my server (it was included with the sveltejs/sapper-template#rollup template so I went with it), and cookie-session for my session middleware.
Here is my Polka configuration, where I seed session data also:
polka()
.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
cookieSession({
name: 'session',
keys: [
'6e818055-d346-4fcb-bf56-4c7d54cb04ab',
'60f3e980-6e9c-460d-8ea7-af1fffbdb92f'
]
}),
sapper.middleware({
session: (req, res) => ({
user: req.session.user
})
})
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
When a user logs in, it's handled in routes/login.js essentially some profile info and tokens get set on session.user and the request is redirected back to the root path. I'm using #polka/send-type to respond. See below:
req.session.user = {
accessToken: access_token,
name: first_name,
pic: picture.data.url
};
send(res, 302, '', {
Location: appUrl
});
There, I have a preload module for routes/index.svelte checks if session.user exists, and if so redirects to dashboard:
<script context="module">
export async function preload(page, session) {
const { user } = session;
if (user) {
return this.redirect(302, "dashboard");
}
}
</script>
This all works fine in dev, but when I npm run build and build for production it doesn't so well. It seems like the session.user isn't getting populated after the redirect in login.js.
The session object that I get in preload doesn't have session.user set after login, and vice versa on logout where session.user is simply set to null and the client is redirected to the root, session.user is still populated.
If I refresh the browser, session.user is in the correct state. Without a refresh – if I just logged out I can click around as if I were logged in, and if I had just logged in nothing happens as session.user is undefined.
Just not sure why it'd work on the dev build but not in production. Given that the session.user state is correct on browser refresh, I'm guessing it's not an issue with the cookie middleware configuration. Seems like I'm missing something else. Any help would be hugely appreciated!
Sapper does indeed handle the cache headers differently for dev and production environment. That's why you are experiencing it this way.
But there is a simple solution by changing the sapper.middleware function:
sapper.middleware({
session: (req, res) => {
res.setHeader('cache-control', 'no-cache, no-store')
return { user: req.session.user }
}
})
This sets the cache-control as you want it.
I am playing around with this library and I am experiencing an annoying scenario which I believe comes from some sort of conflict in cookies or headers authentication.
When I login to one account everything works great. But then when trying to login to another account, it simply ignore the new data provided and move through the authentication with the old data and connecting to the old account. No matter if the email or the password even exist. (Tried also with fake data).
The library doesn't have proper logout method which make sense, you dont really need one because when you run it simply using node on your machine without any server involved and there is no cookies or any kind of data in memory, everything work great. I can login to as many account as I want.
The problem is when running it on an Express server.
CODE:
// api.js
const OKCupid = require("./okc_lib");
const Promise = require("bluebird");
const okc = Promise.promisifyAll(new OKCupid());
async function start(req, res, next) {
const {
body: {
username,
password
}
} = req;
try {
await okc.loginAsync(username, password);
okc.search(
searchOpt,
(err, resp, body) => {
if (err) return console.log({ err });
const results = body.data;
// do dsomething with results
return res.status(200).json({ message: "OK" });
});
}
);
} catch (error) {
res.status(400).json({ message: "Something went wrong", error });
}
}
module.exports = { start };
// routes.js
const express = require("express");
const router = express.Router();
const { start, login } = require("../actions/okc");
router.post("/login", login);
router.post("/start", start);
module.exports = router;
So when trying first to post to url/login it works fine. But when you try to do it again with different username and password it simply go through and ignore the new data and connect to the old one.
As part of my investigation I looked at the source code of the library and found a method clearOAuthToken which clear the token from the header. However it didn't really do anything. So I tried to remove the jar initialisation from the requester helper and it was the only thing that helped me to move on and login to another account. BUT it was only for experimenting and cant be a solution as you do need those cookies for other parts of the library. It was only a proof the problem isn't in the headers but in the cookies.
Any idea how can I "reset" state of server between each call?
"when trying to login to another account, it simply ignore the new data provided and move through the authentication with the old data and connecting to the old account."
As OP mentioned in the comment, this is not an authorization header issue, but a cookie issue.
To implement the logout interface, you can manually clear the cookies:
OKCupid.prototype.logout = function(){
request = request.defaults({jar: request.jar()}) // reset the cookie jar
headers.clearOAuthToken(); // just in case
}