How to secure persisted vuex data in a Vuejs SPA? - vue.js

I am building a single page app with vue js/vuex and Laravel backend, I need the users have different roles. I am authenticating on the backend, and using a jwt token for API calls, I am not concerned that we will allow CRUD operations to be performed by a user without the appropriate permissions. What I need to have is the ability for vue to know the role of the user and maintain statelessness without tampering.
I am using vuex-persistedstate with js-cookie and it works but the cookie is clear text and if I change the "role" in the cookie then refresh the page vuex uses the new value. Obviously the data will come from the API and therefore when the token sent is for a user with role = user for data restricted to role = admin it will return 401. Am I overthinking this? If they really wont get, nor be able to change any data they aren't allowed to, just let the "hackerish" users do what they will?

Related

Custom User Role with Providers (Google, FB, Email)

I have Strapi V4 and 2 custom user roles - seller and buyer.
On the front-end, I have 2 routes that define which role will be assigned to new users /account-buyer/signin and /account-seller/signin.
I tried to make a copy from node_modules\#strapi\plugin-users-permissions\server\controllers\auth.js to src\extensions\users-permissions\controllers\auth.js. but nothing seems to happen.
Also, I’m not sure how to throw the user type (buyer/seller) through all of these back-and-forth redirects of Google (for example).
How could I solve it?
Or let’s say:
save the needed role in localstorage at the moment the provider button is clicked
register the user with the default Authenticated role
on the frontend's /redirect page read the value that has to be set.
Send the value from localstorage and reassign user's role
How can I update the user’s role in this case?
Thank you.
I would start with setting up the Google auth flow with your frontend and Strapi first. On sign in for a non-existing user this will register a user with the default role. Depending on your frontend authentication framework you can pass arguments to the redirect url, e.g. /api/user-registered?role=<some-role>&redirect=<original-redirect-url> (this route can also exist within Strapi instead of your frontend). On this page I would call the Strapi API (or e.g. query engine API inside Strapi itself) and update the role of the current user (you know who this is because they just signed in).

Clarifications on JWT Authentication and User Data

I am implementing JWT Authentication for the first time on a Vue SPA (django rest framework on backend), and I am really struggling to come up with a good flow for managing my user data.
My question is simply this:
Should I make an API route to retrieve the authenticated user from the access token, or should I put user data needed by my frontend within the payload of the JWT and save it on local storage?
If I choose to put all user data needed by the frontend into the payload of the JWT, how am I supposed to maintain a strong user experience with user data that constantly changes? Aren't I at the mercy of my refresh token requesting an access token? Because only then will my frontend decode a new payload with the new user data that can be displayed on the pages.
On the flip side, if I make an API route that retrieves the currently authenticated user, I am ignoring the scalability benefit behind JWTs, and will need to make a db hit frequently.
Thanks.

How to keep user logged in using Vuex?

I am managing performing a login and the login state using Vuex. It is a standard procedure of:
User submits their login details on a form
The form dispatches a Vuex action to make an axios API call to a Node/Express endpoint /api/login with the credentials
The response from the API if successful will send back the user's data which is stored in the state.user object through a setUser mutation. A flag of state.user.isLoggedIn is also set to true.
A JWT access token is sent to the user's browser and stored in a secure cookie to use over and over again until it expires. If it expires, a refresh token is used to generate a new JWT access token.
The site's header displays the user's login information .e.g name
The above all works as expected. However if the user refreshes their browser, then Vuex state gets emptied and the site header no longer shows the user information. To overcome this I stored state.user into localStorage in the browser as persisted data and repopulate the Vuex store using the data in localStorage on page refresh. If the user logs out, the localStorage is cleared.
There are many routes that require checking if the user is logged in. I cannot rely on localStorage because it can be manipulated by the user. If the user deletes their cookies which contains a JWT, then localStorage is not being emptied and the site header still displays their log-in information. Therefore I need to do an API call on almost every page to check if the user is logged in and if not then to delete the localStorage as well.
My question is:
Where do I perform a log-in check every time a page/view is accessed? Is it whenever the Vue app is mounted or within vue-router using the beforeEach navigation guard?
If I use beforeEach in vue-router, is it going to crash the site by making an API call to /api/login every time a user changes route? Is it normal practice to do this on every route?
Are there any other alternative patterns to keeping track of if the user is logged in or not?
Application decisions like this tend to be subjective because every application is different, with different requirements and different architectures. It sounds like this is a single page application, so I'll go from there.
First, the fact that your JWT is set up and working is great - that can be a lot of hard work, so be proud that you made it that far.
If your application has certain pages that are accessible without first being logged in (like a login page, or an access denied page, etc.), then you have to consider that in the design as well. In this case, a beforeEach route guard is the perfect solution to keep a route from loading or to catch those not-logged-in users before they attempt to request content that will likely just give them an error.
You probably shouldn't have to make an API call to the server each time the user navigates to a new page. That would probably be a little slow. Because your application uses JWT, complete with refresh tokens, you should be able to rely on them, so that if a user is logged in, they will continue to be logged in until they close their browser and walk away.
This is why you should not use localStorage. When a tab closes or even after a reboot, the user will still appear to be logged in. Instead of using localStorage, use sessionStorage - Helpful MDN link.
Now, the login event to retrieve the user object should only happen once per browser tab, but stay active during the visit.
I wonder why you don't just request a new user object from the server when the browser is refreshed. It's not strange to force a re-check to the server for a full page load. Doing that would mean you wouldn't have to use anything outside of Vuex to store the user's state
Next, is there a way to check the validity of their JWT during the navigation event? Depending on the way you handle the JWT, you may have access to it, and can possibly decode it (like through the oidc-client-js library), and that way determine for yourself if the token is expired. However, if the token is expired, your token refresh system may just refresh it and not tell you about it.
You can also watch your HTTP requests and look out for 401 or 403 responses (however your back-end handles logged-out users), and direct the user back to the login screen if you see one of those. You can use Axios' global interceptors to catch them, or do it yourself if you centralized the location from where Axios is called.
Overall, you're on your way, and clearly have a good grasp on this. Great progress so far, having done probably 90% of the heavy lifting already.

How to protect admin routes with Vue.js?

I've been using the localStorage to store the JWT token and do the authentication, it works fine because the backend is protected, what is not, are the front end pages in Vue.js and its components, anyone can create a false token in the browser and access the pages, the panel menu, but the data is not loaded because the token is invalidated by the backend. Did you realize that the user will be able to navigate the pages normally? How can I avoid this? Only allowing back-end validated users to navigate the site.
I'm using Vuex and I can not think of a solution. The isLogged state receives the localStorage token, so the user can easily circumvent the browser.
I thought of a solution, do not judge me. Before entering each route, send a request to the back end to check the token, if false, remove the localStorage, would this be a good one?
I'd say your solution is a pretty decent one to start with, but I think you can improve it a bit.
Because you're using VueX, what you could do is store the user (or some other variable) in the Vuex Store. Then before each route you can check if the Store has a user.
If the Store doesn't have a user, check if the localStorage has a token. If there is no token, then the user is not authorised to go to the route.
If the localStorage does have a token, call the back-end and check if the token is valid. If the token is valid, store the user in the Vuex Store and continue. If the token is not valid, than the user is not authorised to go to the route.
This will prevent you from creating unnecessary calls to the back-end whilst still having the validation.
Or you could for example in your store state have a user object that has something like 'userRole' this way you could check for Authentication/Token/Role.

Get userid from auth0 session localStorage

I'm in the process of implementing auth0 login in my project, and to be as fast as possible I'm using a hosted login page. Following the auth0 vue docs I've got up and running quickly and I can login and logout users.
Now, I'm trying to create a user profile page with a route of user/:id. For the :id part I want to use the user_id of the user profile, but I'm having issues understanding the optimal way to get it. I realize I can use the auth0 api users endpoint but I'm not sure that's the correct way. Do I actually need to make an API call to the users endpoint each time an user clicks their profile? Is there no better way to get the user_id, maybe from the id_token which is set in localStorage upon login?
Even better, is there a way to get actual user ids? I know that if I would setup my own login with my own db, I'd have an auto incrementing id which I'd use for user id. This does not seem possible with Auth0, or am I wrong about that?
Disclosure: I work for Auth0.
The idToken returned at the end of the authentication / authorization is a JSON Web Token. This token contains the Auth0 user_id as the sub (subject) claim (property).
If you are using Auth0.js or our AuthService tutorial, Auth0.js will decode this token and give it to you as idTokenPayload object in the authResult. This can be used to identify the user as idTokenPayload.sub.
You can store this object as a whole in localStorage or simply decode the token that you accquired from localStorage. However, please note that you should validate the token after accquiring it from localStorage aswell.
To avoid all the above, you can simply use the checkSession method each time your application bootstraps to get a new copy of the accessToken / idToken and rely on that directly :)