Vue - Token logged in state validation - vue.js

I am able to identify if the user is logged-in using vuex store. The problem is when I manually changed the access_token in the browser (saved in cookies), I'm still considered as logged-in since the value is not null. How do I verify that the browser access_token is a valid token? I want to redirect them to log-in if it's not a valid one.
I think I will also have trouble when users use an expired token.
app.js
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.loggedIn) {
next({
name: 'login',
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (store.getters.loggedIn) {
next({
name: 'dashboard',
})
} else {
next()
}
} else {
next()
}
})
vuex
loggedIn(state) {
return state.token !== null
}

I guess your access token is generated by your server. You will need to send an ajax request with your token as data to the server and the server should check if the token is valid or not and you simply return an true or false and check that via an if statement

Related

Retrieve user settings from server

I have a Vue.js app that requires a user to log in. To do this I simply use Vue Router with protected routes. When the user logs in, they receive an auth token from the server which I store in local storage.
Here's a small part of router/index.js:
const ifNotAuthenticated = (to, from, next) => {
if (!store.getters.isAuthenticated) {
next()
return
}
next('/')
}
const ifAuthenticated = (to, from, next) => {
if (store.getters.isAuthenticated) {
next()
return
}
next('/login/')
}
export default new VueRouter({
routes: [
{
path: '/',
name: 'Dashboard',
component: Dashboard,
beforeEnter: ifAuthenticated,
},
{
path: '/login/',
name: 'Login',
component: Login,
beforeEnter: ifNotAuthenticated,
}
]
})
Here's a small part of store/modules/auth.js:
const actions = {
[AUTH_REQUEST]: ({ commit }, user) => {
return new Promise((resolve, reject) => {
commit(AUTH_REQUEST);
Api.getToken(user)
.then(res => {
localStorage.setItem('user-token', res.data.token);
commit(AUTH_SUCCESS, res.data);
resolve(res);
})
.catch(err => {
commit(AUTH_ERROR, err);
reject(err);
});
})
},
[AUTH_LOGOUT]: ({ commit }) => {
return new Promise((resolve) => {
commit(AUTH_LOGOUT)
localStorage.removeItem('user-token')
resolve()
})
}
}
All very standard I think. So my question is, after a user has logged in I need to make a call to the server to retrieve the users settings. How would I go about doing this to ensure that a logged in user always has their settings available to them throughout the app (ie: its in the store).
Here's a few scenarios:
The user logs in for the first time and receives their auth token (saved in local storage). Then their settings are retrieved and saved to the store.
A user logged in yesterday. Today they don't have to log in again because their auth token is already stored in local storage. Therefore I just need to retrieve their settings no matter which page they happen to open the app on.
Essentially, I need to ensure that a users settings are downloaded either when they initially log in or when they return to the site later on but are already logged in.
You could always use a vuex dispatch. Create an action that fetches the users settings, then, from your [AUTH_REQUEST] action, on successful login, dispatch the created action.
dispatch("[FETCH_USER_SETTINGS]", res.data.id);
localStorage.setItem('user-token', res.data.token);
commit(AUTH_SUCCESS, res.data);
resolve(res);
You'd also need to include it in your params:
[AUTH_REQUEST]: ({ commit, dispatch }, user) => {
This way the action will be called for any scenario where the user logs in.

Vue/Vuex - how to stay logged in as a user after route change

As a small Dev-Team, we are about to create our own social media website just for fun.
Our login process is handled with a jwt which is stored in localStorage.
async login( { commit }, user) {
commit('auth_request');
try {
const res = await axios.post('http://localhost:3000/api/v1/users/login', user);
if (res.data.status === 'success') {
const token = res.data.token;
const user = res.data.user;
localStorage.setItem('jwt', token);
commit('auth_success', { token, user } );
toast.success(res.data.message);
router.push({ path: '/' })
}
return res;
} catch (error) {
commit('set_errors', error.response.data)
toast.error(error.response.data.message);
}
},
However as soon as we change route we getting logged out.
router.beforeEach((to, from, next) => {
// check for user is logged in on route change
next();
})
How to prevent getting logged out on route change or page reload?
Well you are committing the token and it is saved in your localstorage so you can try this way so that your router knows that you are authenticated
This is a example router
path: "/dashboard",
name: "dashboard",
meta: { requiresAuth: true },
component: () => import("../views/Dashboard.vue")
I have a meta with requiresAuth: true so you can go to your router.beforeEach and create a if statement to check if there is a token if there is a token then stay logged in if not when trying to get into the Dashboard page take me to the login page.
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
if (window.localStorage.getItem("jwt")) {
next();
} else {
next({ name: "login" });
}
}else{
next()
}
});
We are doing a if stament where we get the token with window.localStorage.getItem("jwt") if there is a token in the localstorage then we can tell our router to stay logged in and navigate to the pages that has meta: { requiresAuth: true } if we dont have the token in localstorage take us to login page
I hope this helps you out or gives you an idea how to guard your routes. If you haven't solved the problem then just comment on the answer so we can solve the problem.

How to know the source of route redirection in Vue.js?

I have a navigation guard in place (main.js), which redirects certain routes if a condition is met:
router.beforeEach((to, from, next) => {
if (conditionMet)
next('/another-route');
else
next();
})
Now how can I know which route was redirected to /another-route?
The from object in, In-component navigation guard of /another-route doesn't point to the actual referrer, instead it points to the route which referred the redirected route. Sounds confusing?
In simple terms, If route A had a button which on click goes to route B and then route B is redirected to route C. The from object in C has details of A instead of B.
How do I know which route was actually redirected to C?
beforeRouteEnter(to, from, next) {
console.log(from);
/* */
next();
}
Any help would be appreciated.
You can't do it that way. But you can do a workaround by using query params in /another-route that points to route B. So it will be like /another-route?next=%2Froute-b. And after that, you can just use this.$router.redirect(this.$route.query.next)
This how I do it in my program if unauthorized user accessing some pages, e.g /customer/1. I use r for the query params.
beforeEnter: (to, from, next) => {
let access_token = Vue.cookie.get('access_token')
if (access_token == null) {
// user doesn't have access token, redirect to login
if (from.name !== 'login') { // prevent append /r if from is login page itself
next({ name: 'login', query: { r: to.fullPath } })
}
next({ name: 'login' })
} else {
// user has access token, user can open the page
next()
}
},
The link will become /login?r=%2Fcustomer%2F1
And in login.vue
onSubmit () {
this.submitting = true
let { username, password } = this
this.$api.POST('auth/login', { username, password })
.then(response => {
let { id, access_token } = response.data
this.setCookie(id, access_token)
this.$router.replace(this.$route.query.r)
})
.catch(error => {
if (error.response && error.response.status === 401) {
// show error that username or password is invalid
}
})
.finally(() => {
this.submitting = false
})
},

Vuejs Router guard works unexpected

I have router which work with errors and can't understand how to fix it.
This global router which should check jwt token expiration and handle routing. Everything worked fine before adding some functionality like isActivated account. So now I need to check if User has token and if User account is activated.
1) If user has token it should make next() otherwise next("/login") (redirect)
2) If user has token but his account is not activated yet (first time login), it should redirect on Setup page next("/setup") until he submits some information.
So this is my guard
router.beforeEach((to, from, next) => {
const token = localStorage.getItem("token");
const tokenExp = parseInt(localStorage.getItem("tokenExp"))
const isActivated = localStorage.getItem("isActivated")
const now = new Date().getTime() + 129600000
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
console.log("first")
if (requiresAuth && !token) {
next('/login');
} else if (requiresAuth && token) {
if (now > tokenExp) {
axios.post("/user/t/r", token)
.then(e => {
const token = e.headers['authorization'].replace("Bearer ", "");
localStorage.setItem("token", token);
localStorage.setItem("tokenExp", (new Date().getTime() + 172800000).toString())
if (isActivated === 'true') {
next()
} else {
next("/setup")
}
})
.catch(e => {
localStorage.removeItem("token")
localStorage.removeItem("tokenExp")
localStorage.removeItem("fullName")
localStorage.removeItem("role")
next('/login')
})
} else {
console.log("second")
if (isActivated === 'true') {
console.log("third")
next();
} else {
console.log("fourth")
next("/setup")
}
}
} else {
next();
}
})
And this is my console.log with error when I login:
You are infinitely redirecting to /setup, You code on first run hits "fourth" then sends the user to /setup where that before call is run again and your infinite loop starts.
You need to stop calling next('/setup') or next('/login') if the user is already on that page.
You need to make use of router.currentRoute in order to check you are not going to redirect to page they are already on.
https://router.vuejs.org/api/#router-currentroute

Rest API to connect (authorize) google for logged in user

I'm working in an application which uses a REST api using the MEAN stack and Passport JS to manage the authentication.
The authentication, we use JTW tokens for the communication between the backend and frontend. The token is generated based on local username and passwords.
Now I want to 'add' (authorize) the user's google account to the profile to use with google calendar API. (using this-> https://github.com/wanasit/google-calendar)
I've already have managed to send the user to the google authorization page, and get the token back from it. The problem is that when the user gets redirected to the page, it looses the JWT token where I check the user for the request.
Is there any other way to get the current logged in user, or to pass some custom callback authorization header/param when calling the authorize method?
auth.js:
var googleParams = {
clientID: config.auth.google.clientID,
clientSecret: config.auth.google.clientSecret,
callbackURL: config.auth.google.callbackURL
}
var googleStrategy = new GoogleStrategy(googleParams, function (token, refreshToken, profile, done) {
profile.token = token;
return done(null, profile);
});
routes:
rotas.get(
'/google',
auth.authenticate(), // will check the current user
auth.isLoggedIn, // make sure the user is really logged in
auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to google to get the token
);
rotas.get('/callback/google',
auth.authorize('google', { scope: googleScope, passReqToCallback: true })
auth.authRedirect()
);
the auth.authRedirect() function above is the closest solution I've found. It's a Express middleware wich redirects the user to a known route in the frontend where the user IS authenticated... but then I would not be able to fetch all his Google profile and information i need...
You have to be sure the app.use(session) its been called before any route.
...
app.use(session({
secret: 'secret'
}))
app.use(passport.initialize())
app.use(passport.session())
...
rotas.get(
'/google',
auth.authenticate(), // will check the current user
auth.isLoggedIn, // make sure the user is really logged in
auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to google to get the token
);
rotas.get('/callback/google',
auth.authorize('google', { scope: googleScope, passReqToCallback: true })
auth.authRedirect()
);
Your req.user won't be undefined in this case.
If it doen't work right way, I can put my whole code that I've created here.
Hope it help you! :)
So what I ended up doing was:
Authenticate the user making the request via JWT access_token
Get the user's ID and set it to the state option's property
The user is redirected to the google authorization page and choose the account (s)he wants to connect
(S)He gets redirected to my callback url with the state query param having the user's id
Now I just have to get that id, search the user in the database, and set the data I need from req.account which contains the user's openid profile.
var googleScope = ['openid', 'email', 'https://www.googleapis.com/auth/calendar'];
routes.get(
'/google',
auth.authenticate(),
auth.isLoggedIn,
function (req, res, next) {
var _id = '' + req.user._id; // convert to String... _id is an mongoose object
return auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true, state: _id })(req, res, next)
}
);
routes.get('/callback/google',
function (req, res, next) {
auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true })(req, res, next);
},
auth.saveUserData()
);
saveUserData= function () {
return function (req, res, next) {
if (req.query.state) {
var _id = req.query.state;
User.findOne({ _id, deleted: false, active: true })
.exec(function (err, user) {
if (err) {
res.send(err);
}
if (user) {
user.auth.google = {
id: req.account.id,
token: req.account.token,
email: (req.account.emails.length ? req.account.emails[0].value : null),
name: req.account.displayName
}
user.save(function (err, data) {
if (err) {
res.send(err);
} else {
res.redirect('/')
}
})
} else {
res.sendStatus(401);
}
})
} else {
res.sendStatus(400)
}
}