nuxt-auth: When refresh browser on prerender env(SSG), Authentication status is lost.

Environment


  • Operating System: Darwin
  • Node Version: v18.17.1
  • Nuxt Version: 3.7.4
  • CLI Version: 3.9.0
  • Nitro Version: 2.6.3
  • Package Manager: npm@9.6.7
  • Builder: -
  • User Config: devtools, build, alias, ssr, runtimeConfig, nitro, vite, modules, auth, i18n, experimental, css, pinia, app
  • Runtime Modules: @pinia/nuxt@0.4.11, @pinia-plugin-persistedstate/nuxt@1.1.2, @nuxtjs/i18n@8.0.0-rc.5, @sidebase/nuxt-auth@0.6.0-rc.0
  • Build Modules: -

client : npm run generate > npx serve [output static directory] -l 3001 node server : npm run build(port 3000) > node index.mjs

Reproduction

  1. Call signIn on Login Page.
        const { signIn, status } = useAuth();
	setUser(<resLoginType>returnData.value.rs);
	// await authLogin();
	const wrapBody = {
		username: loginId.value,
		password: passwd.value,
		
	};
	await signIn(wrapBody, { redirect: false });

	navigateTo('/main', { replace: true });
  1. signin is authorize and then move main page. (Screenshots : Chrome Inspector Application > Cookie and check useAuth status value) 스크린샷 2023-10-18 오후 6 02 46 스크린샷 2023-10-18 오후 6 09 34

  2. After refreshing the browser, useAuth status value is lost and move the login. 스크린샷 2023-10-18 오후 6 18 48

Describe the bug

  1. Authentication passed on the login page.
  2. Refreshed the main page.
  3. Authentication status is lost and moved to the login page.

I guess this is probably a problem that occurs when pre-rendering is performed on the client-driven server side. Is there a solution to this?

Additional context

“nuxt.config.ts” part the below.

import { resolve } from 'node:path';

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
	devtools: { enabled: true },
	build: {
		transpile: ['vuetify', 'jsonwebtoken'],
	},
	alias: {
		'./runtimeConfig': './runtimeConfig.browser',
		cookie: resolve(__dirname, 'node_modules/cookie'),
	},

	ssr: true,
	runtimeConfig: {
		public: {
			baseApiUrl: process.env.BASE_API_URL,
			midAwsUrl: process.env.MIDDLEWARE_LAMBDA_URL,
			mode: process.env.ENV_MODE,
		},		
	},

	nitro: {
		preset: 'node-server', //'aws-lambda',
		storage: { '.data:auth': { driver: 'fs', base: './.data/auth' } },
		routeRules: {
			'/': { ssr: true, prerender: true },
		},		
	},
	vite: {
		envDir: process.cwd(),
		define: {
			'process.env.DEBUG': false,
			'window.global': {},
		},
		css: {
			preprocessorOptions: {
				scss: {
					additionalData: '@import "@/assets/main.scss"; ',
				},
			},
		},
	},
	modules: [
		'@pinia/nuxt',
		'@pinia-plugin-persistedstate/nuxt',
		'@nuxtjs/i18n',
		'@sidebase/nuxt-auth',
	],
	auth: {
		provider: {
			type: 'local',
			token: {
				signInResponseTokenPointer: '/token/accessToken',
				maxAgeInSeconds: 18000,
				// sameSiteAttribute: 'lax',
			},
		},
		baseURL: 'http://localhost:3000/api/auth', 

		globalAppMiddleware: {
			isEnabled: true,
		},
	},
	i18n: {
		strategy: 'no_prefix',
		locales: [
			{
				code: 'vi',
				name: 'Vietnamese',
				file: 'vi-VN.ts',
			},
			{
				code: 'en',
				name: 'English',
				file: 'en-US.ts',
			},
			{
				code: 'ko',
				name: 'Korean',
				file: 'ko-KR.ts',
			},
		],
		experimental: {
			jsTsFormatResource: true,
		},
		lazy: true,
		langDir: 'lang',
		defaultLocale: 'en',
	},
	experimental: {
		payloadExtraction: false,
	},
	css: ['vuetify/styles', '@/assets/dev.scss'],
	pinia: {
		autoImports: [
			// `defineStore`를 자동으로 임포트함.
			'defineStore', // import { defineStore } from 'pinia'
			['defineStore', 'definePiniaStore'], // import { defineStore as definePiniaStore } from 'pinia'
		],
	},
	app: {
		head: {
			title: 'D-Sales Store',
			// titleTemplate: 'input title template',
			link: [
				{ rel: 'stylesheet', href: '' },
				{ rel: 'preconnect', href: '' },
				{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
				// font - NotoSans 추가
				{
					rel: 'stylesheet',
					type: 'text/css',
					href: 'https://fonts.googleapis.com/css?family=Noto+Sans+KR:400,500,700',
				},
			],
		},
	},
});

Logs

No response

About this issue

  • Original URL
  • State: open
  • Created 8 months ago
  • Reactions: 5
  • Comments: 24 (6 by maintainers)

Most upvoted comments

I’m facing the same issue here. When running the frontend statically generate, reloading the page loses the auth status. It only happens when ssr is set to true in the nuxt config. The cookie containing the token is still there.

Here are two minimal examples:

  1. ssr true, reloading redirects to the login page Repo: https://github.com/Zhenmao/nuxt-auth-local-bug/tree/ssr-true Demo: https://nuxt-auth-local-bug-ssr-true.netlify.app/
  2. ssr false, reloading stays on the home page Repo: https://github.com/Zhenmao/nuxt-auth-local-bug/tree/ssr-false Demo: https://nuxt-auth-local-bug-ssr-false.netlify.app/

I’m also facing this issue using local provider with SSR. The auth state is fine during CSR, but try refreshing the page, or do navigation that is not using history and it’s gone.

Here’s the reproduction: https://stackblitz.com/edit/nuxt-starter-k4cqjg

  • Access login page and input any email and and password
  • The login will be successful and user will be redirected to protected page (/app)
  • Click any navigation link except Regular Link and the auth state is kept.
  • Click Regular Link, and it will bring user to another unprotected page.
  • Click back to app or login page and the auth state is gone (if clicking app, it’ll redirect to login, if clicking login, it will render login page, even though user did not logout)

For anyone who’s only using the local provider of nuxt-auth I highly recommend implementing authentication without a package. It’s simple yet effective.

This package is just not good at simple authentication flow with just the local provider. I’ve lost days trying to work around annoying bugs and over-engineered solutions for a simple Auth flow.

In the end I just build my own solution through a simple global middleware checking for the token in a Pinia store, a Pinia store saving and retrieving the token from a cookie with the useCookie composable and in the app.vue a simple setup script that retrieves whatever user data is necessary. And a login form of course.

However, if you need to implement multiple providers this package is of course amazing and my respect to the maintainers of it!

Thanks to you I regained my sanity. Having an authenticating flash before loading the content on first load is an ugly solution but at least it’s working now. Thank you. Did you ever got the time to come up with a better solution?

I created a pull request (#610) to fix that issue. They’re currently reviewing it. On the project I was having the issue, I just installed the fork I created, instead of using @sidebase/nuxt-auth. I deployed it to NPM, but I’m not sharing the package name since I probably won’t keep it updated, lol.

If either of you are having the problem I stated above, it’s because Nuxt Auth is incorrectly passes down the authentication info when pages are being cached/prerendered. In dev tools run: console.log(useNuxtApp().payload.state['$sauth:loading']), observe that it logs as false, meaning that Nuxt Auth is not sending a loading state by default. Nuxt Auth also doesn’t revalidate client side (nor does it need to).

As a cheap solution you can do this:

onMounted(() => {
	getSession()
})

Since auth.status isn’t initially “loaded”, you could add a custom loading status. (code untested)

const authLoading = ref(true);

onMounted(() => {
	getSession().then(() => loading.value = false)
})

Or as a better solution, you can probably create a composable (if you do, please share).

I was trying to slowly build a PR to fix (as I need this for a personal project). But I’m spending a lot of my personal time on work-related projects.

A permanent solution might be to:

  1. Add a config option to turn off all server-side Nuxt Auth use (but there isn’t fine grained control).
  2. Detect if an option has prerender/isr/swr in routeRules (but there are some variables to this, example: Cloudflare might cache).
  3. Add an option in the routeRules to turn off server-side Nuxt Auth functionality (but a lot of extra lines to the Nuxt config).

Thanks to you I regained my sanity. Having an authenticating flash before loading the content on first load is an ugly solution but at least it’s working now. Thank you. Did you ever got the time to come up with a better solution?

If either of you are having the problem I stated above, it’s because Nuxt Auth is incorrectly passes down the authentication info when pages are being cached/prerendered. In dev tools run: console.log(useNuxtApp().payload.state['$sauth:loading']), observe that it logs as false, meaning that Nuxt Auth is not sending a loading state by default. Nuxt Auth also doesn’t revalidate client side (nor does it need to).

As a cheap solution you can do this:

onMounted(() => {
	getSession()
})

Since auth.status isn’t initially “loaded”, you could add a custom loading status. (code untested)

const authLoading = ref(true);

onMounted(() => {
	getSession().then(() => loading.value = false)
})

Or as a better solution, you can probably create a composable (if you do, please share).

I was trying to slowly build a PR to fix (as I need this for a personal project). But I’m spending a lot of my personal time on work-related projects.

A permanent solution might be to:

  1. Add a config option to turn off all server-side Nuxt Auth use (but there isn’t fine grained control).
  2. Detect if an option has prerender/isr/swr in routeRules (but there are some variables to this, example: Cloudflare might cache).
  3. Add an option in the routeRules to turn off server-side Nuxt Auth functionality (but a lot of extra lines to the Nuxt config).

@TouchSek If you tab in and out of your site does the authentication state fix itself?

Like this:

  1. Navigates to yourwebsite.com
  2. Observe that the unauthenticated state is available to user (“Please sign in”)
  3. Clicks away into another tab
  4. Clicks back on the yourwebsite.com tab
  5. Observe that the authentication state is now showing (“Signed in as…”)

I might have the same issue, I just wanna know if it’s the same thing or not, so I can make my own issue. Here is the reproducible example I made if you wanna compare: Site: https://minimal-nuxt-app-git-using-isr-kylesmith0905.vercel.app/ Repo: https://github.com/KyleSmith0905/mre--nuxt-app/tree/using-isr