How to persist redux user login on server (SSR) - reactjs

I have an application that works as expected on the client with user logins, etc. No problem.
I would like to pre-render some stuff for components which the user can view if they are logged in, however from what I know you can't persist the store on the server. Makes sense as the store is persisted often in localstorage or something on the client. Plus the redux-persist package warns you to conditionally persist only on the client. I'm doing the following when creating the store. This will ensure the client exists and persist if it does. It only persists the user:
if (typeof window !== "undefined") {
persistStore(
store,
{
whitelist: ["user"]
},
() => {
store.dispatch(setPersisted());
}
),
}
Note that I'm using a redux-persist 4.9.1 as I had a hell of a time migrating to v5.x.
So, how do we pre-render things that only a logged-in user can see if the store can't be persisted on the server? Am I going about this totally wrong?

Related

How to store user profile information and access it globally in react components

I am using a passport for authenticating the user. After successful authentication, I would like to show the username and id in one of the react components for now. There could be more places where I need to access user information in the future.
How should I accessing user profile information in independent react components?
I tried storing the user information in a cookie. Accessing the cookie in every component seems to be messy.
app.post('/login/callback', auth.authenticate('saml', { failureRedirect: '/', failureFlash: true }), function (req, res) {
// stores the cookie information
res.cookie('cookie1', req.user.id, { secure: true, signed: true, expires: new Date(Date.now() + 3600) });
res.redirect('/');
}
);
I would like to access the user principal globally across all the react components.
There are multiple options for Global State management.
Local Storage: Use the Browser local storage to save details
Session Storage: This is similar to Local Storage with just one difference: that is while data in localStorage doesn't expire, data in sessionStorage is cleared when the page session ends.
Context: Context is designed to share data that can be considered “global” for a tree of React components
Redux: Use State management library to manage the state in one place.

Handling with external logouts in React app

I just finished my session (cookie) based login system in my React+Redux app.
Then I realized that there is no way how I can check if the user logged out from another location (different chrome tab, removing the cookie, server session invalidation, expiration).
I was looking on Instagram's website what using React too. It seems if you log out in a different browser tab, you can still route to another place in-app until u hit some API fetching... Then website is automatically refreshed...
BUT, there is also some kind of system what realize that user is unlogged even when I do some actions whatnot require API calls.
So how to realize these situations in the best way? How do you handle them when developing.
I'm not exactly sure how Instagrams Authentication works but I'd imagine that it's handled by middleware and when you request an API call, it will check to see if the user has an Auth Token stored in Cookies or whatever before initiating the API Request.
You can easily do this yourself by adding a Redux middleware that checks to see if the cookie is there before dispatching the next action. If it's not there you can return an error message to the user or redirect them or even dispatch a redux action that clears out all loaded data and then finally redirect them back to the login page.
The reason why Instagram is only locking the user when it hits an API call is that you can't really do anything dangerous to the users account if the cookie was deleted as you can't make changes to their account (commenting, posting, changing account settings etc.) without interacting with the API. Therefore, the middleware doesn't have to run every time an action has been dispatched which technically makes their application more performant.
Example Redux middleware
import Cookies from 'js-cookie';
const clientHasToken = store => next => action => {
const authToken = Cookies.get('auth');
if (!authToken) {
// redirects user, but you could do anything here
return window.location.href = '/login';
}
// if user has an auth token, proceed to the next action
next(action);
};
export default clientHasToken;

How to check if a user is logged in and how to save their information? (React)

So pretty much I made a login page communicates with a backend to login a user. Now I want to take their data and store it for all my other components to know that there is
A user logged in
Have access to the user's information
I figured based on past StackOverflow answers, that the best way to do this is using JWT and LocalStorage. But all the answer's I've encountered seem to use Redux. I'm not using Redux at all for my application, and have no clue how to use it, so I was wondering if there's a way to do it without redux.
You don't need Redux. You just have to store the JWT in localStorage. To do that, just use localStorage.setItem('token', data.token) when you receive the login success response from the API. It's that simple. You can read this article for more details. It's for React Redux applications but doesn't need Redux.
Then, assuming you are using react-router, you need two helper functions. The first will go on your protected routes and would look like this:
const requireAuth = (...) => {
if(!localStorage.getItem('token')) {
// go to login route
}
// stay on this route since the user is authenticated
}
The second goes on your unprotected routes and looks like this:
const verifyAuth = (...) => {
if(localStorage.getItem('token')) {
// go to your dashboard or home route
}
// stay on this route since the user is not authenticated
}
Keep in mind that you have to refresh the token according to the server expiration time. A good approach would be to create a call to the server asking if the token is still valid.

Firebase 3. authentication doesn't persist

I'm working with firebase in a angularJS app, using the email and password authentication.
I just update my app from Firebase 2.x to 3.x and AngularFire from 1.x to 2.x.
I followed these 2 docs to do the migration :
https://firebase.google.com/support/guides/firebase-web
https://github.com/firebase/angularfire/blob/master/docs/migration/1XX-to-2XX.md
But now, each time I refresh my page, I need to re-authenticate, like there is no persistent session.
I've checked the localStorage key firebase:authUser I have an expirationTime in the past (actually it's set with the timestamp of my login).
To check if the user is loggedin I use : $firebaseAuth().$getAuth()
EDIT
Here is a working example of my problem (login: toto#mail.fr / password: toto123)
https://plnkr.co/edit/463Hse?p=preview
Does anybody know why this behavior ?
I am gonna guess that you are checking the currentUser directly without waiting for the initial auth state to resolve. You need to add an observer:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
https://firebase.google.com/docs/auth/web/manage-users
Also, auth state is stored in web storage so make sure you have that enabled. (state will not persist in safari private mode browsing for example).

React/Redux server side rendering initial state

I have a React/Redux application that keeps track of a user's authenticated state in local storage. The application is also set up to use server side rendering. I'm running into issues when rendering the initial application state. My server creates a new store and emits the SET_INITIAL_STATE action. This initial action on the client side reads localStorage and passes the authenticated information on to my reducers. The server, however, has no knowledge of this logged in state since I'm using stateless JWT located in local storage for authentication.
Since the server and client are out of sync at this point, I'm getting this error:
React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
Which makes sense, because the server is trying to render an unauthenticated state.
What is the accepted standard or practice for setting this initial state that relies solely on something the client has access to?
I found that the OP is right, that cookie storage is the best option. If you're using react, redux, and express for a universal app the option that worked for me was https://github.com/eXon/react-cookie.
Essentially:
In you server side code you can use:
import cookie from 'react-cookie';
function handleRender(req, res) {
cookie.setRawCookie(req.headers.cookie);
// Create new Redux store instance
const store = createStore(rootReducer, {
username: cookie.load('username') || '',
accessLevel: cookie.load('accessLevel') || userRoles.public,
});
//other react/redux/express related code
}
In your react applications, inside the components, you can just save the cookies directly:
import cookie from 'react-cookie';
//first parameter is anything you want, second is the data you want to save
cookie.save( 'username', username );
cookie.save('accessLevel', accessLevel);
Further if you want to store an object, as I did, you can pass in JSON.stringify(your object). cookie.load will automatically parse it for you.
In my code I called cookie.save from the actions, but you can call it directly in components or any abstraction you have.
I found a working solution.
The trick, unfortunately, is to store the authenticated state in a cookie so the session state gets sent to the server automatically upon request.

Resources