Zachoo
Zachoo2mo ago

Keeping the user logged in forever.

I am developing a mobile application using Convex for authentication management. The app is built using two approaches: a Capacitor Next.js app and a React Native app (for testing both frameworks). How can I maintain the user authentication session across both platforms so that users only need to log in once? Could you provide guidance on implementing a persistent auth session in this setup?
18 Replies
Convex Bot
Convex Bot2mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
ballingt
ballingt2mo ago
That sounds tricky, do any other auth solution offer this?
Zachoo
ZachooOP2mo ago
Hmm the idea is to offer auth that feel like facebook where you login once and just stay logged in even after a period of inactivity here is current solution please help if bad im just tryna learn. I wrap each page in an auth guard
'use client'
import { ReactNode, Suspense } from 'react'
import { AuthLoading, Authenticated, Unauthenticated } from 'convex/react'
import { usePathname, useRouter } from 'next/navigation'
import { useQuery } from 'convex-helpers/react/cache'
import { api } from '@packages/backend/convex/_generated/api'
import LoadingSpinner from './_components/loading-spinner'

export function AuthGuard({ children }: { children: ReactNode }) {

return (
<Suspense fallback={<LoadingComponent centered />}>
<AuthLoading>
<LoadingComponent centered />
{/* <>{children}</> */}
</AuthLoading>
<Authenticated>
<AuthenticatedRouteGuard>{children}</AuthenticatedRouteGuard>
</Authenticated>
<Unauthenticated>
<UnauthenticatedRouteGuard>{children}</UnauthenticatedRouteGuard>
</Unauthenticated>
</Suspense>
)
}

function AuthenticatedRouteGuard({ children }: { children: ReactNode }) {
const pathname = usePathname()
const router = useRouter()
const user = useQuery(api.users.currentUser)
const userState = useQuery(api.users.currentUserState)

// Show loading while fetching user data
if (!user || !userState) return <LoadingComponent centered />

// Add auth route check
if (pathname === '/auth') {
router.replace('/home')
return <LoadingComponent centered />
}
'use client'
import { ReactNode, Suspense } from 'react'
import { AuthLoading, Authenticated, Unauthenticated } from 'convex/react'
import { usePathname, useRouter } from 'next/navigation'
import { useQuery } from 'convex-helpers/react/cache'
import { api } from '@packages/backend/convex/_generated/api'
import LoadingSpinner from './_components/loading-spinner'

export function AuthGuard({ children }: { children: ReactNode }) {

return (
<Suspense fallback={<LoadingComponent centered />}>
<AuthLoading>
<LoadingComponent centered />
{/* <>{children}</> */}
</AuthLoading>
<Authenticated>
<AuthenticatedRouteGuard>{children}</AuthenticatedRouteGuard>
</Authenticated>
<Unauthenticated>
<UnauthenticatedRouteGuard>{children}</UnauthenticatedRouteGuard>
</Unauthenticated>
</Suspense>
)
}

function AuthenticatedRouteGuard({ children }: { children: ReactNode }) {
const pathname = usePathname()
const router = useRouter()
const user = useQuery(api.users.currentUser)
const userState = useQuery(api.users.currentUserState)

// Show loading while fetching user data
if (!user || !userState) return <LoadingComponent centered />

// Add auth route check
if (pathname === '/auth') {
router.replace('/home')
return <LoadingComponent centered />
}
// First check onboarding status
if (!userState.isOnboarded && pathname !== '/onboarding') {
router.replace('/onboarding')
return <LoadingComponent centered />
}

// After onboarding is complete, handle redirects based on project status
if (userState.isOnboarded) {
if (pathname === '/onboarding') {
if (userState.currentProjectId) {
router.replace('/home')
} else {
router.replace('/invites')
}
return <LoadingComponent centered />
}

if (!userState.currentProjectId && pathname !== '/invites') {
router.replace('/invites')
return <LoadingComponent centered />
}
}

return <>{children}</>
}

function UnauthenticatedRouteGuard({ children }: { children: ReactNode }) {
const pathname = usePathname()
const router = useRouter()

// Redirect to auth page if not on an auth-related path
if (!['/auth', '/'].includes(pathname)) {
router.replace('/auth')
return <LoadingComponent centered />
}

// Render children only if on an auth-related path
return <>{children}</>
}

// Reusable loading component
function LoadingComponent({ centered }: { centered?: boolean }) {
return (
<div className={centered ? 'flex h-screen items-center justify-center' : ''}>
<LoadingSpinner />
</div>
)
}
// First check onboarding status
if (!userState.isOnboarded && pathname !== '/onboarding') {
router.replace('/onboarding')
return <LoadingComponent centered />
}

// After onboarding is complete, handle redirects based on project status
if (userState.isOnboarded) {
if (pathname === '/onboarding') {
if (userState.currentProjectId) {
router.replace('/home')
} else {
router.replace('/invites')
}
return <LoadingComponent centered />
}

if (!userState.currentProjectId && pathname !== '/invites') {
router.replace('/invites')
return <LoadingComponent centered />
}
}

return <>{children}</>
}

function UnauthenticatedRouteGuard({ children }: { children: ReactNode }) {
const pathname = usePathname()
const router = useRouter()

// Redirect to auth page if not on an auth-related path
if (!['/auth', '/'].includes(pathname)) {
router.replace('/auth')
return <LoadingComponent centered />
}

// Render children only if on an auth-related path
return <>{children}</>
}

// Reusable loading component
function LoadingComponent({ centered }: { centered?: boolean }) {
return (
<div className={centered ? 'flex h-screen items-center justify-center' : ''}>
<LoadingSpinner />
</div>
)
}
ballingt
ballingt2mo ago
Oh maybe I'm misunderstanding, do you want to maintain auth across these two apps?
Zachoo
ZachooOP2mo ago
Testing with cached query for auth but find if i move between pages quickly without it i fall back into auth loading state No haha sorry just writing it in both to learn different applications just building auth flows to get a feel for which i like more capacitor or native
ballingt
ballingt2mo ago
ah got it, so the current bad thing is just that on navigation within one of these apps to a different page, auth is lost?
Zachoo
ZachooOP2mo ago
this works fine however if you leave app in background for say 10 hours then reopen the application shows the home route then a white screen and then back to home also if you open the application then clear from background straight away and repeate this the app will log the user out
ballingt
ballingt2mo ago
Ah that sounds like a bug we are currently working on well the 10 hours part is re opening the applicatio then clearing it, would like ot hear more about this case
Zachoo
ZachooOP2mo ago
I have a video screen recording of the bug but has my email so will private share
ballingt
ballingt2mo ago
you can email support@convex.dev or tom@convex.dev and I'll write it up in an issue at https://github.com/get-convex/convex-auth
GitHub
GitHub - get-convex/convex-auth: Library for built-in auth
Library for built-in auth. Contribute to get-convex/convex-auth development by creating an account on GitHub.
stefano
stefano2mo ago
@Zachoo did you manage to make Convex Auth (and Convex in general) to work in a NextJS Capacitor app?
ballingt
ballingt2mo ago
We looked together at this, it looked good! There's an edge case that comes up when you repeatedly refresh the page for ten seconds straight (among other times) that we're working on
Zachoo
ZachooOP2mo ago
I did indeed im happy to share my current implementation or can we set the token to not expire else i will setup a background task to refresh the token.
stefano
stefano2mo ago
Yes please! I’d be super interested
ballingt
ballingt2mo ago
Re auth sessions lasting longer, this is the code that controls this That's this here https://github.com/get-convex/convex-auth/blob/a4328945a08761f9da397b474bd2025426f00e91/src/server/implementation/sessions.ts#L19 It's not currently configurable but we can drop an environment variable in there — open an issue on https://github.com/get-convex/convex-auth about this
ballingt
ballingt2mo ago
server - Convex Auth
Authentication library for your Convex backend
Zachoo
ZachooOP2mo ago
Do we know what the cause of the overnight logout issue was e.g. waiting 10 hours then being logged out ?
ballingt
ballingt2mo ago
Yeah, although figuring out the right fix is taking some time. You can see some draft PRs for at https://github.com/get-convex/convex-js/pulls

Did you find this page helpful?