After a user has logged in the first time I redirect him to the page CreateProfile where he types in all the profile information. Afterwards I want to make this site not accessible anymore, such as if the user types the URL into the browser (e.g. www.myproject.com/createProfile).
How can I make sure that only the redirection from my login page has access to the CreateProfile page? That is, if the user types in the URL manually he will be redirected e.g. to the 404 page.
Currently my route for CreateProfile looks the following:
{
path: '/createprofile',
name: 'CreateProfile',
component: CreateProfile,
beforeEnter: (to, from, next) => {
if (store.getters.getUserStatus == true) {
next()
} else {
next({ name: 'Login' })
}
}
}
Thanks!!
You can check the from route object in the beforeEnter navigation guard to test the previous route location.
For example, check that the from.name property is Login, which will only be true when there has been the redirect you want. (Assuming you don't also provide a <router-link> from Login):
beforeEnter: (to, from, next) => {
const isRedirected = from.name === 'Login';
if (isRedirected && store.getters.getUserStatus == true) {
next()
} else {
next({ name: 'Login' })
}
}
Related
I am attempting to build a Vue application that uses Supabase authentication. Inside one of the route guards in the router file, I implemented supabase.auth.getUser() in order to retrieve the user login status for a conditional that prevents next() from executing before the user is authenticated:
// Route guard for auth routes
router.beforeEach((to, from, next) => {
// const user = supabase.auth.user();
const { data: { user } } = await supabase.auth.getUser();
if (to.matched.some((res) => res.meta.auth)) {
if (user) {
next();
return;
}
next({ name: "Login" });
return;
}
next();
});
However, when I implement supabase.auth.getUser() inside the router guard, I get the following error in the console before logging in: "invalid claim: missing sub claim". After logging in, the error goes away. If I remove the supabase.auth.getUser conditional from the route guard, the error also goes away. After some additional digging online and running my Supabase anon key in jwt.io, it appears that my key is missing sub claim in the payload. If this is preventing the key from being authenticated properly, how can I resolve it?
You should be checking against the session rather than the user. The user will try to check against a JWT first which wouldn't exist at the time of checking since you're not logged in. Use getSession instead:
// Route guard for auth routes
router.beforeEach((to, from, next) => {
// const user = supabase.auth.user();
const { data: { session } } = await supabase.auth.getSession();
if (to.matched.some((res) => res.meta.auth)) {
if (session?.user) {
next();
return;
}
next({ name: "Login" });
return;
}
next();
});
I have a VueJS single-page application and I use JWT authentication.
I'm trying to figure out how to make sure that User is authenticated after page reload and if not, redirect them to login page.
accessToken and refreshToken are stored in the cookies and also in Vuex
Vuex.state:
auth: {
user: {},
isAuthenticated: false,
accessToken: null,
refreshToken: null
},
Vuex.actions.refreshToken
refreshToken: async ({state, commit, dispatch}) => {
try {
await api.jwtRefresh(state.auth.refreshToken).then(response => {
if (response.status === 200) {
dispatch("setAuthData",{
accessToken:response.data.access,
isAuthenticated:true
})
}
}).catch(err => {
dispatch('logout')
});
} catch (e) {
dispatch('logout')
}
},
App.vue
export default {
data: () => ({}),
mounted() {
this.$store.dispatch('setAuthDataFromCookies')
this.$store.dispatch('refreshToken') // checks if user is authenticated, redirect to login page if not
this.$router.push('/dashboard')
}
}
My idea is to try to refresh the JWT token. If it was successfully refreshed User can proceed to /dashboard. If not, User is redirected to the /login
The problem is that mounted doesn't wait until refreshToken is done and it redirects User immediately to the /dashboard even before token is refreshed.
How can I make it wait? (The idea is that refreshToken will redirect user to /login in case of error.
You can setup a meta auth field in your router, and a global beforeEnter or beforeEach guard that checks Vuex (or your cookies, or both) for a token.
In your router.js file you'd have something like
routes: [
{
name: 'Login'
},
{
name: 'Dashboard', // + path, component, etc
meta: {
auth: true
}
}
]
Then you setup a global guard, something like this:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.auth)) {
if (!store.getters.authToken) {
next({ name: 'Login' });
} else {
next();
}
} else {
next(); // Very important to call next() in this case!
}
})
This will check before each and every route transition whether the next route has the auth meta field. If it does, it checks your Vuex state for the token, and otherwise navigates as normally.
Vue Router Docs on Navigation Guards
In your case, you're trying to authenticate the user, so you can just call your endpoint inside of the beforeEach guard and redirect like that based on the response. Just make sure to make the callback asynchronous, like router.beforeEach(async (to, from, next) => {})
I am trying to redirect non-logged in user from all pages to /login. I tried beforeEach() but it doesn't fire when user enter site with direct url like /home, /event.
Per-Route Guard beforeEnter() works perfectly since it fires once the user lands on that particular page. However, it requires me to add beforeEnter() on every routes.
I am looking for a way to duplicate that beforeEnter() on almost every page on the router (even on dynamic pages) which non-logged in user will be redirected to /login.
This one works when user enter with direct url /home.
routes: [
{
path: '/home',
name: 'home',
beforeEnter(to, from, next){
if ( to.name !== 'login' && !this.isloggedin ){
next({
path: 'login',
replace: true
})
} else {
next()
}
}
},
...
]
This one only works after user entered the site and route changed
vm.$router.beforeEach((to, from, next)=>{
if ( to.name !== 'login' && !this.isloggedin ){
next({
path: 'login',
replace: true
})
} else {
next();
}
})
Thanks in advance.
It looks like this beforeEach is being defined inside an initialized component, which means the first routing has already occured. Define it in the router module with your routes instead:
const router = new VueRouter({
...
})
router.beforeEach((to, from, next)=>{
if ( to.name !== 'login' && !this.isloggedin ){
next({
path: 'login',
replace: true
})
} else {
next();
}
})
Hopefully you are using Vuex and can import the store for store.state.isloggedin. If not using Vuex yet, this illustrates why it is useful for global state.
For a global and neat solution, you can control the router behavior in the App.vue using the router.beforeResolve(async (to, from, next) => {});.
beforeResolve is better than beforeEach, as beforeResolve will not load the component of the accessed path URL unless you fire manually the next function.
This is very helpful as you'll not render any interafce unless you check the authentication status of the user and then call next().
Example:
router.beforeResolve(async (to, from, next) => {
// Check if the user is authenticated.
let isUserAuthenticated = await apiRequestCustomFunction();
// Redirect user to the login page if not authenticated.
if (!isUserAuthenticated) {
location.replace("https://example.com/signin");
return false;
}
// When next() is called, the router will load the component corresponding
// to the URL path.
next();
});
TIP: You can display a loader while you check if the user is authenticated or not and then take an action (redirect to sign in page or load the app normally).
I'm creating a Single Page Application using Vue front-end, Express and Parse (parse-platform) for back-end. Whenever I authenticate user, I put user's info into session variable req.session.user = result; and then send it back to the client res.status(200).send(req.session);. Whenever user is routing through application, how do securely check if authentication is valid? What I am afraid of, is that the session id that is put into client's cookies could be forged and user would be treated as authenticated. I believe I could send a request to my back-end to check if authentication is valid every time user enters a route but I believe this is not a great idea as routing in vue applications are very quick and if hundreds of users navigating quickly could cause a problem. What else could I do? Or am I doing it/thinking of it the right way?
I use express-session to store client's session into his cookies.
app.use(session({
secret: 'secret_key',
resave: false,
saveUninitialized: true,
cookie: {} }));
This is how I login user:
Parse.User.logIn(username, password).then(result => {
req.session.user = result;
res.status(200).send(req.session);
});
first of all, I recommend using state rather than a session in a single page application.
vuex = https://vuex.vuejs.org/guide/
vue-router have a function called beforeEach.if we defined this function,it's called every time when we call a route. basically request go through this function.
then we can check this user is authenticated or not in this function
ex:-
let router = new Router({
mode: "hash", // https://router.vuejs.org/api/#mode
linkActiveClass: "active",
scrollBehavior: () => ({ y: 0 }),
routes: configRoutes(), // im using function to define all the routes. you can define routes here
});
router.beforeEach((to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (localStorage.getItem("userToken") == null) {
next({
path: "/login",
params: { nextUrl: to.fullPath },
});
} else {
if (!store.state.isAuthenticated) {
next({
path: "/login",
params: { nextUrl: to.fullPath },
});
} else {
next();
}
}
} else {
next();
}
});
after that, we define which route should be authenticated or not. Vue router allows us to define a meta on our routes so we can specify the authenticated routes
ex:-
{
path: "/student",
name: "student",
component: Student,
meta: {
requiresAuth: true,
},
},
now everytime someone enter "/student" url it's gonna check if that user authenticated or not.
this is where I learned this
hope this will help someone.good luck
Well, I'm starting with nuxt and I have following routes:
/home
/dashboard
/login
I want to protect the /dashboard, but only for users logged in with a token in Cookie.
Then i created a middleware
/middleware/auth.js
import Cookie from 'js-cookie'
export default function({ req, redirect }) {
if (process.server) {
if (!req.headers.cookie) return redirect('/login')
const jwtCookie = req.headers.cookie.split(';').find(c => c.trim().startsWith('jwt='))
if (!jwtCookie) return redirect('/login')
} else {
const jwt = Cookie.get('jwt')
if (!jwt) { window.location = '/login' }
}
}
and register the middleware in my layout or dashboard page
<script>
export default {
middleware: 'auth',
}
</script>
when I access /dashboard apparently works perfectly
but the problem is that the middleware is being registered globally, it is running on all pages, all routes
So when you access /home that is a published page, if you do not have the cookie, you end up being redirected to login page
anyone help?
How about creating a condition based on the route.path param ?
export default function({ req, redirect, route }) {
if (!route.path.includes('dashboard')) { // if path doesn't include "dashboard", stop there
return;
}
if (process.server) {
if (!req.headers.cookie) return redirect('/login')
const jwtCookie = req.headers.cookie.split(';').find(c => c.trim().startsWith('jwt='))
if (!jwtCookie) return redirect('/login')
} else {
const jwt = Cookie.get('jwt')
if (!jwt) { window.location = '/login' }
}
}
Therefore you still benefit from the pre-render middleware system.
You probably have registered your middleware/auth.js in your nuxt.config.js.
When you register a middleware in nuxt.config.js, you're registering it globally, meaning it will be called for every route change.
Docs:
https://nuxtjs.org/guide/routing#middleware
In my opinion, you should call them plugin, because of
middleware called by each route changed also you can't use middleware in layout and subComponent, you can use it as plugin and call it manually everywhere also it's reactive and runtime.
path: /plugind/auth.js
import Cookie from 'js-cookie';
export default function({ req, redirect }) {
if (process.server) {
if (!req.headers.cookie) return redirect('/login')
const jwtCookie = req.headers.cookie.split(';').find(c =>
c.trim().startsWith('jwt='))
if (!jwtCookie) return redirect('/login')
} else {
const jwt = Cookie.get('jwt')
if (!jwt) { window.location = '/login'
}
}
}