Open nimashoghi0 opened 1 month ago
To solve the issue of not showing the previous route when directly accessing a page that requires authorization, we need to ensure that the previous route information is stored and retrieved correctly. The solution involves updating the route guard logic to store the previous route in local storage and retrieve it when necessary.
The bug is caused by the fact that when a user directly accesses a protected page (e.g., by opening a new browser tab and navigating to a URL that requires authentication), there is no previous route information available. This results in an empty from
object and an empty auth.redirect()
object. The current implementation does not handle this scenario, leading to the issue described.
src/shims-router.d.ts
Add a property for storing the previous route information in the RouteMeta
interface.
import "vue-router"
import Roles from "./type/Roles"
declare module "vue-router" {
interface RouteMeta {
auth?:
| boolean
| string
| Roles[]
| {
roles?: Roles
redirect?:
| RouteLocationRaw
| ((to: RouteLocationNormalized) => RouteLocationRaw)
notFoundRedirect?:
| RouteLocationRaw
| ((to: RouteLocationNormalized) => RouteLocationRaw)
forbiddenRedirect?:
| RouteLocationRaw
| ((to: RouteLocationNormalized) => RouteLocationRaw)
rolesKey?: string
}
previousRoute?: string // Add this line to store the previous route information
}
}
src/utils/index.ts
Add utility functions for storing and retrieving the previous route information from local storage.
export function storePreviousRoute(route: string): void {
if (isLocalStorage()) {
localStorage.setItem('previousRoute', route);
}
}
export function getPreviousRoute(): string | null {
if (isLocalStorage()) {
return localStorage.getItem('previousRoute');
}
return null;
}
src/Auth.ts
Modify the authentication logic to store the previous route in local storage when redirecting to the login page.
this.options.plugins?.router?.beforeEach(async (to, from, next) => {
if (from.name) {
this.tPrev = from;
storePreviousRoute(from.fullPath);
} else {
const storedPrevRoute = getPreviousRoute();
this.tPrev = storedPrevRoute ? { fullPath: storedPrevRoute } : null;
}
this.tCurrent = to;
await syncStorage(this);
const authMeta = getAuthMeta(to);
processTransitionEach(this, to, authMeta, (redirect) => {
if (!redirect) {
next();
return;
}
next(redirect);
});
});
Clear the previous route on logout.
function logout(auth: Auth, redirect?: RouteLocationRaw) {
$cookie.remove(auth, auth.options.tokenImpersonateKey);
$cookie.remove(auth, auth.options.tokenDefaultKey);
$token.remove(auth, auth.options.tokenImpersonateKey);
$token.remove(auth, auth.options.tokenDefaultKey);
$token.remove(auth, auth.options.staySignedInKey);
$token.remove(auth, auth.options.userKey);
auth.state.loaded = true;
auth.state.authenticated = false;
auth.state.data = null;
localStorage.removeItem('previousRoute');
routerPush(auth, redirect);
}
src/index.ts
Ensure the previous route is stored and retrieved during redirects.
import { computed, ComputedRef, inject } from "vue"
import Auth from "./Auth"
import { authKey } from "./injectionKey"
import Options from "./type/Options"
import AuthDriver, { defineAuthDriver } from "./type/drivers/AuthDriver"
import HttpDriver, { defineHttpDriver } from "./type/drivers/HttpDriver"
import OAuth2Driver, { defineOAuth2Driver } from "./type/drivers/OAuth2Driver"
import { storePreviousRoute } from "./utils/index"
export function createAuth(options: Options) {
const auth = new Auth(options);
router.beforeEach((to, from, next) => {
if (to.meta.auth) {
storePreviousRoute(from.fullPath);
}
next();
});
return auth;
}
export function useAuth(key: symbol | string = authKey): Auth {
return inject(key) as Auth;
}
export function useUser<User extends object>(
key: symbol | string = authKey
): ComputedRef<User | null> {
const auth = useAuth(key);
return computed(() => {
return auth.user();
});
}
export type {
Options,
Auth,
AuthDriver,
HttpDriver,
OAuth2Driver,
defineAuthDriver,
defineHttpDriver,
defineOAuth2Driver,
}
https://domain/requires-auth
).from
object and an empty auth.redirect()
object.By implementing the proposed changes, the previous route information will be correctly stored and retrieved, even when directly accessing a protected page. This should resolve the issue of the empty from
object and auth.redirect()
object.
Click here to create a Pull Request with the proposed solution
Files used for this task:
.redirect()
will only return the page doing the redirect when it comes from vue-router
can you make me a minimal reproducible example?
Hey @tachibana-shin, Thanks for getting in touch with me.
See here: https://github.com/nimashoghi0/vue-auth
When you hit /demo directly, vue-auth-3 redirects to the /login page, and there is no data on the previous page (/demo), vue router also doesn't show any info so that's probably why.
I need to capture this previous data, so I will write my own wrapper to check authorisation, where I can store the information in local storage, it would have been great to use your meta field checker though. Maybe something to consider in the future?
Cheers
Hey there,
Thanks for building this this awesome plugin. My app needs to know the previous route before redirecting to the login page if unauthorised. I was thinking of storing this in a Store for safe keeping.
It seems, if I'm coming from a valid page, then hit a route with meta: {auth: true}, it redirects me and shows me the previous page fine. However if i directly hit that unauthorised page it comes back with an empty from, and also empty auth.redirect() object.
By directly I mean a new chrome tab - then hit https://domain/requires-auth, it doesn't show me the previous page, but if I'm in a https://domain/open-page, then click a link that takes me to https://domain/requires-auth it shows me the previous page.
This is the method I'm using to try and find the previous page:
router.beforeEach((to, from) => { console.log('from'); console.log(from); console.log(auth.redirect()) })
I hope this explanation makes sense. I must be doing something wrong?
Cheers