* authentication for specific routes

feature: #1
This commit is contained in:
2023-01-01 02:07:21 +01:00
parent af3b12344f
commit 23c40c5468
10 changed files with 145 additions and 11 deletions

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

26
package-lock.json generated
View File

@@ -16,6 +16,8 @@
"@vuelidate/core": "^2.0.0", "@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0", "@vuelidate/validators": "^2.0.0",
"axios": "^1.2.2", "axios": "^1.2.2",
"jwt-decode": "^3.1.2",
"keycloak-js": "^12.0.4",
"vue": "^3.2.45", "vue": "^3.2.45",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
@@ -2766,6 +2768,11 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "dev": true
}, },
"node_modules/base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -5863,6 +5870,11 @@
"url": "https://opencollective.com/js-sdsl" "url": "https://opencollective.com/js-sdsl"
} }
}, },
"node_modules/js-sha256": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -5923,6 +5935,20 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/jwt-decode": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"node_modules/keycloak-js": {
"version": "12.0.4",
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-12.0.4.tgz",
"integrity": "sha512-O/BHtyiDrZrUnKBrVF8POojqd3gmhuiDw4FiI+FbnB14nu7G5jKFrKYZa9Q0JYKIZXHJOBzSaKQcMp2WUI+zmA==",
"dependencies": {
"base64-js": "1.3.1",
"js-sha256": "0.9.0"
}
},
"node_modules/kleur": { "node_modules/kleur": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",

View File

@@ -6,7 +6,7 @@
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore" "lint": "eslint . --ext .vue,.ts,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/fontawesome-svg-core": "^6.2.1",
@@ -17,6 +17,8 @@
"@vuelidate/core": "^2.0.0", "@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0", "@vuelidate/validators": "^2.0.0",
"axios": "^1.2.2", "axios": "^1.2.2",
"jwt-decode": "^3.1.2",
"keycloak-js": "^12.0.4",
"vue": "^3.2.45", "vue": "^3.2.45",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",

View File

@@ -0,0 +1,7 @@
<html>
<body>
<script>
parent.postMessage(location.href, location.origin)
</script>
</body>
</html>

View File

@@ -0,0 +1,42 @@
import Keycloak from 'keycloak-js'
let keycloak = null
function setup() {
keycloak = new Keycloak({
url: 'https://auth.denysoft.eu/',
realm: 'FST',
clientId: 'frontend'
})
return keycloak
.init({
onLoad: 'login-required',
checkLoginIframe: false
})
.then(function (authenticated) {
setInterval(() => {
keycloak
.updateToken(90)
.then(refreshed => {
if (refreshed) {
console.info('Token refreshed ' + refreshed)
}
})
.catch(() => {
console.error('Failed to refresh token')
})
}, 6000)
})
.catch(function () {
alert('failed to initialize')
})
}
function login() {
keycloak.login()
}
function getKeycloak() {
return keycloak
}
export default { setup, login, getKeycloak }

View File

@@ -0,0 +1,5 @@
<template>
<div>
<h1>Forbidden!</h1>
</div>
</template>

View File

@@ -7,14 +7,15 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faUserSecret } from '@fortawesome/free-solid-svg-icons' import { faUserSecret } from '@fortawesome/free-solid-svg-icons'
import './assets/main.css'
import AuthHelper from './authentication/AuthHelper'
/* add icons to the library */ /* add icons to the library */
library.add(faUserSecret) library.add(faUserSecret)
import './assets/main.css'
const i18n = createI18n() const i18n = createI18n()
const app = createApp(App) let app = createApp(App)
app.use(router) app.use(router)
app.use(i18n) app.use(i18n)

View File

@@ -1,26 +1,58 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue' import HomeView from '../views/HomeView.vue'
import AuthHelper from '../authentication/AuthHelper'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [
{ {
path: '/', path: '/',
name: 'home', name: 'main',
component: HomeView component: HomeView,
meta: {
requiresAuth: false
}
}, },
{ {
path: '/about', path: '/about',
name: 'about', name: 'about',
// this page is lazy-loaded when the route is visited. // this page is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue') component: () => import('../views/AboutView.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/forbidden',
name: 'forbidden',
component: () => import('../components/ForbiddenPage.vue'),
meta: {
requiresAuth: false
}
}, },
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
name: 'notfound', name: 'notfound',
component: () => import('../components/PageNotFound.vue') component: () => import('../components/PageNotFound.vue'),
meta: {
requiresAuth: false
}
} }
] ]
}) })
router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAuth === true) {
if (AuthHelper.getKeycloak() === null) {
await AuthHelper.setup()
}
if (!AuthHelper.getKeycloak().authenticated) {
next('/forbidden')
}
next()
} else {
next()
}
})
export default router export default router

View File

@@ -1,9 +1,11 @@
import { createStore } from 'vuex' import { createStore } from 'vuex'
import moduleA from './moduleA.module' import moduleA from './moduleA.module'
import moduleB from './moduleB.module' import moduleB from './moduleB.module'
import keycloak from './keycloak.module'
export default createStore({ export default createStore({
modules: { modules: {
keycloak: keycloak,
a: moduleA, a: moduleA,
b: moduleB b: moduleB
} }

View File

@@ -0,0 +1,20 @@
export default {
state: () => ({
keycloak: null
}),
mutations: {
initKeycloak(state, data) {
state.keycloak = data
}
},
actions: {
initKeycloak({ commit }) {
commit('initKeycloak')
}
},
getters: {
getKeycloak(state) {
return state.keycloak
}
}
}