Implement client-side route protection with role-based access control. The auth store now extracts the role claim from JWT tokens and exposes isAdmin. Router guards enforce three levels of access: guestOnly (redirect authenticated users), requiresAuth (redirect unauthenticated to login with redirect param), and requiresAdmin (redirect non-admin users to home). Changes: - utils/jwt.ts: JWT payload parser using base64url decode (new file) - authStore: add role ref, isAdmin computed, extractRole from JWT payload - router: add route metadata (requiresAuth, requiresAdmin, guestOnly) and beforeEach guard with getActivePinia() safety for test environments - OrdersPage.vue, AdminPage.vue: placeholder pages (new files) - LoginPage.vue, RegisterPage.vue: use route.query.redirect after auth - Router.spec.ts: 14 tests covering all guard scenarios - authStore.spec.ts: tests for role extraction, isAdmin, role persistence - LoginPage.spec.ts: test for redirect query param after login - auth-guards.spec.ts: 7 Playwright E2E tests for guard behavior - login.spec.ts: fix seed user credentials (test@bilhalsning.se)
22 lines
521 B
TypeScript
22 lines
521 B
TypeScript
export interface JwtPayload {
|
|
sub?: string
|
|
role?: string
|
|
exp?: number
|
|
iat?: number
|
|
}
|
|
|
|
export function parseJwtPayload(token: string): JwtPayload {
|
|
try {
|
|
const base64Url = token.split('.')[1]
|
|
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
|
|
const jsonPayload = decodeURIComponent(
|
|
atob(base64)
|
|
.split('')
|
|
.map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
|
|
.join(''),
|
|
)
|
|
return JSON.parse(jsonPayload)
|
|
} catch {
|
|
return {}
|
|
}
|
|
}
|