diff --git a/Services/client/src/App.vue b/Services/client/src/App.vue index ba0f916..cb1fcea 100644 --- a/Services/client/src/App.vue +++ b/Services/client/src/App.vue @@ -1,7 +1,5 @@ - - - - diff --git a/Services/client/src/components/Profile.vue b/Services/client/src/components/Profile.vue index 564c912..a710a44 100644 --- a/Services/client/src/components/Profile.vue +++ b/Services/client/src/components/Profile.vue @@ -1,10 +1,8 @@ diff --git a/Services/client/src/main.ts b/Services/client/src/main.ts index 9354622..4a436d5 100644 --- a/Services/client/src/main.ts +++ b/Services/client/src/main.ts @@ -5,18 +5,18 @@ import th from 'quasar/lang/th' import App from './App.vue' -import HttpService from '@/services/HttpService' import quasarUserOptions from './quasar-user-options' -import router from './router' import 'quasar/src/css/index.sass' +import { login } from './services/KeyCloakService' + +await login() const app = createApp(App) const pinia = createPinia() -app.use(router) +app.use((await import('./router')).default) app.use(pinia) - app.use(Quasar, { ...quasarUserOptions, plugins: { @@ -28,9 +28,7 @@ app.use(Quasar, { app.component( 'full-loader', - defineAsyncComponent(() => import('@/components/FullLoader.vue')) + defineAsyncComponent(() => import('@/components/FullLoader.vue')), ) app.mount('#app') - -HttpService.configureAxiosKeycloak() diff --git a/Services/client/src/router/index.ts b/Services/client/src/router/index.ts index 89c820d..ed79c86 100644 --- a/Services/client/src/router/index.ts +++ b/Services/client/src/router/index.ts @@ -1,73 +1,52 @@ import { createRouter, createWebHistory } from 'vue-router' import UserModule from '@/modules/01_user/router' import AdminModule from '@/modules/02_admin/router' -import KeyCloakService from '@/services/KeyCloakService' +import { getRole, getToken, login } from '@/services/KeyCloakService' +const history = createWebHistory(import.meta.env.BASE_URL) const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), + history, routes: [ { path: '/', name: 'UserModule', component: () => import('@/views/MainLayout.vue'), - beforeEnter: (_to, _from, next) => { - const token = KeyCloakService.GetAccesToken() - if (token) { - next() - } else { - KeyCloakService.CallLogin(() => { - const tokenAfterLogin = KeyCloakService.GetAccesToken() - const roles = KeyCloakService.GetUserRoles() + beforeEnter: async (_to, _from, next) => { + const token = await getToken() - if ( - tokenAfterLogin && - (roles.includes('user') || roles.includes('admin')) - ) { - next() - } else { - console.error('ไม่สามารถดึง Token หลังจากล็อกอินได้') - next('/') - } - }) - } + if (token) return next() + + await login(async () => { + if (await getToken()) return next() + return next('/') + }) }, children: [...UserModule], - meta: { - statusAccount: false, - }, + meta: { statusAccount: false }, }, { path: '/admin', name: 'AdminModule', component: () => import('@/views/MainLayout.vue'), - beforeEnter: (_to, _from, next) => { - const token = KeyCloakService.GetAccesToken() - if (token) { - next() - } else { - KeyCloakService.CallLogin(() => { - const tokenAfterLogin = KeyCloakService.GetAccesToken() - const roles = KeyCloakService.GetUserRoles() - console.log(roles) + beforeEnter: async (_to, _from, next) => { + const token = await getToken() - if (tokenAfterLogin && roles.includes('admin')) { - next() - } else { - console.error('ไม่สามารถดึง Token หลังจากล็อกอินได้') - next('/') - } - }) - } - }, - meta: { - statusAccount: true, + if (token) return next() + + await login(async () => { + const token = await getToken() + const roles = getRole() + + if (token && roles.includes('admin')) { + return next() + } + + return next('/') + }) }, + meta: { statusAccount: true }, children: [...AdminModule], }, - /** - * 404 Not Found - * ref: https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route - */ { path: '/:pathMatch(.*)*', name: 'NotFound', diff --git a/Services/client/src/services/HttpService.ts b/Services/client/src/services/HttpService.ts index e31e1f7..64bbf6f 100644 --- a/Services/client/src/services/HttpService.ts +++ b/Services/client/src/services/HttpService.ts @@ -1,36 +1,11 @@ -import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios' import axios from 'axios' -import KeyCloakService from '@/services/KeyCloakService' +import { getToken } from './KeyCloakService' -const HttpMethods = { - GET: 'GET', - POST: 'POST', - DELETE: 'DELETE', -} +const instance = axios.create() -const _axios = axios.create() -const cb = (config: InternalAxiosRequestConfig) => { - config.headers.Authorization = `Bearer ${KeyCloakService.GetAccesToken()}` +instance.interceptors.request.use(async (config) => { + config.headers.Authorization = `Bearer ${await getToken()}` return config -} +}) -const configureAxiosKeycloak = (): void => { - _axios.interceptors.request.use( - (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { - if (KeyCloakService.IsLoggedIn()) { - KeyCloakService.UpdateToken(cb(config)) - } - return config - } - ) -} - -const getAxiosClient = (): AxiosInstance => _axios - -const HttpService = { - HttpMethods, - configureAxiosKeycloak, - getAxiosClient, -} - -export default HttpService +export default instance diff --git a/Services/client/src/services/KeyCloakService.ts b/Services/client/src/services/KeyCloakService.ts index c0e2805..e16335e 100644 --- a/Services/client/src/services/KeyCloakService.ts +++ b/Services/client/src/services/KeyCloakService.ts @@ -1,77 +1,42 @@ import Keycloak from 'keycloak-js' -const keycloakInstance = new Keycloak() +const keycloak = new Keycloak() -interface CallbackOneParam { - (param1: T1): T2 -} -/** - * Initializes Keycloak instance and calls the provided callback function if successfully authenticated. - * - * @param onAuthenticatedCallback - */ -const Login = (onAuthenticatedCallback: CallbackOneParam): void => { - keycloakInstance - .init({ onLoad: 'login-required' }) - .then(function (authenticated) { - authenticated ? onAuthenticatedCallback() : alert('non authenticated') - }) - .catch((e) => { - console.dir(e) - console.log(`keycloak init exception: ${e}`) +export async function login(cb?: (...args: any[]) => void) { + const auth = await keycloak + .init({ + onLoad: 'login-required', + responseMode: 'query', + checkLoginIframe: false, }) + .catch((e) => console.dir(e)) + + if (auth && cb) cb() } -const UserName = (): string | undefined => - keycloakInstance?.tokenParsed?.preferred_username +export async function logout() { + await keycloak.logout() +} -const Token = (): string | undefined => keycloakInstance?.token -const IdToken = (): string | undefined => keycloakInstance?.idToken +export async function getToken() { + await keycloak.updateToken(60).catch(() => login()) + return keycloak.token +} -const LogOut = () => keycloakInstance.logout() +export function getUsername(): string { + return keycloak.tokenParsed?.preferred_username +} -/* -const UserRoles = (): string[] | undefined => { - if (keycloakInstance.resourceAccess === undefined) return undefined; - if (keycloakInstance.resourceAccess["express-client"] === undefined) return undefined; +export function getRole(): string[] { + const decoded = keycloak.tokenParsed - return keycloakInstance.resourceAccess["express-client"].roles; -}; -*/ -const UserRoles = () => { - const decoded = DecodeToken() - - if (decoded && decoded.resource_access) { - return decoded.resource_access[decoded.azp ?? ''].roles + if (decoded && decoded.resource_access && decoded.azp) { + return decoded.resource_access[decoded.azp].roles } + return [] } -const updateToken = (successCallback: any) => - keycloakInstance.updateToken(5).then(successCallback).catch(doLogin) - -const doLogin = keycloakInstance.login - -const isLoggedIn = () => !!keycloakInstance.token - -const DecodeToken = () => { - return keycloakInstance.tokenParsed +export function isLoggedIn() { + return !!keycloak.token } -const DecodeIdToken = () => { - return keycloakInstance.idTokenParsed -} - -const KeycloakService = { - CallLogin: Login, - GetUserName: UserName, - GetAccesToken: Token, - GetIdToken: IdToken, - CallLogOut: LogOut, - GetUserRoles: UserRoles, - UpdateToken: updateToken, - IsLoggedIn: isLoggedIn, - GetDecodeToken: DecodeToken, - GetDecodeIdToken: DecodeIdToken, -} - -export default KeycloakService diff --git a/Services/client/src/stores/tree-data.ts b/Services/client/src/stores/tree-data.ts index fae7662..635a4c5 100644 --- a/Services/client/src/stores/tree-data.ts +++ b/Services/client/src/stores/tree-data.ts @@ -1,9 +1,8 @@ import { ref } from 'vue' import { defineStore } from 'pinia' import { useLoader } from '@/stores/loader' -import HttpService from '@/services/HttpService' +import axiosClient from '@/services/HttpService' -const axiosClient = HttpService.getAxiosClient() const apiEndpoint: string = import.meta.env.VITE_API_ENDPOINT export interface EhrFolder { diff --git a/Services/client/src/views/MainLayout.vue b/Services/client/src/views/MainLayout.vue index 988fc88..7524d41 100644 --- a/Services/client/src/views/MainLayout.vue +++ b/Services/client/src/views/MainLayout.vue @@ -29,9 +29,9 @@ const { loader } = storeToRefs(loaderStore) - + - +