Add error page for when a session has been taken

Describe to the user what it means when a session has been detected as
taken and provide a means to abandoned the session and log in again.
This commit is contained in:
Hornwitser 2025-07-08 15:51:50 +02:00
parent 011687b391
commit ebeedff5d0
2 changed files with 49 additions and 9 deletions

View file

@ -3,26 +3,65 @@
SPDX-License-Identifier: AGPL-3.0-or-later SPDX-License-Identifier: AGPL-3.0-or-later
--> -->
<template> <template>
<Header /> <template v-if='data?.data?.code === "SESSION_TAKEN"'>
<h1>{{ error.statusCode }} {{ error.statusMessage }}</h1> <h1>Session taken</h1>
<p v-if="error.message !== error.statusMessage"> <p>
{{ error.message }} Your session with the server has been taken over by another browser, device, or HTTP agent. This could happen due to one of the following reasons:
</p> </p>
<pre v-if="error.stack"><code>{{ error.stack }}</code></pre> <ul>
<li>
The session cookie on this device got restored to an earlier state for example as a result of a restore or a crash.
</li>
<li>
The server issued a new session to the client but the client didn't get the repsonse.
</li>
<li>
The session cookie was copied and used on another device, browser or HTTP agent.
</li>
</ul>
<p>
It's possible however unlikely that someone else have hijacked your session.
</p>
<p>
<button
type="button"
@click="abandonSession"
>
Abandon Session
</button>
</p>
</template>
<template v-else>
<Header />
<h1>{{ error.statusCode }} {{ error.statusMessage }}</h1>
<p v-if="error.message !== error.statusMessage">
{{ error.message }}
</p>
<pre v-if="error.stack"><code>{{ error.stack }}</code></pre>
</template>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ error: { const props = defineProps<{ error: {
statusCode: number, statusCode: number,
fatal: boolean, fatal: boolean,
unhandled: boolean, unhandled: boolean,
statusMessage?: string, statusMessage?: string,
data?: unknown, data?: string,
cause?: unknown, cause?: unknown,
// Undocumented fields // Undocumented fields
url?: string, url?: string,
message?: string, message?: string,
stack?: string, stack?: string,
} }>() }}>();
const data = computed<{
data?: {
code?: string,
},
}>(() => props.error.data ? JSON.parse(props.error.data) : undefined);
async function abandonSession() {
await $fetch("/api/auth/session", { method: "DELETE", }).catch(err => alert(err.message));
await navigateTo("/");
}
</script> </script>

View file

@ -106,6 +106,7 @@ export async function getServerSession(event: H3Event, ignoreExpired: boolean) {
statusCode: 403, statusCode: 403,
statusMessage: "Forbidden", statusMessage: "Forbidden",
message: "Session has been taken by another agent.", message: "Session has been taken by another agent.",
data: { code: "SESSION_TAKEN" },
}); });
} }
const now = Date.now(); const now = Date.now();