diff --git a/bun.lockb b/bun.lockb index 4a06b74..24df2e4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5bc75ce..f5575d5 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "format": "prettier --write src/" }, "dependencies": { + "@zitadel/vue": "^1.0.0", "axios": "^1.6.5", "axios-retry": "^4.0.0", "pinia": "^2.1.7", diff --git a/public/config.json b/public/config.json index 4d58f70..01a3b83 100644 --- a/public/config.json +++ b/public/config.json @@ -1,3 +1,3 @@ { - "apiBaseUrl": "http://localhost:3000" + "apiBaseURL": "http://localhost:3000" } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 4dce636..427009f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,8 @@ import { createVuetify } from 'vuetify' import * as components from 'vuetify/components' import * as directives from 'vuetify/directives' import axios from 'axios' +import zitadelAuth from '@/services/zitadelAuth' +import { apiBaseURL } from './types/ConfigSymbols' const vuetify = createVuetify({ components, @@ -25,20 +27,32 @@ const vuetify = createVuetify({ } }) -const app = createApp(App) -const pinia = createPinia() -app.use(pinia) -app.use(router) -app.use(vuetify) +// Stuff to get OIDC auth working through Zitadel +declare module 'vue' { + interface ComponentCustomProperties { + $zitadel: typeof zitadelAuth + } +} -// Fetch my config -axios - .get('/config.json?noCache=' + Date.now()) - .then((resp) => { - app.config.globalProperties.$apiBaseUrl = resp.data.apiBaseUrl +zitadelAuth.oidcAuth.startup().then(() => { + const app = createApp(App) + const pinia = createPinia() - app.mount('#app') - }) - .catch((err) => { - console.log(err) - }) + app.use(router) + app.use(vuetify) + app.use(pinia) + + app.config.globalProperties.$zitadel = zitadelAuth + + // Fetch my config + axios + .get('/config.json?noCache=' + Date.now()) + .then((resp) => { + app.provide(apiBaseURL, resp.data.apiBaseURL) + + app.mount('#app') + }) + .catch((err) => { + console.log(err) + }) +}) diff --git a/src/router/index.ts b/src/router/index.ts index ad0cbae..bbdb682 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,3 +1,4 @@ +import zitadelAuth from '@/services/zitadelAuth' import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ @@ -9,26 +10,46 @@ const router = createRouter({ component: () => import('@/views/HomeView.vue') }, { - path: "/blessing-power", - name: "blessing-power", + path: '/blessing-power', + name: 'blessing-power', component: () => import('@/views/BlessingPowerView.vue') }, { - path: "/fellow-power", - name: "fellow-power", + path: '/fellow-power', + name: 'fellow-power', component: () => import('@/views/FellowPowerView.vue') }, { - path: "/intimacy-power", - name: "intimacy-power", + path: '/intimacy-power', + name: 'intimacy-power', component: () => import('@/views/IntimacyPowerView.vue') }, { - path: "/test", - name: "test", + path: '/test', + name: 'test', component: () => import('@/views/Test.vue') + }, + + { + path: '/login', + name: 'login', + meta: { + authName: zitadelAuth.oidcAuth.authName + }, + component: () => import('@/views/Login.vue') + }, + { + path: '/admin', + name: 'admin', + meta: { + authName: zitadelAuth.oidcAuth.authName + }, + component: () => + zitadelAuth.hasRole('admin') ? import('@/views/Admin.vue') : import('@/views/NoAccess.vue') } ] }) +zitadelAuth.oidcAuth.useRouter(router) + export default router diff --git a/src/services/zitadelAuth.ts b/src/services/zitadelAuth.ts new file mode 100644 index 0000000..f22420e --- /dev/null +++ b/src/services/zitadelAuth.ts @@ -0,0 +1,46 @@ +import {createZITADELAuth} from "@zitadel/vue"; +import {User} from "oidc-client"; + +const zitadelAuth = createZITADELAuth({ + project_resource_id: '252968011466539011', + client_id: "252968365600079875@isl", + issuer: "http://localhost:8080/", +}) + +// handle events +zitadelAuth.oidcAuth.events.addAccessTokenExpiring(function() { + // eslint-disable-next-line no-console + console.log('access token expiring') +}) + +zitadelAuth.oidcAuth.events.addAccessTokenExpired(function() { + // eslint-disable-next-line no-console + console.log('access token expired') +}) + +zitadelAuth.oidcAuth.events.addSilentRenewError(function(err: Error) { + // eslint-disable-next-line no-console + console.error('silent renew error', err) +}) + +zitadelAuth.oidcAuth.events.addUserLoaded(function(user: User) { + // eslint-disable-next-line no-console + console.log('user loaded', user) +}) + +zitadelAuth.oidcAuth.events.addUserUnloaded(function() { + // eslint-disable-next-line no-console + console.log('user unloaded') +}) + +zitadelAuth.oidcAuth.events.addUserSignedOut(function() { + // eslint-disable-next-line no-console + console.log('user signed out') +}) + +zitadelAuth.oidcAuth.events.addUserSessionChanged(function() { + // eslint-disable-next-line no-console + console.log('user session changed') +}) + +export default zitadelAuth diff --git a/src/stores/powerItems.ts b/src/stores/powerItems.ts index 35dd629..e77cc3c 100644 --- a/src/stores/powerItems.ts +++ b/src/stores/powerItems.ts @@ -4,6 +4,8 @@ import { defineStore } from 'pinia' import { computed, ref, toRaw } from 'vue' import axiosRetry from 'axios-retry' import { getCurrentInstance } from 'vue' +import { apiBaseURL as apiBaseURLKey } from '@/types/ConfigSymbols' +import { inject } from 'vue' const BLESSING = 1 const INTIMACY = 2 @@ -26,7 +28,9 @@ export const usePowerItems = defineStore('powerItems', () => { const fellowPowerItems = ref(new Map()) const intimacyPowerItems = ref(new Map()) const isLoadComplete = ref(false) - const apiBaseUrl = getCurrentInstance()?.appContext.config.globalProperties.$apiBaseUrl + const auth = getCurrentInstance()?.appContext.config.globalProperties.$zitadel + auth?.oidcAuth.mgr.getUser().then(res => console.log(res?.id_token)) + console.log(auth?.oidcAuth.accessToken) axiosRetry(axios, { retries: 3, @@ -34,8 +38,9 @@ export const usePowerItems = defineStore('powerItems', () => { }) async function fetchPowerItems() { + const apiBaseURL = inject(apiBaseURLKey) axios - .get(apiBaseUrl + '/powerItem/byType/' + BLESSING + '/asMap', noCacheConfig) + .get(apiBaseURL + '/powerItems/byType/' + BLESSING + '/asMap', noCacheConfig) .then((resp) => { const plainMap = new Map( Object.entries(JSON.parse(localStorage.getItem(BLESSING_POWER_ITEM_STORAGE) || '{}')) @@ -51,7 +56,7 @@ export const usePowerItems = defineStore('powerItems', () => { console.log(err) }) axios - .get(apiBaseUrl + '/powerItem/byType/' + FELLOW + '/asMap', noCacheConfig) + .get(apiBaseURL + '/powerItems/byType/' + FELLOW + '/asMap', noCacheConfig) .then((resp) => { const plainMap = new Map( Object.entries(JSON.parse(localStorage.getItem(FELLOW_POWER_ITEM_STORAGE) || '{}')) @@ -67,7 +72,7 @@ export const usePowerItems = defineStore('powerItems', () => { console.log(err) }) axios - .get(apiBaseUrl + '/powerItem/byType/' + INTIMACY + '/asMap', noCacheConfig) + .get(apiBaseURL + '/powerItems/byType/' + INTIMACY + '/asMap', noCacheConfig) .then((resp) => { const plainMap = new Map( Object.entries(JSON.parse(localStorage.getItem(INTIMACY_POWER_ITEM_STORAGE) || '{}')) diff --git a/src/types/ConfigSymbols.ts b/src/types/ConfigSymbols.ts new file mode 100644 index 0000000..aed3f97 --- /dev/null +++ b/src/types/ConfigSymbols.ts @@ -0,0 +1,7 @@ +import type { InjectionKey } from "vue"; + +const apiBaseURL = Symbol() as InjectionKey + +export { + apiBaseURL +} \ No newline at end of file diff --git a/src/views/Admin.vue b/src/views/Admin.vue new file mode 100644 index 0000000..52c2f7f --- /dev/null +++ b/src/views/Admin.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/src/views/Login.vue b/src/views/Login.vue new file mode 100644 index 0000000..b56f985 --- /dev/null +++ b/src/views/Login.vue @@ -0,0 +1,61 @@ + + + + + + diff --git a/src/views/NoAccess.vue b/src/views/NoAccess.vue new file mode 100644 index 0000000..d1c9e80 --- /dev/null +++ b/src/views/NoAccess.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file