Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support array syntax in beforeEnter #2688

Open
posva opened this issue Mar 28, 2019 · 7 comments
Open

Support array syntax in beforeEnter #2688

posva opened this issue Mar 28, 2019 · 7 comments
Labels
feature request fixed on 4.x This issue has been already fixed on the v4 but exists in v3

Comments

@posva
Copy link
Member

posva commented Mar 28, 2019

What problem does this feature solve?

This will allow splitting guard logic in smaller functions and making it easy to chain them. It's similar to express middlewares

What does the proposed API look like?

{ path: '/some', beforeEnter: [authGuard, adminGuard] }

current solution to this: https://www.npmjs.com/package/vue-router-multiguard

Edit: Supporting arrays for in-component guards seems unnecessary as a component guard is specific to a component, they are not reused. On top of that, the typing of this would probably break so I removed them from the proposal.

@posva posva added the fixed on 4.x This issue has been already fixed on the v4 but exists in v3 label Mar 18, 2020
@posva
Copy link
Member Author

posva commented Mar 18, 2020

For those interested in this feature request, is there any use case that requires array syntax for the in-component guards? I can see how having an array for beforeEnter is useful to reuse small middlewares that are unrelated to component but I can't find a use case for in-component guards

@Saeid-Za
Copy link

Saeid-Za commented Jun 4, 2020

At this moment, I can't think of a use-case that would require an Array based syntax for guards at component level, but are we sure that there wouldn't be? and would there be additional cost to add that syntax to component level?
In my opinion, it is better to support this new syntax at any level just to keep the convention.

@iteniasim
Copy link

I used vue-router-multiguard when i needed to pass multiple guards to some pages on the previous project i worked on.
This was the situation on that project and doesnt seem like a rare use case.
There is the auth guard and guest guard for checking authentication, but even when user is authenticated some pages had to be accessible by the subscribed users only, so i created another guard, subscription guard, there was probablly admin guard too, for similar reasons, and if you think i could have done it better some other way, i'd like to hear about that, because like i said its not a rare use case

@posva posva changed the title Support array syntax for guards Support array syntax in beforeEnter May 2, 2021
@BonBonSlick
Copy link

arrays for in-component guards seems unnecessary as a component guard is specific to a component, they are not reused.

right, something like isAuthenticatedGuard will never be reused anywhere, its specific guard for specific component for specific route only... or not? What if we have more guards like localization? Captchas and other security which will be unnecessary to call if first failed? See Symfony Voters, they have somthing similar for PHP.
Yes, package solves the issue and there are plenty of other workarounds.
Talking about

    "vue-router": "^3.0.1",
    "vuex-router-sync": "^5.0.0",

@prescience-data
Copy link

Not sure if this has been resolved within vue-router already (might have missed something in the docs), but fwiw this is my current implementation:

import {
  createRouter,
  createWebHistory,
  NavigationGuard,
  NavigationGuardNext,
  RouteLocationRaw,
  Router
} from "vue-router"

import {
  doesNotHaveTeams,
  hasTeams,
  isAuthenticated,
  isNotAuthenticated
} from "./guards"

/** Define provider signature **/
export type MultiGuard = (guards: NavigationGuard[]) => NavigationGuard

/**
 * Provide routes with the ability to execute multiple guards.
 * This is implemented due to a gap in Vue Router functionality which limits callbacks to a single guard.
 *
 * @see https://github.com/vuejs/vue-router/issues/2688
 * @param {NavigationGuard[]} guards
 * @return {NavigationGuard}
 */
export const multiGuard: MultiGuard =
  (guards: NavigationGuard[]): NavigationGuard =>
  async (to, from, next) => {
    // Let sharedNext callback break execution if a guard is triggered.
    let resolved: boolean = false

    // Create a "shared" next callback which breaks the iteration loop if "next()" is called within guard.
    // TODO: Implement "boolean" and "callback" cases...
    const sharedNext = (location?: RouteLocationRaw): void => {
      if (location) {
        resolved = true
        next(location)
      }
    }

    // Iterate through provided guards array until complete or redirected.
    for (const guard of guards) {
      await guard(to, from, sharedNext as NavigationGuardNext)
      // Check if the sharedNext callback triggered a break.
      if (resolved) {
        break
      }
    }
    // If none of the guards triggered a redirect, allow the original intent.
    if (!resolved) {
      next()
    }
  }

/**
 * Example multiguard implementation.
 *
 * @type {Router}
 */
export const router: Router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: "/teams",
      name: TEAMS_ROUTE,
      beforeEnter: multiGuard([isAuthenticated, hasTeams]),
      component: () => import("/@src/pages/teams.vue")
    },
    {
      path: "/onboarding",
      name: ONBOARDING_ROUTE,
      beforeEnter: multiGuard([isAuthenticated, doesNotHaveTeams]),
      component: () => import("/@src/pages/onboarding.vue")
    },
    {
      path: "/sign-in",
      name: SIGN_IN_ROUTE,
      beforeEnter: multiGuard([isNotAuthenticated]),
      component: () => import("/@src/pages/auth/sign-in.vue")
    }
  ]
})

Example route guard.

/**
 * Example route guard.
 * 
 * @param {RouteLocationNormalized} _to
 * @param {RouteLocationNormalized} _from
 * @param {NavigationGuardNext} next
 * @return {Promise<void>}
 */
export const isAuthenticated: NavigationGuard = async (
  _to,
  _from,
  next
): Promise<void> => {
  if (!await session().isAuthenticated()) {
    next({
      name: SIGN_IN_ROUTE
    })
  } else {
    next()
  }
}

@daniilgri
Copy link

Nice to have!

@adamreisnz
Copy link

Is this still not supported in Vue router?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request fixed on 4.x This issue has been already fixed on the v4 but exists in v3
Projects
None yet
Development

No branches or pull requests

7 participants