What is the suggested method for loading user data (ex: email, fname) after a successful login?
Should it be fetched after POST, or should the controller pass an object back on successful passport.authenticate(...)?
Thx 馃憤
I would say that after a successful login passport.authenticate you should redirect to your home page.
When that happens, it would trigger an SSR in middleware.js where the creation of a new store would happen.
As you can see there, the user details are initialized so if req.isAuthenticated() returns true, you can grab the user details that you've saved on the session by using req.user and save them / or some of them in the store
@slavab89 Thanks for the tip, so far the data only appears after a hard-refresh. Can you show how you would redirect after passport.authenticate? What am I missing here?
I'm doing the following: (For some reason its not really documented in passport docs)
app.get('/auth/callback',
passport.authenticate('google', { failWithError: true }),
(req, res) => {
res.redirect('/');
},
(err, req, res, next) => { // Special specific error callback for that route
res.redirect('/?loginError=' + encodeURIComponent(err));
}
);
But it should work with the current code that's in the branch.
And in middleware.js
const store = configureStore({
user: {
authenticated,
userData: req.user,
......
},
......
}, history);
It should trigger a SSR request because you force the user to do a redirect to your home page. You could add a print in middleware.js to see if it actually reaches there...
I populate them in deserialize function like below
export function deserializeUser (id, done) => {
User.findById(id).populate('friendRequest friends').exec((err, user) => {
done(err, user);
});
};
You guys are awesome, really appreciate the help on this! Feels so close to working... However, still coming up empty handed on both options...
@slavab89 I'm only using 'local' passport login setup (google login is disabled). Using the latest on master, and that first part looks pretty unique. I get the middleware.js part, but the other part has me a little confused... Are you saying that instead of return res.sendStatus(200); on successful login to return res.redirect('/');?
@ZeroCho How would I modify the latest (master) deserializeUser() to populate user.profile state? Is there anything else that needs to be changed as well?
export default (id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
};
$10 bounty on whoever nails the solution - Thx! 馃憤
In ReactGo case, you can use user.profile without doing anything.
@ZeroCho So, when a user logs in, the profile object of that DB-user isn't being populated into the user.profile state. That's my current problem/objective.
@slavab89 is onto something, because when you take in req.user.profile on configureStore() it WORKS... However, it doesn't appear or update the state until after you refresh the browser.
This is what I currently have:
middleware.js
const store = configureStore({
user: {
authenticated,
isWaiting: false,
message: '',
refCode: authenticated ? req.user.refCode : '',
profile: authenticated ? req.user.profile : {}
}
}, history);
db/mongo/controllers/users.js
export function login(req, res, next) {
passport.authenticate('local', (authErr, user, info) => {
if (authErr) return next(authErr);
if (!user) {
return res.sendStatus(401);
}
return req.logIn(user, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.sendStatus(200);
});
})(req, res, next);
}
const UserSchema = new mongoose.Schema({
email: { type: String, unique: true, lowercase: true },
password: String,
tokens: Array,
profile: {
name: { type: String, default: '' },
gender: { type: String, default: '' },
location: { type: String, default: '' },
website: { type: String, default: '' },
picture: { type: String, default: '' }
},
resetPasswordToken: String,
resetPasswordExpires: Date,
google: {}
});
Current user schema looks like this.
What about email, password, tokens? Do they come to user object? If so, there's no reason that profile object doesn't come to user.profile.
and @slavab89 's answer is correct. However, if it doesn't appear before refreshing the browser, it means that something's wrong in SSR.
If you're using mongo (which seems like you do 馃槃 ) Then ReactGo is configured to work with a mongo DB store.
The flow of passport is basically:
middleware.js and create the store correctly with filled req.user object. (You can also see req.session for the full session data)I hope that i'm correct with that flow 馃槣 .
Try adding a console.log print in both serializeUser and serializeUser to see what info you get there. Or console.log in another points to see that everything is corrected.
Please clone and run this: https://github.com/jrodl3r/reactGoUserDataPop. Here are the diff/changes.
After logging-in user.profile.refCode doesn't update until after a refresh for me... 馃槮
So it actually something "wrong" with the manual login (We should have payed more attention to it 馃槃 )
When you perform a manual login (using local strategy) it behaves a little different than what we've described before.
You send a request from the web page using Axios to the server with the user details to perform a login
You get to the login endpoint on the server. That checks the user details and if everything is correct, it returns a 200 status.
The action that you've previously sent gets a response back, and does dispatch(push('/dashboard'));. This triggers a CLIENT-SIDE redirect, meaning only the web site changes, it doesnt go to the backend for this. So you dont get the user details like you've done in middleware.js
When using a manual login, i see 3 options that can be done (We might actually want to change ReactGo logic as well for this)
dispatch(push('/dashboard')); to window.location = '/dashboardI think the optimal choice for manual login is (2) because it wont cause a refresh of the page so the user will have a more smooth experience.
When using google login, however, its not possible to have something like this because the flow redirects you to Google site itself, so either way you're leaving the page.
Very interesting... Considering how much of the codebase is dedicated to the user, I'd think this functionality (simple user data population) and a basic UI on say /dashboard should be considered for addition to the project.
There is one caveat with option 2 in that if you refresh the page, guess what happens? User profile data goes bye-bye. However, this action in tandem w/ the store nails both scenarios 馃槈 馃憤
@slavab89 shoot me an email
Should we close this then?
btw, i've tried to write you and email (Based on your github profile) but seems like that address does not exist
Most helpful comment
So it actually something "wrong" with the manual login (We should have payed more attention to it 馃槃 )
When you perform a manual login (using
localstrategy) it behaves a little different than what we've described before.You send a request from the web page using Axios to the server with the user details to perform a login
You get to the login endpoint on the server. That checks the user details and if everything is correct, it returns a 200 status.
The action that you've previously sent gets a response back, and does
dispatch(push('/dashboard'));. This triggers a CLIENT-SIDE redirect, meaning only the web site changes, it doesnt go to the backend for this. So you dont get the user details like you've done inmiddleware.jsWhen using a manual login, i see 3 options that can be done (We might actually want to change ReactGo logic as well for this)
dispatch(push('/dashboard'));towindow.location = '/dashboardI think the optimal choice for manual login is (2) because it wont cause a refresh of the page so the user will have a more smooth experience.
When using google login, however, its not possible to have something like this because the flow redirects you to Google site itself, so either way you're leaving the page.