Sill i would use sessionStorage to minimize any side-effects. Also if you have a backend api, then i would consider to encrypt those in the backend then store the encrypted version of it. When you need to use the data in storage you can pass it to backend and decrypt there. It is still not bullet proof but better than nothing. Consider this, if i am using your app and notice you store two properties in local storage, userID and email address. Then when user comes back to your site, you get thoose and start a session in backend. I copy those then logout, put those back in localStorage with different values i found in your application. since i know one of them is an id and the other one is an email i can impersonate someone else and try to go private part of your app.
Rule #1 of frontend development is "do not ever ever never trust client data validate in the backend".
Here is what i normally do. Most of my apps are api centric. When user logs in i store auth token and expiration date in session storage. So when user closes browser, it is gone. Then in private routes first i check if is there a token, then is it expired, then try to loan current user by just token. If all succeed then token is valid and let user in. If token is expired by date value, then try to call refresh token with current token. Api would either return a new token(if it is a valid token) or will fail if someone is trying to hijack it. Then i just clear everything and redirect them to login, no further api call needed. If user auth by token returns error ( api also validates, valid token, still not expired and actually belongs to a user) then forward them to login. Aldo i keep token expiration to 30 minutes, and each api request makes sure if token is still valid and expired, or else try to refreh pattern. Once it fails, straight to login and clear everything.