rough session timeout

This commit is contained in:
2026-03-19 03:00:09 +00:00
parent 5f15d92903
commit f3d6df2f82
5 changed files with 82 additions and 5 deletions

View File

@@ -23,6 +23,22 @@ builder.Services.AddIdentityApiEndpoints<IdentityUser>( options => {
}) })
.AddEntityFrameworkStores<ApplicationDbContext>(); .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "Seasoned.Session";
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.Events.OnRedirectToLogin = context =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
});
builder.Services.AddAuthorization(); builder.Services.AddAuthorization();
builder.Services.AddControllers() builder.Services.AddControllers()

View File

@@ -43,6 +43,7 @@
<v-main> <v-main>
<NuxtPage /> <NuxtPage />
<SessionTimeout />
</v-main> </v-main>
</v-app> </v-app>
</template> </template>
@@ -50,6 +51,7 @@
<script setup> <script setup>
import { onMounted } from 'vue' import { onMounted } from 'vue'
import '@/assets/css/app-theme.css' import '@/assets/css/app-theme.css'
import SessionTimeout from './components/SessionTimeout.vue'
const authCookie = useCookie('.AspNetCore.Identity.Application') const authCookie = useCookie('.AspNetCore.Identity.Application')
const isLoggedIn = useState('isLoggedIn', () => false) const isLoggedIn = useState('isLoggedIn', () => false)

View File

@@ -0,0 +1,39 @@
<template>
<v-dialog v-model="showTimeout" persistent max-width="450">
<v-card class="recipe-card pa-6 text-center" theme="light">
<div class="mb-4">
<v-icon color="#8c4a32" size="64">mdi-clock-outline</v-icon>
</div>
<h2 class="auth-title mb-2">Session Expired</h2>
<v-card-text class="text-body-1" style="font-family: 'Libre Baskerville', serif;">
Your kitchen session has timed out after 30 minutes of inactivity.
Please sign back in to continue managing your recipes.
</v-card-text>
<v-divider class="my-4 separator"></v-divider>
<v-card-actions>
<v-btn
block
class="auth-btn"
size="large"
@click="redirectToLogin"
>
Return to Sign In
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup>
import '@/assets/css/app-theme.css'
const showTimeout = useState('showSessionTimeout')
const redirectToLogin = () => {
showTimeout.value = false
navigateTo('/login')
}
</script>

View File

@@ -127,6 +127,8 @@ const errorMessage = ref('')
const authLoading = ref(false) const authLoading = ref(false)
const config = useRuntimeConfig() const config = useRuntimeConfig()
const isLoggedIn = useState('isLoggedIn', () => false)
const toggleMode = () => { const toggleMode = () => {
isLogin.value = !isLogin.value isLogin.value = !isLogin.value
errorMessage.value = '' errorMessage.value = ''
@@ -144,7 +146,7 @@ const handleAuth = async () => {
const endpoint = isLogin.value ? 'api/auth/login' : 'api/auth/register' const endpoint = isLogin.value ? 'api/auth/login' : 'api/auth/register'
const url = isLogin.value const url = isLogin.value
? `${config.public.apiBase}${endpoint}?useCookies=true` ? `${config.public.apiBase}${endpoint}?useCookies=true&useSessionCookies=false`
: `${config.public.apiBase}${endpoint}` : `${config.public.apiBase}${endpoint}`
try { try {
@@ -153,28 +155,31 @@ const handleAuth = async () => {
body: { body: {
email: email.value, email: email.value,
password: password.value password: password.value
} },
credentials: 'include'
}) })
if (isLogin.value) { if (isLogin.value) {
const isLoggedIn = useState('isLoggedIn')
isLoggedIn.value = true isLoggedIn.value = true
navigateTo('/') navigateTo('/')
} else { } else {
isLogin.value = true isLogin.value = true
authLoading.value = false authLoading.value = false
errorMessage.value = "Account created! Please sign in." errorMessage.value = "Account created! Please sign in."
password.value = ''
confirmPassword.value = ''
} }
} catch (err) { } catch (err) {
authLoading.value = false authLoading.value = false
if (err.status === 401) { if (err.status === 401) {
errorMessage.value = "Invalid email or password. Please try again." errorMessage.value = "Invalid email or password. Please try again."
} else if (err.status === 400) {
errorMessage.value = "Registration failed. Check your password length."
} else if (err.status === 404) { } else if (err.status === 404) {
errorMessage.value = "Account not found. Would you like to register?" errorMessage.value = "Account not found. Would you like to register?"
} else { } else {
errorMessage.value = "Something went wrong. Please check your connection." errorMessage.value = "Something went wrong."
} }
console.error('Auth error:', err) console.error('Auth error:', err)
} }
} }

View File

@@ -0,0 +1,15 @@
export default defineNuxtPlugin((nuxtApp) => {
const showTimeout = useState('showSessionTimeout', () => false);
const isLoggedIn = useState('isLoggedIn');
nuxtApp.hooks.hook('app:suspense:resolve', () => {
globalThis.$fetch = $fetch.create({
onResponseError({ response }) {
if (response.status === 401 && isLoggedIn.value) {
isLoggedIn.value = false;
showTimeout.value = true;
}
}
});
});
});