Browse Source

refactor: session handling (#2398)

* refactor session handling

* simplify
pull/2402/head
Bernd Storath 5 months ago
committed by GitHub
parent
commit
51558c7027
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .vscode/extensions.json
  2. 10
      .vscode/settings.json
  3. 10
      src/app/middleware/auth.global.ts
  4. 3
      src/app/pages/admin.vue
  5. 2
      src/app/pages/clients/[id].vue
  6. 3
      src/app/pages/index.vue
  7. 3
      src/app/pages/login.vue
  8. 1
      src/app/pages/me.vue
  9. 19
      src/app/stores/auth.ts
  10. 9
      src/server/api/session.get.ts
  11. 5
      src/shared/utils/permissions.ts

2
.vscode/extensions.json

@ -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",

10
.vscode/settings.json

@ -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
},

10
src/app/middleware/auth.global.ts

@ -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');
}
}

3
src/app/pages/admin.vue

@ -38,9 +38,6 @@
</template>
<script setup lang="ts">
const authStore = useAuthStore();
authStore.update();
const { t } = useI18n();
const route = useRoute();

2
src/app/pages/clients/[id].vue

@ -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;

3
src/app/pages/index.vue

@ -29,9 +29,6 @@
</template>
<script setup lang="ts">
const authStore = useAuthStore();
authStore.update();
const globalStore = useGlobalStore();
const clientsStore = useClientsStore();

3
src/app/pages/login.vue

@ -67,9 +67,6 @@
</template>
<script setup lang="ts">
const authStore = useAuthStore();
authStore.update();
const toast = useToast();
const { t } = useI18n();

1
src/app/pages/me.vue

@ -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);

19
src/app/stores/auth.ts

@ -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 };
});

9
src/server/api/session.get.ts

@ -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;
});

5
src/shared/utils/permissions.ts

@ -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);

Loading…
Cancel
Save