diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3c64177..9b15ac2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,5 @@
{
+ "dotnet.defaultSolution": "FiscalOS.slnx",
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"tsconfig.json": "tsconfig.*.json, env.d.ts, typed-router.d.ts",
diff --git a/src/FiscalOS.Web/package-lock.json b/src/FiscalOS.Web/package-lock.json
index aa73a7b..bb26642 100644
--- a/src/FiscalOS.Web/package-lock.json
+++ b/src/FiscalOS.Web/package-lock.json
@@ -8,6 +8,7 @@
"name": "fiscalos-web",
"version": "0.0.0",
"dependencies": {
+ "jwt-decode": "^4.0.0",
"pinia": "^3.0.4",
"vue": "beta",
"vue-router": "^5.0.2"
@@ -5145,6 +5146,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
diff --git a/src/FiscalOS.Web/package.json b/src/FiscalOS.Web/package.json
index bf50d1c..d943a11 100644
--- a/src/FiscalOS.Web/package.json
+++ b/src/FiscalOS.Web/package.json
@@ -17,6 +17,7 @@
"format": "prettier --write --experimental-cli src/"
},
"dependencies": {
+ "jwt-decode": "^4.0.0",
"pinia": "^3.0.4",
"vue": "beta",
"vue-router": "^5.0.2"
diff --git a/src/FiscalOS.Web/src/App.vue b/src/FiscalOS.Web/src/App.vue
index 289d3c3..ee7c659 100644
--- a/src/FiscalOS.Web/src/App.vue
+++ b/src/FiscalOS.Web/src/App.vue
@@ -1,8 +1,10 @@
- FiscalOS
+
diff --git a/src/FiscalOS.Web/src/router/index.ts b/src/FiscalOS.Web/src/router/index.ts
index 6c13dc5..12978a3 100644
--- a/src/FiscalOS.Web/src/router/index.ts
+++ b/src/FiscalOS.Web/src/router/index.ts
@@ -1,8 +1,49 @@
+import { useUserStore } from "@/stores/userStore";
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
- routes: [],
+ routes: [
+ {
+ path: '/public',
+ redirect: '/public/login',
+ beforeEnter: () => {
+ const userStore = useUserStore();
+
+ if (userStore.user) {
+ return { path: '/' };
+ }
+
+ return true;
+ },
+ children: [
+ {
+ path: 'login',
+ component: () => import('../views/LoginView.vue')
+ }
+ ],
+ },
+ {
+ path: '/',
+ beforeEnter: () => {
+ // TODO: Handle expired token
+ // Try to refresh
+ // if refresh fails log out
+ // redirect to login page
+ // if refresh succeeds
+ // log user in and allow
+ // to pass
+ const userStore = useUserStore();
+
+ if (userStore.user === null) {
+ return { path: '/public/login' }
+ }
+
+ return true;
+ },
+ component: () => import('../views/HomeView.vue'),
+ }
+ ],
});
export default router;
diff --git a/src/FiscalOS.Web/src/stores/userStore.ts b/src/FiscalOS.Web/src/stores/userStore.ts
new file mode 100644
index 0000000..e38560c
--- /dev/null
+++ b/src/FiscalOS.Web/src/stores/userStore.ts
@@ -0,0 +1,49 @@
+import { jwtDecode } from 'jwt-decode';
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+
+export const USER_KEY = 'onxGraphAuth';
+
+export type User = {
+ id: string;
+ expiresAtInSeconds: number;
+ token: string;
+};
+
+type JwtTokenPayload = {
+ sub: string;
+ exp: number;
+};
+
+function getUserFromLocalStorage(): User | null {
+ const user = localStorage.getItem(USER_KEY);
+ return user === null ? null : JSON.parse(user);
+}
+
+export type UserStore = ReturnType;
+
+export const useUserStore = defineStore('userStore', () => {
+ const user = ref(getUserFromLocalStorage());
+
+ function logUserIn(jwtToken: string) {
+ const { sub, exp } = jwtDecode(jwtToken);
+ const loggedInUser: User = {
+ id: sub,
+ expiresAtInSeconds: exp,
+ token: jwtToken,
+ };
+ localStorage.setItem(USER_KEY, JSON.stringify(loggedInUser));
+ user.value = loggedInUser;
+ }
+
+ function logUserOut() {
+ localStorage.removeItem(USER_KEY);
+ user.value = null;
+ }
+
+ return {
+ user: user,
+ logUserIn,
+ logUserOut,
+ };
+});
diff --git a/src/FiscalOS.Web/src/views/HomeView.vue b/src/FiscalOS.Web/src/views/HomeView.vue
new file mode 100644
index 0000000..f81ad63
--- /dev/null
+++ b/src/FiscalOS.Web/src/views/HomeView.vue
@@ -0,0 +1,7 @@
+
+
+
+ Home View
+
+
+
diff --git a/src/FiscalOS.Web/src/views/LoginView.vue b/src/FiscalOS.Web/src/views/LoginView.vue
new file mode 100644
index 0000000..806f702
--- /dev/null
+++ b/src/FiscalOS.Web/src/views/LoginView.vue
@@ -0,0 +1,47 @@
+
+
+
+ Login View
+
+
+
+
diff --git a/src/FiscalOS.Web/public/favicon.ico b/src/FiscalOS.Web/static/favicon.ico
similarity index 100%
rename from src/FiscalOS.Web/public/favicon.ico
rename to src/FiscalOS.Web/static/favicon.ico
diff --git a/src/FiscalOS.Web/vite.config.ts b/src/FiscalOS.Web/vite.config.ts
index 9d74b77..2f9dbbd 100644
--- a/src/FiscalOS.Web/vite.config.ts
+++ b/src/FiscalOS.Web/vite.config.ts
@@ -7,6 +7,7 @@ import vueDevTools from 'vite-plugin-vue-devtools'
import mkcert from 'vite-plugin-mkcert';
export default defineConfig({
+ publicDir: 'static',
plugins: [
vue(),
vueJsx(),