Skip to main content
@spa-tools/core-router

Pre-Route Auth Check

The @spa-tools/core-router package provides a way to execute pre-processing logic before navigating to a route. A common scenario that falls into this workflow is performing a check to ensure the user is authenticated prior to route execution.

import { CoreRouter, routesFactory } from '@spa-tools/core-router';

const createMyRoutes = routesFactory();

// here we create two routes, one that requires auth and one that does not
//
// the authCheck is a 100% custom property that we will use to determine
// whether or not authorization is required before allowing navigation to
// the route; it is NOT a built-in property of the route object and can be
// anything we want, which makes route flow control very flexible/powerful
const myRoutes = createMyRoutes({
loginRoute: {
authCheck: false,
path: '/login',
},
membersOnlyRoute: {
authCheck: true,
path: '/members-only',
},
});

// this is just a contrived function that emulates an auth check by
// returning a promise that resolves to a random boolean
function fakeAuthCheck() {
const randomAuthState = Math.random() < 0.5 ? true : false;
return Promise.resolve(randomAuthState);
}

// here we create a new instance of the CoreRouter class and pass in
// our routes object; we also pass in an object that implements two
// router lifecycle callbacks
const myRouter = CoreRouter.initialize(myRoutes, {
onRouteRequest: async (routeRequest) => {
// if the new route has an authCheck property set to `true`
// then we will perform an async check to see if the user
// is authorized to navigate to this route
const isAuthorized = routeRequest.newRoute.authCheck ? await fakeAuthCheck() : true;

if (!isAuthorized) {
// if the user is not authorized to navigate to the route, we
// will redirect to the login route; we pass the original route
// request as state so that the login view can hypothetically
// redirect back to the original route after login
//
// we could've just called the navigate method here, but to keep
// the browser history clean, we instead perform a redirect by
// returning a 2-element tuple containing redirect route and state
return [myRoutes.loginRoute, routeRequest];
}

// this means either the user is authorized or the route does not
// require authorization, so we return true to allow the navigation
//
// we could've also omitted the return altogether and it would have
// the same effect, but returning true is more explicit
return true;
},
onRouteChange: (routeChange) => {
// this is where we would perform any post-processing logic, which
// typically means rendering the requested route. How to do this
// ranges based on the UI framework being used, so here we simply
// log the route path to the console.
//
// If you're using React or want to create an abstraction for a
// different rendering package/framework, you should definitely
// check out the Core React Router abstraction, which has the
// rendering logic sweetly baked in.
console.log(`TODO: Render the approved route: ${routeChange.route.path}`);
},
});

myRouter.navigate(
// request to nav to a route that requires auth
myRoutes.membersOnlyRoute
);