diff --git a/app/src/components/AuthenticationPart.vue b/app/src/components/AuthenticationPart.vue index 3d2da99..d1bfef4 100644 --- a/app/src/components/AuthenticationPart.vue +++ b/app/src/components/AuthenticationPart.vue @@ -1,11 +1,36 @@ - + diff --git a/app/src/components/HeaderPart.vue b/app/src/components/HeaderPart.vue index 60e000b..54d4591 100644 --- a/app/src/components/HeaderPart.vue +++ b/app/src/components/HeaderPart.vue @@ -1,4 +1,14 @@ - + diff --git a/app/src/library.js b/app/src/library.js new file mode 100644 index 0000000..f637869 --- /dev/null +++ b/app/src/library.js @@ -0,0 +1,37 @@ +/* import the fontawesome core */ +import { library } from '@fortawesome/fontawesome-svg-core' + +/* import specific icons */ +import { + faBars, + faArrowLeft, + faList, + faGear, + faTriangleExclamation, + faExpand, + faTerminal, + faArrowRotateLeft, + faArrowRightFromBracket, +} from '@fortawesome/free-solid-svg-icons' + +import { + faLightbulb, + faCircleQuestion, + faCircleRight, +} from '@fortawesome/free-regular-svg-icons' + +/* add icons to the library */ +library.add( + faBars, + faArrowLeft, + faTriangleExclamation, + faGear, + faExpand, + faList, + faLightbulb, + faCircleQuestion, + faArrowRotateLeft, + faCircleRight, + faTerminal, + faArrowRightFromBracket +) diff --git a/app/src/main.js b/app/src/main.js index 55e4c22..d5067e7 100644 --- a/app/src/main.js +++ b/app/src/main.js @@ -1,59 +1,11 @@ import { createApp } from 'vue' -import { createRouter, createWebHistory } from 'vue-router' import './style.css' - -/* import the fontawesome core */ -import { library } from '@fortawesome/fontawesome-svg-core' - /* import font awesome icon component */ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' - -/* import specific icons */ -import { - faBars, - faArrowLeft, - faList, - faGear, - faTriangleExclamation, - faExpand, - faTerminal, - faArrowRotateLeft, -} from '@fortawesome/free-solid-svg-icons' - -import { - faLightbulb, - faCircleQuestion, - faCircleRight, -} from '@fortawesome/free-regular-svg-icons' +import './library' import App from './App.vue' -import GamePage from './views/GamePage.vue' -import AuthPage from './views/AuthPage.vue' - -/* add icons to the library */ -library.add( - faBars, - faArrowLeft, - faTriangleExclamation, - faGear, - faExpand, - faList, - faLightbulb, - faCircleQuestion, - faArrowRotateLeft, - faCircleRight, - faTerminal -) - -const routes = [ - { path: '/', name: 'Home', component: AuthPage }, - { path: '/game', name: 'Game', component: GamePage }, -] - -const router = createRouter({ - history: createWebHistory(), - routes, -}) +import router from './router' const app = createApp(App) diff --git a/app/src/router.js b/app/src/router.js new file mode 100644 index 0000000..ae61a28 --- /dev/null +++ b/app/src/router.js @@ -0,0 +1,46 @@ +import { createRouter, createWebHistory } from 'vue-router' +import GamePage from './views/GamePage.vue' +import AuthPage from './views/AuthPage.vue' +import { isAuthenticated } from './utils/auth' + +const routes = [ + { + path: '/', + name: 'auth', + component: AuthPage, + meta: { requiresGuest: true }, + }, + { + path: '/game', + name: 'game', + component: GamePage, + meta: { requiresAuth: true }, + }, +] + +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +router.beforeEach((to, from, next) => { + console.log(to, from) + const isAuthed = isAuthenticated.value + // Check if the route requires authentication + if (to.meta.requiresAuth && !isAuthed) { + // Check if the user isn't authenticated + // Redirect to the login page + next({ + name: 'auth', + query: { redirect: to.fullPath }, // Optionally pass a redirect parameter + }) + } else if (to.meta.requiresGuest && isAuthed) { + next({ + name: 'game', // Or any default authenticated user route + }) + } else { + next() // Always call next() at the end + } +}) + +export default router diff --git a/app/src/utils/api.js b/app/src/utils/api.js index 67ad944..4a5e011 100644 --- a/app/src/utils/api.js +++ b/app/src/utils/api.js @@ -2,6 +2,17 @@ import axios from 'axios' const api = axios.create({ baseURL: '/api/v1', + headers: { + 'Content-Type': 'application/json', + }, }) +export const setToken = (token) => { + if (token) { + api.defaults.headers.common.Authorization = `Bearer ${token}` + } else { + delete api.defaults.headers.common.Authorization + } +} + export default api diff --git a/app/src/utils/auth.js b/app/src/utils/auth.js new file mode 100644 index 0000000..3c0eda2 --- /dev/null +++ b/app/src/utils/auth.js @@ -0,0 +1,47 @@ +import { computed, ref, watch } from 'vue' +import api, { setToken } from './api' + +const TOKEN_KEY = 'JWT_TOKEN' + +const myStorage = window.localStorage +const myToken = ref(myStorage.getItem(TOKEN_KEY)) + +if (myToken.value) { + setToken(myToken.value) +} + +// watch the token and store it in localStorage +watch(myToken, (newToken) => { + if (newToken) { + console.log('update token:', newToken) + myStorage.setItem(TOKEN_KEY, newToken) + setToken(newToken) + } else { + myStorage.removeItem(TOKEN_KEY) + setToken(null) + } +}) + +export const getToken = () => { + return myToken.value +} + +export const login = async (username, password) => { + try { + const res = await api.post('/auth/login', { username, password }) + const { token } = res.data + myToken.value = token + return true + } catch (err) { + if (err.response?.status === 401) { + return false + } + throw err + } +} + +export const logout = () => { + myToken.value = null +} + +export const isAuthenticated = computed(() => !!myToken.value) diff --git a/app/src/utils/socket.js b/app/src/utils/socket.js index 69050ca..ec719ce 100644 --- a/app/src/utils/socket.js +++ b/app/src/utils/socket.js @@ -1,5 +1,7 @@ import io from 'socket.io-client' +import { getToken } from './auth' + class SocketWrapper { constructor() { this.socket = null @@ -16,6 +18,9 @@ class SocketWrapper { query: { session_id: session._id, }, + auth: { + token: getToken(), + }, }) this.socket.on('connect', () => { diff --git a/server/src/api/routes/auth.js b/server/src/api/routes/auth.js index be4cb8c..9295dd9 100644 --- a/server/src/api/routes/auth.js +++ b/server/src/api/routes/auth.js @@ -11,7 +11,7 @@ const router = Router() router.post( '/login', - passport.authenticate('local', { session: false }), + passport.authenticate('local', { session: false, failWithError: true }), issueToken ) diff --git a/server/src/api/routes/index.js b/server/src/api/routes/index.js index 0c801f3..94d95dc 100644 --- a/server/src/api/routes/index.js +++ b/server/src/api/routes/index.js @@ -15,12 +15,12 @@ router.use('/auth', authRouter) router.use('/quests', quests) router.use( '/sessions', - passport.authenticate('jwt', { session: false }), + passport.authenticate('jwt', { session: false, failWithError: true }), sessions ) router.use( '/commands', - passport.authenticate('jwt', { session: false }), + passport.authenticate('jwt', { session: false, failWithError: true }), commands ) diff --git a/server/swagger.yaml b/server/swagger.yaml index 564ab6d..571616a 100644 --- a/server/swagger.yaml +++ b/server/swagger.yaml @@ -62,6 +62,7 @@ paths: properties: quest_id: type: string + example: helloworld responses: '201': description: session created