feat: implement force logout event and update navigation redirects
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 1m37s

This commit is contained in:
2026-03-05 09:52:19 -05:00
parent 992dd8423f
commit ccfc710753
8 changed files with 38 additions and 10 deletions

View File

@@ -14,6 +14,10 @@ from werkzeug.security import generate_password_hash, check_password_hash
from api.utils import sanitize_email
from config.paths import get_user_image_dir
from events.sse import send_event_to_user
from events.types.event import Event
from events.types.event_types import EventType
from events.types.payload import Payload
from api.error_codes import (
MISSING_FIELDS, EMAIL_EXISTS, MISSING_TOKEN, INVALID_TOKEN, TOKEN_TIMESTAMP_MISSING,
@@ -359,6 +363,9 @@ def reset_password():
# Invalidate ALL refresh tokens for this user
refresh_tokens_db.remove(TokenQuery.user_id == user.id)
# Notify all active sessions (other tabs/devices) to sign out immediately
send_event_to_user(user.id, Event(EventType.FORCE_LOGOUT.value, Payload({})))
resp = jsonify({'message': 'Password has been reset'})
_clear_auth_cookies(resp)
return resp, 200

View File

@@ -27,3 +27,5 @@ class EventType(Enum):
CHORE_SCHEDULE_MODIFIED = "chore_schedule_modified"
CHORE_TIME_EXTENDED = "chore_time_extended"
CHILD_CHORE_CONFIRMATION = "child_chore_confirmation"
FORCE_LOGOUT = "force_logout"

View File

@@ -28,7 +28,7 @@ function handleUnauthorizedResponse(): void {
unauthorizedRedirectHandler()
return
}
window.location.assign('/auth')
window.location.assign('/')
}
/**

View File

@@ -1,9 +1,12 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useBackendEvents } from '@/common/backendEvents'
import { currentUserId } from '@/stores/auth'
import { currentUserId, logoutUser } from '@/stores/auth'
import { eventBus } from '@/common/eventBus'
const userId = ref(currentUserId.value)
const router = useRouter()
watch(currentUserId, (id) => {
userId.value = id
@@ -11,6 +14,19 @@ watch(currentUserId, (id) => {
// Always call useBackendEvents in setup, passing the reactive userId
useBackendEvents(userId)
function handleForceLogout() {
logoutUser()
router.push('/')
}
onMounted(() => {
eventBus.on('force_logout', handleForceLogout)
})
onUnmounted(() => {
eventBus.off('force_logout', handleForceLogout)
})
</script>
<template>

View File

@@ -3,7 +3,7 @@
<!-- Sticky nav -->
<nav class="landing-nav" :class="{ 'landing-nav-scrolled': scrolled }">
<div class="nav-inner">
<img src="/images/c_logo.png" alt="Chorly" class="nav-logo" />
<img src="/images/c_logo.png" alt="Chorly" class="nav-logo" @click="scrollToTop" />
<button class="nav-signin" @click="goToLogin">Sign In</button>
</div>
</nav>
@@ -34,6 +34,10 @@ function goToLogin() {
router.push({ name: 'Login' })
}
function scrollToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
onMounted(() => {
window.addEventListener('scroll', onScroll, { passive: true })
})
@@ -82,6 +86,7 @@ onUnmounted(() => {
.nav-logo {
height: 34px;
filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.35));
cursor: pointer;
}
.nav-signin {

View File

@@ -341,7 +341,7 @@ function handleDeleteSuccess() {
}).finally(() => {
// Clear client-side auth and redirect, regardless of logout response
logoutUser()
router.push('/auth/login')
router.push('/')
})
}

View File

@@ -225,7 +225,7 @@ async function signOut() {
try {
await fetch('/api/auth/logout', { method: 'POST' })
logoutUser()
router.push('/auth')
router.push('/')
} catch {
// Optionally show error
}

View File

@@ -21,10 +21,8 @@ const router = useRouter()
const route = useRoute()
const handleBack = () => {
// route to the auth landing page instead of using browser history
router.push({ name: 'AuthLanding' }).catch(() => {
// fallback to a safe path if named route isn't available
window.location.href = '/auth'
router.push({ name: 'LandingPage' }).catch(() => {
window.location.href = '/'
})
}