svelte-router icon indicating copy to clipboard operation
svelte-router copied to clipboard

Async route guarding?

Open ejolly opened this issue 6 years ago • 15 comments

Thanks for the great package! I was wondering if it's currently possible to guard a route using an asynchronous function. It doesn't seem to work with the implementation I have in the example below but I wasn't sure if I was just doing something wrong:

#routes.js


# Check user consent status using a call to google firebase
async function checkConsent() {
  try {
    let resp = await db.collection('users').doc('test-user').get();
    let consent = resp.data().consent;
    return consent ? true : false;
  } catch (error) {
    console.error(error);
  }
}

# Render the instructions route depending on the consent status of the user
export const routes = [
{
    name: 'instructions',
    component: Instructions,
    onlyIf: { guard: checkConsent, redirect: '/exitSurvey' }
  }
]

ejolly avatar Nov 09 '19 01:11 ejolly

Hello @ejolly

Async functions are not supported now. But I'll add that to the TODO list. I'll let you know once it's implemented.

jorgegorka avatar Nov 11 '19 16:11 jorgegorka

Hello @ejolly

Async functions are not supported now. But I'll add that to the TODO list. I'll let you know once it's implemented.

are pull requests welcomed?

pckkkkkk avatar Nov 14 '19 18:11 pckkkkkk

Of course @pckkkkkk

They are more than welcome.

jorgegorka avatar Nov 15 '19 09:11 jorgegorka

[ EDIT ] THE WORKAROUND BELOW DOES NOT WORK. The isConsented() function returns always false because the IIFE pattern immediately returns a PENDING promise :( I think there is no way to bypass the problem excepted modifying the way the router handle the guard function to accept promises as returned results.

Hi @ejolly
I faced this problem too. As a workaround I used the IIFE pattern (Immediatly Invoked Function Execution). Here is an implementation example. Hope it'll help ;)

// Check user consent status using a call to google firebase
async function checkConsent() {
  try {
    let resp = await db.collection('users').doc('test-user').get();
    let consent = resp.data().consent;
    return consent ? true : false;
  } catch (error) {
    console.error(error);
  }
}

// This function will wait for the promise resolution 
// (or rejection) of your async function before returning.
function isConsented() {
  let consent false;
  // IIFE pattern is here 
  (async function() {
    consent = await checkConsent(); // (if you prefer, you can also directly include the content of your checkConsent() function here)
  })();
  // The line below will be executed once the promise 
  // returned by the checkConsent() function will be resolved.
  return consent;
}

// Render the instructions route depending on the consent status of the user
export const routes = [
{
    name: 'instructions',
    component: Instructions,
    onlyIf: { guard: isConsented, redirect: '/exitSurvey' }
  }
]

webcoder31 avatar Feb 28 '20 18:02 webcoder31

In the same vein, it would probably be a good idea to have async component resolution for code splitting.

PierBover avatar Mar 29 '20 17:03 PierBover

Hi @jorgegorka, First of all, best wishes for 2021 ! ;) I was wondering if the status of this enhancement request evolved since last year ? Regards

webcoder31 avatar Jan 20 '21 07:01 webcoder31

Hi @jorgegorka, any plans to implement this enhancement yet?

moalamri avatar Mar 24 '21 03:03 moalamri

@moalamri If @jorgegorka ok with it, i can implement this feature

si3nloong avatar Mar 27 '21 05:03 si3nloong

@si3nloong you have done great work for this awesome library. I'm sure @jorgegorka is totally open for PRs as he mentioned above. This will be an awesome addition, so thank you in advance.

moalamri avatar Mar 27 '21 06:03 moalamri

@si3nloong also i want to mention that I've tried to implement this, but I was uncertain about either I make a new method where it is called guardAsync or make the existing method as a promise, because trying to implement a method to detect the function type is not a good practice, so making it as a promise for all seems to be the best approach. Anyways eventually I didn't do anything, but I'm putting hope on you :)

moalamri avatar Mar 27 '21 06:03 moalamri

solution for firebase

routes.js

import { auth } from './firebase';

function userIsAuthenticated() { let user = auth.currentUser; if (!user) { return false; } else return true; }

const routes = [ ... { name: 'xxx', component: XXX, onlyIf: { guard: userIsAuthenticated, redirect: 'login' }, }, ... ]

solar-path avatar Apr 01 '21 10:04 solar-path

Any updates on this? It's been open for quite a while. Or any suggestions for a workaround, because I need to fetch from the server to know about a guard.

stemaDev avatar Feb 12 '22 15:02 stemaDev

@stemaDev I ended up fetching allowed components names from the database after a successful login. Then dynamically load the allowed components.

moalamri avatar Feb 12 '22 20:02 moalamri

After the login I redirected to a guarded page, I wanted that to double check from the server, but instead I am setting a cookie on the server and checking that in the guard method. So it's a similar workaround as your. Thanks for thq quick answer btw.

stemaDev avatar Feb 14 '22 07:02 stemaDev

I think we both have different approach :) Perhaps if you followed mine it would be better and I will explain why. But I need you to elaborate for me... If by cookie you mean jwt payload?

moalamri avatar Feb 14 '22 14:02 moalamri