Skip to content

Commit 3075a17

Browse files
committed
feat: implement server-side demo organization check for fresh sign-up flow
1 parent b60b9bc commit 3075a17

2 files changed

Lines changed: 53 additions & 30 deletions

File tree

app/pages/auth/fresh-signup.vue

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,31 @@ definePageMeta({ layout: 'auth' })
44
useSeoMeta({ robots: 'noindex, nofollow' })
55
66
const localePath = useLocalePath()
7-
const config = useRuntimeConfig()
87
98
onMounted(async () => {
10-
const { data: session } = await authClient.useSession(useFetch)
11-
12-
if (!session.value) {
13-
// Not logged in — just go to sign-up
14-
await navigateTo(localePath('/auth/sign-up'), { replace: true })
15-
return
16-
}
17-
18-
// Check if the user is in the demo org
19-
const demoSlug = config.public.demoOrgSlug
20-
if (!demoSlug) {
21-
// No demo slug configured — redirect to dashboard
22-
await navigateTo(localePath('/dashboard'), { replace: true })
23-
return
24-
}
25-
26-
// Await the active org — useActiveOrganization() is reactive and
27-
// may not have resolved when read synchronously inside onMounted
28-
const { data: org } = await authClient.organization.getFullOrganization()
29-
const isDemo = org?.slug === demoSlug
30-
31-
if (isDemo) {
32-
// Demo user — sign out and redirect to sign-up for a real account
33-
await authClient.signOut()
34-
clearNuxtData()
35-
await navigateTo(localePath('/auth/sign-up'), { replace: true })
9+
try {
10+
// Server-side demo check — avoids client-side auth library quirks
11+
const { hasSession, isDemo } = await $fetch('/api/auth/demo-check')
12+
13+
if (!hasSession) {
14+
window.location.href = localePath('/auth/sign-up')
15+
return
16+
}
17+
18+
if (isDemo) {
19+
// Sign out the demo session via the auth API directly
20+
await $fetch('/api/auth/sign-out', { method: 'POST', body: {} })
21+
// Hard navigate to clear all client-side state (Better Auth atoms, Nuxt caches)
22+
window.location.href = localePath('/auth/sign-up')
23+
}
24+
else {
25+
// Real user — go to dashboard
26+
window.location.href = localePath('/dashboard')
27+
}
3628
}
37-
else {
38-
// Real user — don't sign out, redirect to dashboard
39-
await navigateTo(localePath('/dashboard'), { replace: true })
29+
catch {
30+
// On any error, default to sign-up
31+
window.location.href = localePath('/auth/sign-up')
4032
}
4133
})
4234
</script>

server/api/auth/demo-check.get.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { eq } from 'drizzle-orm'
2+
import * as schema from '../../database/schema'
3+
4+
/**
5+
* Server-side check: is the current session a demo organization?
6+
*
7+
* Used by the fresh-signup page to reliably detect demo sessions
8+
* before signing out and redirecting to sign-up.
9+
*/
10+
export default defineEventHandler(async (event) => {
11+
const session = await auth.api.getSession({ headers: event.headers })
12+
13+
if (!session) {
14+
return { hasSession: false, isDemo: false }
15+
}
16+
17+
const demoSlug = useRuntimeConfig().public.demoOrgSlug as string
18+
const activeOrgId = (session.session as { activeOrganizationId?: string }).activeOrganizationId
19+
20+
if (!demoSlug || !activeOrgId) {
21+
return { hasSession: true, isDemo: false }
22+
}
23+
24+
const [org] = await db
25+
.select({ slug: schema.organization.slug })
26+
.from(schema.organization)
27+
.where(eq(schema.organization.id, activeOrgId))
28+
.limit(1)
29+
30+
return { hasSession: true, isDemo: org?.slug === demoSlug }
31+
})

0 commit comments

Comments
 (0)