minimize cookie and simplify roles
All checks were successful
Build and Publish / build-release (push) Successful in 2m11s
All checks were successful
Build and Publish / build-release (push) Successful in 2m11s
This commit is contained in:
@@ -16,21 +16,19 @@ export function useOrganizationSelection() {
|
|||||||
const { data: session } = useAuth()
|
const { data: session } = useAuth()
|
||||||
|
|
||||||
const organizations = computed<OrganizationInfo[]>(() => {
|
const organizations = computed<OrganizationInfo[]>(() => {
|
||||||
const allOrgRoles = (session.value?.user as any)?.allOrgRoles as Record<string, Record<string, Record<string, string>>> | undefined
|
const allOrgRoles = (session.value?.user as any)?.roles as Record<string, Record<string, string>> | undefined
|
||||||
if (!allOrgRoles) {
|
if (!allOrgRoles) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: OrganizationInfo[] = []
|
const result: OrganizationInfo[] = []
|
||||||
for (const roles of Object.values(allOrgRoles)) {
|
for (const [role, orgMap] of Object.entries(allOrgRoles)) {
|
||||||
for (const [role, orgMap] of Object.entries(roles)) {
|
|
||||||
for (const [orgId, orgDomain] of Object.entries(orgMap)) {
|
for (const [orgId, orgDomain] of Object.entries(orgMap)) {
|
||||||
if (!result.find(o => o.orgId === orgId)) {
|
if (!result.find(o => o.orgId === orgId)) {
|
||||||
result.push({ orgId, orgDomain: orgDomain as string, role, orgSubDomain: extractSubdomain(orgDomain) })
|
result.push({ orgId, orgDomain: orgDomain as string, role, orgSubDomain: extractSubdomain(orgDomain) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
result.sort((a, b) => a.orgDomain.localeCompare(b.orgDomain))
|
result.sort((a, b) => a.orgDomain.localeCompare(b.orgDomain))
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
inherit pname version src;
|
inherit pname version src;
|
||||||
pnpm = pkgs.pnpm;
|
pnpm = pkgs.pnpm;
|
||||||
fetcherVersion = 3;
|
fetcherVersion = 3;
|
||||||
hash = "sha256-QjmYXOv8JliJhN+5XfA1+OnBIf4I/6s84LSisJlBdTo=";
|
hash = "sha256-Z045R87Mgu4FReVqcgCn2PR4THMlcZoG3NEuBy6JEwI=";
|
||||||
};
|
};
|
||||||
|
|
||||||
env.NUXT_TELEMETRY_DISABLED = 1;
|
env.NUXT_TELEMETRY_DISABLED = 1;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"@nuxt/ui": "^4.5.0",
|
"@nuxt/ui": "^4.5.0",
|
||||||
"@zitadel/nuxt-auth": "^1.0.0",
|
"@zitadel/nuxt-auth": "^1.0.0",
|
||||||
"jspdf": "^4.2.0",
|
"jspdf": "^4.2.0",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"nuxt": "^4.3.1",
|
"nuxt": "^4.3.1",
|
||||||
"nuxt-open-fetch": "^0.13.8",
|
"nuxt-open-fetch": "^0.13.8",
|
||||||
"tailwindcss": "^4.2.1",
|
"tailwindcss": "^4.2.1",
|
||||||
|
|||||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -16,13 +16,16 @@ importers:
|
|||||||
version: 1.15.2(@typescript-eslint/utils@8.59.1(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.32)(eslint@10.2.1(jiti@2.6.1))(magicast@0.5.2)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))
|
version: 1.15.2(@typescript-eslint/utils@8.59.1(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.32)(eslint@10.2.1(jiti@2.6.1))(magicast@0.5.2)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))
|
||||||
'@nuxt/ui':
|
'@nuxt/ui':
|
||||||
specifier: ^4.5.0
|
specifier: ^4.5.0
|
||||||
version: 4.6.1(@tiptap/extensions@3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.3))(@tiptap/pm@3.22.3))(@tiptap/y-tiptap@3.0.3(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6)
|
version: 4.6.1(@tiptap/extensions@3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.3))(@tiptap/pm@3.22.3))(@tiptap/y-tiptap@3.0.3(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.10.1)(jwt-decode@4.0.0)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6)
|
||||||
'@zitadel/nuxt-auth':
|
'@zitadel/nuxt-auth':
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0(@auth/core@0.40.0)(@nuxt/schema@4.4.2)(magicast@0.5.2)(radix3@1.1.2)(vue@3.5.32(typescript@5.9.3))
|
version: 1.0.0(@auth/core@0.40.0)(@nuxt/schema@4.4.2)(magicast@0.5.2)(radix3@1.1.2)(vue@3.5.32(typescript@5.9.3))
|
||||||
jspdf:
|
jspdf:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.1
|
version: 4.2.1
|
||||||
|
jwt-decode:
|
||||||
|
specifier: ^4.0.0
|
||||||
|
version: 4.0.0
|
||||||
nuxt:
|
nuxt:
|
||||||
specifier: ^4.3.1
|
specifier: ^4.3.1
|
||||||
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.32)(cac@6.7.14)(db0@0.3.4)(eslint@10.2.1(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup-plugin-visualizer@7.0.1(rollup@4.60.1))(rollup@4.60.1)(srvx@0.11.15)(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(yaml@2.8.3)
|
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@parcel/watcher@2.5.6)(@vue/compiler-sfc@3.5.32)(cac@6.7.14)(db0@0.3.4)(eslint@10.2.1(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(rollup-plugin-visualizer@7.0.1(rollup@4.60.1))(rollup@4.60.1)(srvx@0.11.15)(terser@5.46.1)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(yaml@2.8.3)
|
||||||
@@ -3415,6 +3418,10 @@ packages:
|
|||||||
jspdf@4.2.1:
|
jspdf@4.2.1:
|
||||||
resolution: {integrity: sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==}
|
resolution: {integrity: sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==}
|
||||||
|
|
||||||
|
jwt-decode@4.0.0:
|
||||||
|
resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
keyv@4.5.4:
|
keyv@4.5.4:
|
||||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||||
|
|
||||||
@@ -6019,7 +6026,7 @@ snapshots:
|
|||||||
rc9: 3.0.1
|
rc9: 3.0.1
|
||||||
std-env: 4.1.0
|
std-env: 4.1.0
|
||||||
|
|
||||||
'@nuxt/ui@4.6.1(@tiptap/extensions@3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.3))(@tiptap/pm@3.22.3))(@tiptap/y-tiptap@3.0.3(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6)':
|
'@nuxt/ui@4.6.1(@tiptap/extensions@3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.3))(@tiptap/pm@3.22.3))(@tiptap/y-tiptap@3.0.3(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.10.1)(jwt-decode@4.0.0)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.2(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/dom': 1.7.6
|
'@floating-ui/dom': 1.7.6
|
||||||
'@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3))
|
'@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3))
|
||||||
@@ -6054,7 +6061,7 @@ snapshots:
|
|||||||
'@tiptap/vue-3': 3.22.3(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.3(@tiptap/pm@3.22.3))(@tiptap/pm@3.22.3)(vue@3.5.32(typescript@5.9.3))
|
'@tiptap/vue-3': 3.22.3(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.3(@tiptap/pm@3.22.3))(@tiptap/pm@3.22.3)(vue@3.5.32(typescript@5.9.3))
|
||||||
'@unhead/vue': 2.1.13(vue@3.5.32(typescript@5.9.3))
|
'@unhead/vue': 2.1.13(vue@3.5.32(typescript@5.9.3))
|
||||||
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
||||||
'@vueuse/integrations': 14.2.1(change-case@5.4.4)(fuse.js@7.3.0)(vue@3.5.32(typescript@5.9.3))
|
'@vueuse/integrations': 14.2.1(change-case@5.4.4)(fuse.js@7.3.0)(jwt-decode@4.0.0)(vue@3.5.32(typescript@5.9.3))
|
||||||
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
||||||
colortranslator: 5.0.0
|
colortranslator: 5.0.0
|
||||||
consola: 3.4.2
|
consola: 3.4.2
|
||||||
@@ -7359,7 +7366,7 @@ snapshots:
|
|||||||
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
||||||
vue: 3.5.32(typescript@5.9.3)
|
vue: 3.5.32(typescript@5.9.3)
|
||||||
|
|
||||||
'@vueuse/integrations@14.2.1(change-case@5.4.4)(fuse.js@7.3.0)(vue@3.5.32(typescript@5.9.3))':
|
'@vueuse/integrations@14.2.1(change-case@5.4.4)(fuse.js@7.3.0)(jwt-decode@4.0.0)(vue@3.5.32(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
'@vueuse/core': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
||||||
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
'@vueuse/shared': 14.2.1(vue@3.5.32(typescript@5.9.3))
|
||||||
@@ -7367,6 +7374,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
change-case: 5.4.4
|
change-case: 5.4.4
|
||||||
fuse.js: 7.3.0
|
fuse.js: 7.3.0
|
||||||
|
jwt-decode: 4.0.0
|
||||||
|
|
||||||
'@vueuse/metadata@10.11.1': {}
|
'@vueuse/metadata@10.11.1': {}
|
||||||
|
|
||||||
@@ -8631,6 +8639,8 @@ snapshots:
|
|||||||
dompurify: 3.4.0
|
dompurify: 3.4.0
|
||||||
html2canvas: 1.4.1
|
html2canvas: 1.4.1
|
||||||
|
|
||||||
|
jwt-decode@4.0.0: {}
|
||||||
|
|
||||||
keyv@4.5.4:
|
keyv@4.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
json-buffer: 3.0.1
|
json-buffer: 3.0.1
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { NuxtAuthHandler } from '#auth'
|
import { NuxtAuthHandler } from '#auth'
|
||||||
import ZitadelProvider from '@auth/core/providers/zitadel'
|
import ZitadelProvider from '@auth/core/providers/zitadel'
|
||||||
|
import { jwtDecode } from 'jwt-decode'
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ export default NuxtAuthHandler({
|
|||||||
pkce: true,
|
pkce: true,
|
||||||
authorization: {
|
authorization: {
|
||||||
params: {
|
params: {
|
||||||
scope: `openid email profile offline_access urn:zitadel:iam:org:project:${config.projectId}:aud`
|
scope: `openid email profile offline_access urn:zitadel:iam:org:project:${config.zitadelProjectId}:aud urn:zitadel:iam:org:project:${config.zitadelProjectId}:roles`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -26,34 +27,6 @@ export default NuxtAuthHandler({
|
|||||||
if (account?.provider === 'zitadel') {
|
if (account?.provider === 'zitadel') {
|
||||||
token.accessToken = account.access_token
|
token.accessToken = account.access_token
|
||||||
token.idToken = account.id_token
|
token.idToken = account.id_token
|
||||||
token.roles = (user as any)?.roles
|
|
||||||
|
|
||||||
/* Extract org roles from ID token claims */
|
|
||||||
const allOrgRoles: Record<string, Record<string, Record<string, string>>> = {}
|
|
||||||
const idTokenClaims = (() => {
|
|
||||||
try {
|
|
||||||
const parts = (account.id_token || '').split('.')
|
|
||||||
if (parts.length === 3) {
|
|
||||||
const payload = Buffer.from(parts[1], 'base64url').toString('utf8')
|
|
||||||
return JSON.parse(payload) as Record<string, any>
|
|
||||||
}
|
|
||||||
} catch { /* ignore */ }
|
|
||||||
return null
|
|
||||||
})()
|
|
||||||
if (idTokenClaims) {
|
|
||||||
for (const key of Object.keys(idTokenClaims)) {
|
|
||||||
if (key.startsWith('urn:zitadel:iam:org:project:') && key.endsWith(':roles')) {
|
|
||||||
allOrgRoles[key] = idTokenClaims[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Also check userinfo response for org role claims */
|
|
||||||
for (const key of Object.keys((user as any) || {})) {
|
|
||||||
if (key.startsWith('urn:zitadel:iam:org:project:') && key.endsWith(':roles')) {
|
|
||||||
allOrgRoles[key] = (user as any)[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token.allOrgRoles = Object.keys(allOrgRoles).length > 0 ? allOrgRoles : undefined
|
|
||||||
}
|
}
|
||||||
if (user?.id) {
|
if (user?.id) {
|
||||||
token.sub = user.id
|
token.sub = user.id
|
||||||
@@ -71,9 +44,18 @@ export default NuxtAuthHandler({
|
|||||||
user.name = token.name || undefined
|
user.name = token.name || undefined
|
||||||
user.email = token.email || undefined
|
user.email = token.email || undefined
|
||||||
user.image = token.image || undefined
|
user.image = token.image || undefined
|
||||||
user.roles = token.roles as string[] | undefined
|
|
||||||
user.accessToken = token.accessToken as string | undefined
|
user.accessToken = token.accessToken as string | undefined
|
||||||
user.allOrgRoles = token.allOrgRoles as Record<string, Record<string, Record<string, string>>> | undefined
|
|
||||||
|
// Decode idToken and extract org roles claim
|
||||||
|
if (token.idToken) {
|
||||||
|
try {
|
||||||
|
const decoded = jwtDecode(token.idToken)
|
||||||
|
const roles = decoded[`urn:zitadel:iam:org:project:${config.zitadelProjectId}:roles`]
|
||||||
|
user.roles = roles
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to decode idToken:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return session
|
return session
|
||||||
},
|
},
|
||||||
|
|||||||
6
types/auth.d.ts
vendored
6
types/auth.d.ts
vendored
@@ -3,20 +3,20 @@ import type { DefaultSession } from '@auth/core/types'
|
|||||||
declare module '@auth/core/types' {
|
declare module '@auth/core/types' {
|
||||||
interface Session {
|
interface Session {
|
||||||
user: {
|
user: {
|
||||||
roles?: string[]
|
roles?: Record<string, Record<string, Record<string, string>>>
|
||||||
accessToken?: string
|
accessToken?: string
|
||||||
} & DefaultSession['user']
|
} & DefaultSession['user']
|
||||||
}
|
}
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
roles?: string[]
|
roles?: Record<string, Record<string, Record<string, string>>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '#auth' {
|
declare module '#auth' {
|
||||||
interface Session {
|
interface Session {
|
||||||
user: {
|
user: {
|
||||||
roles?: string[]
|
roles?: Record<string, Record<string, Record<string, string>>>
|
||||||
accessToken?: string
|
accessToken?: string
|
||||||
} & DefaultSession['user']
|
} & DefaultSession['user']
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user