Browse Source
refactor: session handling (#2398)
* refactor session handling
* simplify
pull/2402/head
Bernd Storath
5 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with
37 additions and
30 deletions
-
.vscode/extensions.json
-
.vscode/settings.json
-
src/app/middleware/auth.global.ts
-
src/app/pages/admin.vue
-
src/app/pages/clients/[id].vue
-
src/app/pages/index.vue
-
src/app/pages/login.vue
-
src/app/pages/me.vue
-
src/app/stores/auth.ts
-
src/server/api/session.get.ts
-
src/shared/utils/permissions.ts
|
|
|
@ -3,7 +3,7 @@ |
|
|
|
"aaron-bond.better-comments", |
|
|
|
"dbaeumer.vscode-eslint", |
|
|
|
"antfu.goto-alias", |
|
|
|
"esbenp.prettier-vscode", |
|
|
|
"prettier.prettier-vscode", |
|
|
|
"yoavbls.pretty-ts-errors", |
|
|
|
"bradlc.vscode-tailwindcss", |
|
|
|
"vue.volar", |
|
|
|
|
|
|
|
@ -1,22 +1,22 @@ |
|
|
|
{ |
|
|
|
"editor.tabSize": 2, |
|
|
|
"editor.useTabStops": false, |
|
|
|
"editor.defaultFormatter": "esbenp.prettier-vscode", |
|
|
|
"editor.defaultFormatter": "prettier.prettier-vscode", |
|
|
|
"editor.formatOnSave": true, |
|
|
|
"editor.codeActionsOnSave": { |
|
|
|
"source.fixAll.eslint": "always" |
|
|
|
}, |
|
|
|
"[vue]": { |
|
|
|
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|
|
|
"editor.defaultFormatter": "prettier.prettier-vscode" |
|
|
|
}, |
|
|
|
"[typescript]": { |
|
|
|
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|
|
|
"editor.defaultFormatter": "prettier.prettier-vscode" |
|
|
|
}, |
|
|
|
"[json]": { |
|
|
|
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|
|
|
"editor.defaultFormatter": "prettier.prettier-vscode" |
|
|
|
}, |
|
|
|
"[markdown]": { |
|
|
|
"editor.defaultFormatter": "esbenp.prettier-vscode", |
|
|
|
"editor.defaultFormatter": "prettier.prettier-vscode", |
|
|
|
"editor.tabSize": 4, |
|
|
|
"editor.useTabStops": false |
|
|
|
}, |
|
|
|
|
|
|
|
@ -4,25 +4,27 @@ export default defineNuxtRouteMiddleware(async (to) => { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const event = useRequestEvent(); |
|
|
|
|
|
|
|
const authStore = useAuthStore(); |
|
|
|
const userData = await authStore.getSession(); |
|
|
|
authStore.userData = await authStore.getSession(event); |
|
|
|
|
|
|
|
// skip login if already logged in
|
|
|
|
if (to.path === '/login') { |
|
|
|
if (userData?.username) { |
|
|
|
if (authStore.userData?.username) { |
|
|
|
return navigateTo('/', { redirectCode: 302 }); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Require auth for every page other than Login
|
|
|
|
if (!userData?.username) { |
|
|
|
if (!authStore.userData?.username) { |
|
|
|
return navigateTo('/login', { redirectCode: 302 }); |
|
|
|
} |
|
|
|
|
|
|
|
// Check for admin access
|
|
|
|
if (to.path.startsWith('/admin')) { |
|
|
|
if (!hasPermissions(userData, 'admin', 'any')) { |
|
|
|
if (!hasPermissions(authStore.userData, 'admin', 'any')) { |
|
|
|
return abortNavigation('Not allowed to access Admin Panel'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -38,9 +38,6 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
const authStore = useAuthStore(); |
|
|
|
authStore.update(); |
|
|
|
|
|
|
|
const { t } = useI18n(); |
|
|
|
|
|
|
|
const route = useRoute(); |
|
|
|
|
|
|
|
@ -206,9 +206,7 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script lang="ts" setup> |
|
|
|
const authStore = useAuthStore(); |
|
|
|
const globalStore = useGlobalStore(); |
|
|
|
authStore.update(); |
|
|
|
|
|
|
|
const route = useRoute(); |
|
|
|
const id = route.params.id as string; |
|
|
|
|
|
|
|
@ -29,9 +29,6 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
const authStore = useAuthStore(); |
|
|
|
authStore.update(); |
|
|
|
|
|
|
|
const globalStore = useGlobalStore(); |
|
|
|
const clientsStore = useClientsStore(); |
|
|
|
|
|
|
|
|
|
|
|
@ -67,9 +67,6 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
const authStore = useAuthStore(); |
|
|
|
authStore.update(); |
|
|
|
|
|
|
|
const toast = useToast(); |
|
|
|
const { t } = useI18n(); |
|
|
|
|
|
|
|
|
|
|
|
@ -120,7 +120,6 @@ |
|
|
|
import { encodeQR } from 'qr'; |
|
|
|
|
|
|
|
const authStore = useAuthStore(); |
|
|
|
authStore.update(); |
|
|
|
|
|
|
|
const name = ref(authStore.userData?.name); |
|
|
|
const email = ref(authStore.userData?.email); |
|
|
|
|
|
|
|
@ -1,18 +1,25 @@ |
|
|
|
import type { H3Event } from 'h3'; |
|
|
|
import type { SharedPublicUser } from '~~/shared/utils/permissions'; |
|
|
|
|
|
|
|
export const useAuthStore = defineStore('Auth', () => { |
|
|
|
const { data: userData, refresh: update } = useFetch('/api/session', { |
|
|
|
method: 'get', |
|
|
|
}); |
|
|
|
const userData = useState<SharedPublicUser | null>('user-data', () => null); |
|
|
|
|
|
|
|
async function getSession() { |
|
|
|
async function getSession(event?: H3Event) { |
|
|
|
const fetch = event?.$fetch || $fetch; |
|
|
|
try { |
|
|
|
const { data } = await useFetch('/api/session', { |
|
|
|
const data = await fetch('/api/session', { |
|
|
|
method: 'get', |
|
|
|
}); |
|
|
|
return data.value; |
|
|
|
return data; |
|
|
|
} catch { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async function update() { |
|
|
|
const data = await getSession(); |
|
|
|
userData.value = data; |
|
|
|
} |
|
|
|
|
|
|
|
return { userData, update, getSession }; |
|
|
|
}); |
|
|
|
|
|
|
|
@ -1,9 +1,14 @@ |
|
|
|
import type { SharedPublicUser } from '~~/shared/utils/permissions'; |
|
|
|
|
|
|
|
export default defineEventHandler(async (event) => { |
|
|
|
const session = await useWGSession(event); |
|
|
|
|
|
|
|
if (!session.data.userId) { |
|
|
|
// not logged in
|
|
|
|
return null; |
|
|
|
throw createError({ |
|
|
|
statusCode: 401, |
|
|
|
statusMessage: 'Not authenticated', |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
const user = await Database.users.get(session.data.userId); |
|
|
|
@ -21,5 +26,5 @@ export default defineEventHandler(async (event) => { |
|
|
|
name: user.name, |
|
|
|
email: user.email, |
|
|
|
totpVerified: user.totpVerified, |
|
|
|
}; |
|
|
|
} satisfies SharedPublicUser; |
|
|
|
}); |
|
|
|
|
|
|
|
@ -45,6 +45,11 @@ type SharedUserType = |
|
|
|
| Pick<UserType, 'id' | 'role'> |
|
|
|
| (Pick<UserType, 'id'> & { role: BrandedNumber }); |
|
|
|
|
|
|
|
export type SharedPublicUser = Pick< |
|
|
|
UserType, |
|
|
|
'id' | 'username' | 'name' | 'email' | 'totpVerified' |
|
|
|
> & { role: BrandedNumber }; |
|
|
|
|
|
|
|
type PermissionCheck<Key extends keyof Permissions> = |
|
|
|
| boolean |
|
|
|
| ((user: SharedUserType, data: Permissions[Key]['dataType']) => boolean); |
|
|
|
|