mirror of
https://forgejo.merr.is/annika/isl-vue3.git
synced 2025-12-13 15:58:58 -05:00
Syncing Lots Of Auth Tweaks
This commit is contained in:
parent
baf8e0f954
commit
a1aa171bb1
11 changed files with 227 additions and 28 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"apiBaseUrl": "http://localhost:3000"
|
"apiBaseURL": "http://localhost:3000"
|
||||||
}
|
}
|
||||||
44
src/main.ts
44
src/main.ts
|
|
@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
46
src/services/zitadelAuth.ts
Normal file
46
src/services/zitadelAuth.ts
Normal 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
|
||||||
|
|
@ -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) || '{}'))
|
||||||
|
|
|
||||||
7
src/types/ConfigSymbols.ts
Normal file
7
src/types/ConfigSymbols.ts
Normal 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
22
src/views/Admin.vue
Normal 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
61
src/views/Login.vue
Normal 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
22
src/views/NoAccess.vue
Normal 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>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue