Add shared device inactivity timeout to AuthGate
This commit is contained in:
parent
e4e6958ff9
commit
e0ff800c6b
1 changed files with 33 additions and 1 deletions
|
|
@ -1,6 +1,13 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import type { User } from '../types'
|
import type { User } from '../types'
|
||||||
|
|
||||||
|
const SHARED_TIMEOUT_MS = 10 * 60 * 1000
|
||||||
|
|
||||||
|
function isSharedDevice() {
|
||||||
|
return document.cookie.split(';').some(c => c.trim() === 'hnf_shared_device=1')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: (user: User) => React.ReactNode
|
children: (user: User) => React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
@ -23,6 +30,7 @@ export function AuthGate({ children }: Props) {
|
||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/api/auth/verify?app=hk-planner', { credentials: 'include' })
|
fetch('/api/auth/verify?app=hk-planner', { credentials: 'include' })
|
||||||
|
|
@ -33,6 +41,30 @@ export function AuthGate({ children }: Props) {
|
||||||
.catch(() => setState('login'))
|
.catch(() => setState('login'))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state !== 'authed' || !isSharedDevice()) return
|
||||||
|
|
||||||
|
async function forceLogout() {
|
||||||
|
await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }).catch(() => {})
|
||||||
|
setUser(null)
|
||||||
|
setState('login')
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
if (timerRef.current) clearTimeout(timerRef.current)
|
||||||
|
timerRef.current = setTimeout(forceLogout, SHARED_TIMEOUT_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
const events = ['mousemove', 'keydown', 'click', 'touchstart'] as const
|
||||||
|
events.forEach(e => window.addEventListener(e, reset, { passive: true }))
|
||||||
|
reset()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timerRef.current) clearTimeout(timerRef.current)
|
||||||
|
events.forEach(e => window.removeEventListener(e, reset))
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
async function login(e: React.FormEvent) {
|
async function login(e: React.FormEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue