Syncing Lots Of Auth Tweaks

This commit is contained in:
Annika Merris 2024-02-10 17:33:57 -05:00
parent baf8e0f954
commit a1aa171bb1
11 changed files with 227 additions and 28 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -14,6 +14,7 @@
"format": "prettier --write src/" "format": "prettier --write src/"
}, },
"dependencies": { "dependencies": {
"@zitadel/vue": "^1.0.0",
"axios": "^1.6.5", "axios": "^1.6.5",
"axios-retry": "^4.0.0", "axios-retry": "^4.0.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",

View file

@ -1,3 +1,3 @@
{ {
"apiBaseUrl": "http://localhost:3000" "apiBaseURL": "http://localhost:3000"
} }

View file

@ -13,6 +13,8 @@ import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components' import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives' import * as directives from 'vuetify/directives'
import axios from 'axios' import axios from 'axios'
import zitadelAuth from '@/services/zitadelAuth'
import { apiBaseURL } from './types/ConfigSymbols'
const vuetify = createVuetify({ const vuetify = createVuetify({
components, components,
@ -25,20 +27,32 @@ const vuetify = createVuetify({
} }
}) })
const app = createApp(App) // Stuff to get OIDC auth working through Zitadel
const pinia = createPinia() declare module 'vue' {
app.use(pinia) interface ComponentCustomProperties {
app.use(router) $zitadel: typeof zitadelAuth
app.use(vuetify) }
}
// Fetch my config zitadelAuth.oidcAuth.startup().then(() => {
axios const app = createApp(App)
.get('/config.json?noCache=' + Date.now()) const pinia = createPinia()
.then((resp) => {
app.config.globalProperties.$apiBaseUrl = resp.data.apiBaseUrl
app.mount('#app') app.use(router)
}) app.use(vuetify)
.catch((err) => { app.use(pinia)
console.log(err)
}) 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)
})
})

View file

@ -1,3 +1,4 @@
import zitadelAuth from '@/services/zitadelAuth'
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({ const router = createRouter({
@ -9,26 +10,46 @@ const router = createRouter({
component: () => import('@/views/HomeView.vue') component: () => import('@/views/HomeView.vue')
}, },
{ {
path: "/blessing-power", path: '/blessing-power',
name: "blessing-power", name: 'blessing-power',
component: () => import('@/views/BlessingPowerView.vue') component: () => import('@/views/BlessingPowerView.vue')
}, },
{ {
path: "/fellow-power", path: '/fellow-power',
name: "fellow-power", name: 'fellow-power',
component: () => import('@/views/FellowPowerView.vue') component: () => import('@/views/FellowPowerView.vue')
}, },
{ {
path: "/intimacy-power", path: '/intimacy-power',
name: "intimacy-power", name: 'intimacy-power',
component: () => import('@/views/IntimacyPowerView.vue') component: () => import('@/views/IntimacyPowerView.vue')
}, },
{ {
path: "/test", path: '/test',
name: "test", name: 'test',
component: () => import('@/views/Test.vue') 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 export default router

View file

@ -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

View file

@ -4,6 +4,8 @@ import { defineStore } from 'pinia'
import { computed, ref, toRaw } from 'vue' import { computed, ref, toRaw } from 'vue'
import axiosRetry from 'axios-retry' import axiosRetry from 'axios-retry'
import { getCurrentInstance } from 'vue' import { getCurrentInstance } from 'vue'
import { apiBaseURL as apiBaseURLKey } from '@/types/ConfigSymbols'
import { inject } from 'vue'
const BLESSING = 1 const BLESSING = 1
const INTIMACY = 2 const INTIMACY = 2
@ -26,7 +28,9 @@ export const usePowerItems = defineStore('powerItems', () => {
const fellowPowerItems = ref(new Map<string, PowerItem>()) const fellowPowerItems = ref(new Map<string, PowerItem>())
const intimacyPowerItems = ref(new Map<string, PowerItem>()) const intimacyPowerItems = ref(new Map<string, PowerItem>())
const isLoadComplete = ref(false) 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, { axiosRetry(axios, {
retries: 3, retries: 3,
@ -34,8 +38,9 @@ export const usePowerItems = defineStore('powerItems', () => {
}) })
async function fetchPowerItems() { async function fetchPowerItems() {
const apiBaseURL = inject(apiBaseURLKey)
axios axios
.get(apiBaseUrl + '/powerItem/byType/' + BLESSING + '/asMap', noCacheConfig) .get(apiBaseURL + '/powerItems/byType/' + BLESSING + '/asMap', noCacheConfig)
.then((resp) => { .then((resp) => {
const plainMap = new Map<string, PowerItem>( const plainMap = new Map<string, PowerItem>(
Object.entries(JSON.parse(localStorage.getItem(BLESSING_POWER_ITEM_STORAGE) || '{}')) Object.entries(JSON.parse(localStorage.getItem(BLESSING_POWER_ITEM_STORAGE) || '{}'))
@ -51,7 +56,7 @@ export const usePowerItems = defineStore('powerItems', () => {
console.log(err) console.log(err)
}) })
axios axios
.get(apiBaseUrl + '/powerItem/byType/' + FELLOW + '/asMap', noCacheConfig) .get(apiBaseURL + '/powerItems/byType/' + FELLOW + '/asMap', noCacheConfig)
.then((resp) => { .then((resp) => {
const plainMap = new Map<string, PowerItem>( const plainMap = new Map<string, PowerItem>(
Object.entries(JSON.parse(localStorage.getItem(FELLOW_POWER_ITEM_STORAGE) || '{}')) Object.entries(JSON.parse(localStorage.getItem(FELLOW_POWER_ITEM_STORAGE) || '{}'))
@ -67,7 +72,7 @@ export const usePowerItems = defineStore('powerItems', () => {
console.log(err) console.log(err)
}) })
axios axios
.get(apiBaseUrl + '/powerItem/byType/' + INTIMACY + '/asMap', noCacheConfig) .get(apiBaseURL + '/powerItems/byType/' + INTIMACY + '/asMap', noCacheConfig)
.then((resp) => { .then((resp) => {
const plainMap = new Map<string, PowerItem>( const plainMap = new Map<string, PowerItem>(
Object.entries(JSON.parse(localStorage.getItem(INTIMACY_POWER_ITEM_STORAGE) || '{}')) Object.entries(JSON.parse(localStorage.getItem(INTIMACY_POWER_ITEM_STORAGE) || '{}'))

View file

@ -0,0 +1,7 @@
import type { InjectionKey } from "vue";
const apiBaseURL = Symbol() as InjectionKey<string>
export {
apiBaseURL
}

22
src/views/Admin.vue Normal file
View file

@ -0,0 +1,22 @@
<template>
<div class="admin">
<div>
<h1>
This is an administrator page.
</h1>
<h2>
You can see it because you have the role "admin" in your ZITADEL project.
</h2>
</div>
</div>
</template>
<style>
@media (min-width: 1024px) {
.admin {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

61
src/views/Login.vue Normal file
View file

@ -0,0 +1,61 @@
<template>
<div class="userinfo">
<div>
<h1>This is a login-protected page</h1>
<h2>
The following profile data is extended by information from ZITADELs userinfo endpoint.
</h2>
<p>
<ul class="claims">
<li v-for="c in claims" :key="c.key">
<strong>{{ c.key }}</strong
>: {{ c.value }}
</li>
</ul>
</p>
</div>
<button @click="signout">Sign Out</button>
</div>
</template>
<style>
@media (min-width: 1024px) {
.userinfo {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>
<script lang="ts">
import { getCurrentInstance } from 'vue';
import { ref } from 'vue';
import { defineComponent } from 'vue'
export default defineComponent({
computed: {
user() {
return this.$zitadel.oidcAuth.userProfile
},
claims() {
if (this.user) {
return Object.keys(this.user).map((key) => ({
key,
value: this.user[key]
}))
}
return []
}
}
})
</script>
<script setup lang="ts">
const zitadel = ref(getCurrentInstance()?.appContext.config.globalProperties.$zitadel!)
const user = ref(zitadel.value.oidcAuth.userProfile)
const signout = function() {
if (user.value) {
zitadel.value.oidcAuth.signOut()
}
}
</script>

22
src/views/NoAccess.vue Normal file
View file

@ -0,0 +1,22 @@
<template>
<div class="admin">
<div>
<h1>
Access denied
</h1>
<h2>
You don't have the role "admin" in your ZITADEL project.
</h2>
</div>
</div>
</template>
<style>
@media (min-width: 1024px) {
.admin {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>