async-labs / builderbook

Open source web application to learn JS stack: React, Material-UI, Next.js, Node.js, Express.js, Mongoose, MongoDB database.
https://builderbook.org
MIT License
3.76k stars 891 forks source link

Cannot fetch data when running on remote server #219

Closed sudano-r closed 5 years ago

sudano-r commented 5 years ago

I am using the builderbook boilerplate to study, and added new pages and models basically by copying the Book "CRUD" and adapting to my needs. And It was running fine at my machine (all localhost) as a pretend-ERP, with 'supplier', 'product' and 'purchase' register functionality already working. I've run at a remote server (AWS EC2 Windows instance) exactly like I did at my machine, starting a local Mongo service and running 'npm start' at the server's VS Code terminal. I had only to change the 'google.js' Oauth callbackURL (address of the server) to make the login work. When I log in and the application tries to fetch data at any 'componentDidMount', I get 'unauthorized' message. I think it is related to setting the 'user' property at the request, which I thought was being done under the hoods, but I guess I am missing something. I am not very familiar with React, NextJS.

I basically try to fetch the same way already implemented in the boilerplate:

Example (compras.js): async componentDidMount() { NProgress.start();

try {
  const { compras } = await getComprasList();

  let filteredItems = this.filterItems(compras, this.state.filters);
  let pagedItems = this.paginateItems(filteredItems, 1, this.state.itemsPerPage);

  this.setState({
    compras: pagedItems,
    loading: false,
    currentPage: 1,
    totalPages: this.calculateTotalPages(filteredItems, this.state.itemsPerPage),
  });

  NProgress.done();
} catch (err) {
  this.setState({ loading: false, error: err.message || err.toString() });
  NProgress.done();
}}

The full code is here: https://github.com/sudano-r/harekit

Any thoughts? Thank you very much!

tima101 commented 5 years ago

@sudano-r Thanks for sharing code. Do I understand it correctly that google login works fine on local machine but not when deployed to AWS?

sudano-r commented 5 years ago

No. Well, initially it didn't work, but only because the oauth callbackURL was set to 'localhost:8000'. Since I was login in from my home, when the oauth redirected, it set my browser's address to 'localhost' instead of the AWS server address. So, I updated the callbackURL to that of the server and it worked fine. But, the main problem is: after already logged in, if I go to "Estoque" (a new option I created at the logged in menu), for instance, it threw an error. As a first rush attempt, I enabled CORS... And instead of getting "failed to fetch" error, I got "unauthorized". Looks like every time it attempts to fetch data at 'componentDidMount', doesn't work. I get that the 'user' is not in the request. The only part of the code that have an "unauthorized" string is this one:

(customer.js) router.use((req, res, next) => { if (!req.user) { res.status(401).json({ error: 'Unauthorized' }); return; } next(); });

And I don't know where the "req.user" should be set after the login. Because, it seems something is missing in the flow, even after I am logged. I thought the userId was within the session and that was automatically handled. I don't know what to look for now...

I could turn on the server and give you the application address if you wish to try using it. Just let me know the time cause I cannot keep it on constantly running. Thanks

tima101 commented 5 years ago

@sudano-r Thanks for explaining further. So req.user does not exist on deployed app. What about local app, does req.user exist there? Related question, do you see session doc in both local and deployed MongoDB? Does session doc contains user info in both cases?

I could not find Estoque page. Which page produces error Unauthorized in the public repo that you shared in your first comment? Please provide GitHub link to that page.

I haven't run your app since you are saying that locally all works. Your server/google.js code looks good to me. When passport.authenticate('google', options)(req, res, next) executes successfully then passport lib will populate req.user.

Please look into server/app.js:

  if (!dev) {
    server.set('trust proxy', 1); // trust first proxy
    sess.cookie.secure = true; // serve secure cookies
  }

This is the only difference I can think of in terms of authentication for local and deployed apps.

https://github.com/expressjs/session#proxy https://github.com/expressjs/session#cookiesecure

See if you have more than one server proxy on your AWS setup. Also check if you serve app using HTTPS.

Please let me know how it goes. I am curious at this point.

sudano-r commented 5 years ago

req.user exists in local. In remote, it is undefined. Both cases have session doc with user id. The file for the page "Estoque" is actually 'compras.js' (sorry for that... 'estoque' is only the menu alias).

After I implemented CORS and set the the oauth returnURL to server's address, every page shows 'unauthorized' (even after login) when something needs to be fetched. I mean, the login works, but further fetch attempts simply don't get authorized.

I found these links, feels like maybe they are related to this issue: https://stackoverflow.com/questions/32672058/cors-and-the-oauth-2-authorization-code-flow and https://github.com/jaredhanson/passport/issues/403 (specially YUzhva's comment on passport's user (de)serialization).

Do I have to update 'urls' in another parts of code? I still have to check your last suggestions (server proxy on my AWS setup and HTTPS serve).

Thanks.

tima101 commented 5 years ago

@sudano-r

Do I have to update 'urls' in another parts of code?

Yes, you definitely should search up entire codebase for domain strings. For example, here are two lines where you need to paste your value:

https://github.com/sudano-r/harekit/blob/d3094953076db9bce0adc981516569b370c61e51/lib/api/getRootUrl.js#L4

https://github.com/sudano-r/harekit/blob/d3094953076db9bce0adc981516569b370c61e51/server/app.js#L23

I think if you update domain strings and make sure that you serve app via HTTPS then it should all work.

sudano-r commented 5 years ago

It works! I just did what you said, but I don't quite understand exactly what changed. Because, whilst I am indeed starting express server with credentials, as far as I am concerned, I am not using them. Actually, if I use "https://" in the urls, I get SSL protocol error or related browser message ('this site can't provide secure connection') or 'failed to fetch'. But with usual "http" it works just fine. Could you provide me some information or link so I can actually understand better what I did and why it works? Thank you for all your help! :)

tima101 commented 5 years ago

@sudano-r You are welcome. I am not sure what else to add to what I already said above.

For SSL errors, check up how to get certificate. Also try deleting session doc from DB and cookie from browser. Deployed app should only work as expected with HTTPS, not HTTP (with the code in builderbook/builderbook repo).