-
-
Notifications
You must be signed in to change notification settings - Fork 168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: authState getting wiped on page reload on static websites #785
base: main
Are you sure you want to change the base?
fix: authState getting wiped on page reload on static websites #785
Conversation
set and reload data, rawToken from cookie fix lint fix typecheck
Hello @zoey-kaiser, Thanks ! |
Hi @Geekimo 👋 I would like to get a review by @phoenix-ru in before merging this PR 😊 |
@zoey-kaiser Thanks for your reply |
Any news on this fix or can we help? I use a local provider and having auth work with ssg would be highly appreciated :-) |
Hello @zoey-kaiser and @phoenix-ru, Kind regards. |
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionally approved 🥳
Hi @Vijayabhaskar96 👋 I wanted to push this PR into the next release, but sadly the authjs tests are failing. We will finalize the PRs for the release tomorrow. For the case that we are not able to include it yet, you can directly install your PR as an npm package using:
|
Hello @zoey-kaiser, |
Is there a specific reason why this wasn't included in the 0.9.4 release? |
Sorry, I don’t want to come across as impatient, but we’re currently holding back a production site until this fix is implemented. Is there already an idea of when this will be released? |
// Augment types | ||
declare module 'nuxt/schema' { | ||
interface PublicRuntimeConfig { | ||
auth: ModuleOptionsNormalized | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question as to why was this added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't remember, but I guess it was causing merge conflicts.
sessionCookie.value = { | ||
lastRefreshedAt: lastRefreshedAt.value, | ||
data: data.value | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storing user data in the cookie may lead to a GDPR violation if the getSession
endpoint returns any personal information (which is often the case). It undoubtedly spawns the "protecting personal data" requirement for anyone using the module, even if they weren't affected by the SSG issue in the first place.
I don't find it very attractive to introduce such a burden to NuxtAuth :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe introducing an option to use SessionStorage nor LocalStorage would be great instead of using cookies ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If an attacker gains access to a token stored in a Cookie, Session, or LocalStorage, the primary concern is not just identifying the user, but also the ability to access personal data and perform actions the user is authorized to do. This presents a clear security risk.
From a GDPR perspective, the critical question is whether the token can be used to track the user or if it contains personal data. Regardless of where the token is stored, if it can uniquely identify a user or track their actions, it falls under GDPR regulations.
For example, JWTs often store a user identifier in the payload. Since JWTs are not opaque and can be decoded, they may be used to directly identify a user, making them subject to GDPR. Even if the token does not explicitly store "personal data" like an email address, it qualifies as personal data if it can uniquely identify a user (e.g., through a user ID).
In this case, you must inform users via a cookie notice or a similar consent mechanism, ensuring transparency about the use of the token and obtaining their consent where required. However, if the cookies are strictly necessary for the operation of the web application (e.g., to keep users logged in or enable certain features), explicit consent may not be required under GDPR. This generally applies to authentication cookies, afaik.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thorge You are mostly right. However, auth cookies/jwts don't need to store emails or other identifying information. A website can use "shallow" JWTs storing only user id in it (this is a valid Access Token
in NuxtAuth terms). As to whether an ID is "personal data" is very subjective.
However if a website uses such a "shallow" JWT (Access Token
) and makes a call to a getSession
endpoint to check its validity + get user data, then it's a different thing. For example, internally we optimize such a call to also return user email, name, permissions, etc. on top of a regular JWT validity check. I would prefer to not store this information in a cookie.
Please note that I am also not clearly understanding the usecase of static websites fully. If there is authentication, what is the issue with the session being refetched on page reload?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phoenix-ru There’s actually no need to store the session itself; we only need to store the access token. That’s what I thought this was about, but I must admit I didn’t examine the changes in detail. Sorry for my tldr answer above :-)
As @Geekimo and @Vijayabhaskar96 suggested, I’d prefer a solution where storing the access token in a cookie (or alternatively in SessionStorage/LocalStorage) is optional. If the session data isn’t available after a client page reload, the access token could be used to retrieve the session data as needed.
src/runtime/plugin.ts
Outdated
function handleLocalAuth(config: ProviderLocal): void { | ||
const sessionCookie = useCookie<SessionCookie | null>( | ||
'auth:sessionCookie' | ||
) | ||
const cookieToken = useCookie<string | null>( | ||
config.token?.cookieName ?? 'auth.token' | ||
) | ||
|
||
if (sessionCookie?.value && !rawToken?.value && cookieToken?.value) { | ||
restoreSessionFromCookie(sessionCookie, cookieToken) | ||
} | ||
} | ||
|
||
function restoreSessionFromCookie( | ||
sessionCookie: CookieRef<SessionCookie | null>, | ||
cookieToken: CookieRef<string | null> | ||
): void { | ||
try { | ||
loading.value = true | ||
const sessionData = sessionCookie.value | ||
lastRefreshedAt.value = sessionData?.lastRefreshedAt | ||
data.value = sessionData?.data | ||
rawToken.value = cookieToken.value | ||
} | ||
catch (error) { | ||
console.error('Failed to parse session data from cookie:', error) | ||
} | ||
finally { | ||
loading.value = false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am struggling to understand as to why saving/restoring the session data in the cookie is needed in the first place? I read both the original issue #551 and the PRs around it, but still haven't found a valid reason for this approach.
I think there should be a less hacky way of solving it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I encountered an issue with page reloads after logging in while using SSG with this module, causing the authentication token to be lost. It’s possible that I misconfigured something, but in general, I need a solution that ensures the user stays logged in even when he triggers a page reload.
When using SSG and wanting users to stay logged in across page reloads, client-side storage is required. I recommend using cookies for this purpose, as they are domain-specific, secure (with attributes like HttpOnly
and Secure
), and automatically sent to the server with each request, making them ideal for authentication.
localStorage is vulnerable to XSS. sessionStorage is cleared on page reloads, making it unsuitable for long-term session persistence.
While all three options have their trade-offs, cookies are typically the most secure and practical for authentication across page reloads imho. In case of JWT I guess the best option would be having short time tokens with long time opaque refresh tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One possible solution is to just load the raw token from the cookie and not store the session data anywhere.
Call getSession
if data.value
is null.
This would fetch the session every time the page is refreshed.
Will this work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue with the above approach is that middleware protects pages based on status
- if a user
refreshes a protected page, the data.value
is lost, causing status.value
to be 'unauthenticated'.
This triggers unwanted redirects.
To prevent this, we can determine status.value
based on the existence of rawToken.value
for local
provider only. This remains GDPR compliant as only the authentication token is stored in cookies,
with the session being fetched on each page refresh.
Any other suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Vijayabhaskar96 Thank you for a good explainer! I think this is the best take so far and I would absolutely be up to fixing unnecessary redirects, but without introducing any extra cookies - only using ones we have so far:
const _rawTokenCookie = useCookie<string | null>(config.token.cookieName, { |
const _rawRefreshTokenCookie = useCookie<string | null>(config.refresh.token.cookieName, { |
@Geekimo @thorge @Vijayabhaskar96 Hi, sorry for keeping you waiting as this PR has been quite controversial for me. I just left my concerns in the review above. I don't want to give any false promises about merging the PR soon, as I do not feel confident at all in the patch. If you trust it, you can use it at your risk by using the latest commit (see the message from bot -> click on commit):
I know it's one of the long-standing issues in the upd: Unfortunately, I didn't have enough resources in December due to other internal company projects. I will keep this PR in priority when I come back in the second part of January |
Previous PR: #712
🔗 Linked issue
#551
This issue occurs because
data
(data fromgetSession()
) is not stored in the browser, which is not a problem in SSR with server because the server takes care of it, but on a statically generated website, thisdata
is lost along withrawToken
even if the token is stored in the browser cookies if you refresh the webpage.❓ Type of change
📚 Description
This PR stores the retrieved data from
getSession()
and stores it in the browser cookieauth:sessionCookie
and reloads thedata
andrawToken
back on the client side if they'reundefined
.This PR fixes the data issue, but you still need to set the
prerender:false
for middleware protected routes in routeRules so that client-side middleware is forced to run and load the states.📝 Checklist