Skip to content

Commit 187c828

Browse files
committed
fix: hard reload
1 parent 60d87dc commit 187c828

File tree

8 files changed

+43
-17
lines changed

8 files changed

+43
-17
lines changed

docs/user-guide/authentication.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ The module automatically handles:
389389
- Token expiration validation
390390
- Automatic token cleanup
391391
- Token refresh on activity
392+
- **Hard reload persistence**: Users remain logged in after hard refresh (Ctrl+F5)
392393

393394
## Security Features
394395

playground/pages/index.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<script setup lang="ts">
22
import { useAuthentication } from '#imports'
33
4-
const { user, initializeUser } = useAuthentication()
4+
const { user } = useAuthentication()
55
6-
// Initialize user from localStorage on page load
7-
initializeUser()
6+
// User is automatically initialized by the client plugin
7+
// No need to manually call initializeUser() anymore
88
</script>
99

1010
<template>

src/runtime/components/NUsersLoginForm.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const handleSubmit = async () => {
5656
}
5757
})
5858
59-
emit('success', response.user, formData.value.rememberMe)
59+
emit('success', response.user, formData.value.rememberMe ?? false)
6060
6161
// Redirect if specified
6262
if (props.redirectTo) {

src/runtime/composables/useAuthentication.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { useState, useRuntimeConfig } from '#app'
22
import { computed, readonly } from 'vue'
33
import type { User, UserWithoutPassword } from 'nuxt-users/utils'
44

5+
// Global flag to track if initialization has been attempted
6+
let initializationPromise: Promise<void> | null = null
7+
58
export const useAuthentication = () => {
69
const user = useState<UserWithoutPassword | null>('user', () => null)
710
const { public: { nuxtUsers } } = useRuntimeConfig()
@@ -13,17 +16,18 @@ export const useAuthentication = () => {
1316
// Remove password from user data before storing
1417
const { password: _, ...userWithoutPassword } = userData
1518
user.value = userWithoutPassword
16-
19+
1720
// Store user data based on rememberMe preference
1821
if (import.meta.client) {
1922
// Clear any existing user data from both storages
2023
localStorage.removeItem('user')
2124
sessionStorage.removeItem('user')
22-
25+
2326
if (rememberMe) {
2427
// Store in localStorage for persistent login across browser sessions
2528
localStorage.setItem('user', JSON.stringify(userWithoutPassword))
26-
} else {
29+
}
30+
else {
2731
// Store in sessionStorage for session-only login
2832
sessionStorage.setItem('user', JSON.stringify(userWithoutPassword))
2933
}
@@ -55,18 +59,20 @@ export const useAuthentication = () => {
5559
try {
5660
const response = await $fetch<{ user: UserWithoutPassword }>(`${apiBasePath}/me`, { method: 'GET' })
5761
user.value = response.user
58-
62+
5963
// Update the appropriate storage with fresh user data
6064
if (import.meta.client) {
6165
// Determine which storage was being used and update it
6266
const wasInLocalStorage = localStorage.getItem('user') !== null
6367
const wasInSessionStorage = sessionStorage.getItem('user') !== null
64-
68+
6569
if (wasInLocalStorage) {
6670
localStorage.setItem('user', JSON.stringify(response.user))
67-
} else if (wasInSessionStorage) {
71+
}
72+
else if (wasInSessionStorage) {
6873
sessionStorage.setItem('user', JSON.stringify(response.user))
69-
} else {
74+
}
75+
else {
7076
// Default to sessionStorage for new sessions without rememberMe
7177
sessionStorage.setItem('user', JSON.stringify(response.user))
7278
}
@@ -95,7 +101,7 @@ export const useAuthentication = () => {
95101
const storedUserLocal = localStorage.getItem('user')
96102
const storedUserSession = sessionStorage.getItem('user')
97103
const storedUser = storedUserLocal || storedUserSession
98-
104+
99105
if (storedUser) {
100106
try {
101107
// First set the user from storage for immediate UI response
@@ -113,6 +119,14 @@ export const useAuthentication = () => {
113119
}
114120
}
115121

122+
// Auto-initialize user on first access (client-side only)
123+
if (import.meta.client && !initializationPromise) {
124+
initializationPromise = initializeUser().catch(() => {
125+
// Silently handle initialization errors
126+
// Don't prevent the composable from working
127+
})
128+
}
129+
116130
return {
117131
user: readonly(user),
118132
isAuthenticated,

src/runtime/server/api/nuxt-users/session/index.post.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export default defineEventHandler(async (event) => {
5959
// For "remember me" sessions, use a longer expiration (30 days default)
6060
const longTermDays = options.auth.rememberMeExpiration || 30 // days
6161
expiresAt.setDate(expiresAt.getDate() + longTermDays)
62-
} else {
62+
}
63+
else {
6364
// For regular sessions, use the configured token expiration
6465
expiresAt.setMinutes(expiresAt.getMinutes() + options.auth.tokenExpiration)
6566
}
@@ -72,22 +73,29 @@ export default defineEventHandler(async (event) => {
7273
`
7374

7475
// Set the cookie with appropriate expiration based on rememberMe
75-
const cookieOptions = {
76+
const cookieOptions: {
77+
httpOnly: boolean
78+
secure: boolean
79+
sameSite: 'lax'
80+
path: string
81+
maxAge?: number
82+
} = {
7683
httpOnly: true,
7784
secure: process.env.NODE_ENV === 'production', // Use secure cookies in production
7885
sameSite: 'lax' as const, // Adjust as needed
7986
path: '/',
8087
}
81-
88+
8289
if (rememberMe) {
8390
// For "remember me", set a long-term cookie
8491
const longTermDays = options.auth.rememberMeExpiration || 30
8592
cookieOptions.maxAge = 60 * 60 * 24 * longTermDays // Convert days to seconds
86-
} else {
93+
}
94+
else {
8795
// For regular login, don't set maxAge to create a session cookie
8896
// Session cookies expire when the browser is closed
8997
}
90-
98+
9199
setCookie(event, 'auth_token', token, cookieOptions)
92100

93101
// Return user without password for security

test/test-setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const getTestOptions = (dbType: DatabaseType, dbConfig: DatabaseConfig) =
6363
auth: {
6464
whitelist: [],
6565
tokenExpiration: 1440,
66+
rememberMeExpiration: 30,
6667
permissions: { admin: ['*'] }
6768
},
6869
passwordValidation: {

test/unit/api.users-list.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const testOptions: ModuleOptions = {
5858
auth: {
5959
whitelist: [],
6060
tokenExpiration: 1440,
61+
rememberMeExpiration: 30,
6162
permissions: {}
6263
},
6364
passwordValidation: {

test/unit/usePublicPaths.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe('usePublicPaths', () => {
4242
auth: {
4343
whitelist: ['/about', '/contact'],
4444
tokenExpiration: 1440,
45+
rememberMeExpiration: 30,
4546
permissions: {
4647
admin: ['*'],
4748
user: ['/profile', '/dashboard'],

0 commit comments

Comments
 (0)