Files
gremiumhub/legalconsenthub/composables/auth/useAuthState.ts

131 lines
3.3 KiB
TypeScript

import type { ClientOptions, InferSessionFromClient, InferUserFromClient } from 'better-auth/client'
import type { RuntimeAuthConfig } from '~/types/auth'
import { defu } from 'defu'
import { useAuthClient } from '~/composables/auth/useAuthClient'
// Global state for auth
const session = ref<InferSessionFromClient<ClientOptions> | null>(null)
const user = ref<InferUserFromClient<ClientOptions> | null>(null)
const sessionFetching = import.meta.server ? ref(false) : ref(false)
const jwt = ref<string | null>(null)
const organizations = ref<
{
id: string
name: string
createdAt: Date
slug: string
metadata?: Record<string, unknown>
logo?: string | null
}[]
>([])
const selectedOrganization = ref<{
id: string
name: string
createdAt: Date
slug: string
metadata?: Record<string, unknown>
logo?: string | null
} | null>(null)
const activeMember = ref<{ role: string } | null>(null)
export function useAuthState() {
const { client } = useAuthClient()
const route = useRoute()
const options = defu(useRuntimeConfig().public.auth as Partial<RuntimeAuthConfig>, {
redirectUserTo: '/',
redirectGuestTo: '/login'
})
const headers = import.meta.server ? useRequestHeaders() : undefined
async function fetchSession(targetPath?: string) {
if (sessionFetching.value) {
console.log('already fetching session')
return
}
sessionFetching.value = true
const { data } = await client.getSession({
fetchOptions: {
headers
}
})
session.value = data?.session || null
user.value = data?.user || null
sessionFetching.value = false
// Only fetch JWT and organizations if we have a session and not on public routes
if (session.value && !isPublicPath(targetPath)) {
await fetchJwtAndOrganizations()
}
return data
}
async function fetchJwtAndOrganizations() {
// Fetch JWT
const tokenResult = await client.token()
jwt.value = tokenResult.data?.token ?? null
// Fetch organization
const orgResult = await client.organization.list({
fetchOptions: {
headers
}
})
organizations.value = orgResult.data ?? []
if (!selectedOrganization.value && organizations.value.length > 0) {
selectedOrganization.value = organizations.value[0]
}
// Fetch active member
const activeMemberResult = await client.organization.getActiveMember({
fetchOptions: {
headers
}
})
activeMember.value = activeMemberResult.data || null
}
function isPublicPath(path?: string) {
const finalPath = path ?? route.path
const publicRoutes = ['/login', '/signup', '/accept-invitation']
return publicRoutes.some((path) => finalPath.startsWith(path))
}
// Watch organization changes
watch(
() => selectedOrganization.value,
async (newValue) => {
if (newValue) {
await client.organization.setActive({
organizationId: newValue?.id
})
}
}
)
// Client-side session listening
if (import.meta.client) {
client.$store.listen('$sessionSignal', async (signal) => {
if (!signal) return
await fetchSession()
})
}
return {
session,
user,
sessionFetching,
jwt,
organizations,
selectedOrganization,
activeMember,
options,
fetchSession,
isPublicPath,
loggedIn: computed(() => !!session.value)
}
}