N
Nuxt2mo ago
Uzyxen

middleware

I created a middleware in which I send an api request and validate the JWT token. In the vue app I have a token download from the serwer. App.vue
<script setup>
const { useAccessToken } = useAuth();
const token = useAccessToken();

const { data } = await useFetch('/api/auth/refresh', {
method: 'post'
});
if(data.value.access_token) {
token.value = data.value.access_token;
}
</script>
<script setup>
const { useAccessToken } = useAuth();
const token = useAccessToken();

const { data } = await useFetch('/api/auth/refresh', {
method: 'post'
});
if(data.value.access_token) {
token.value = data.value.access_token;
}
</script>
in composables I have useAuth.js with:
export default () => {
const useAccessToken = () => useState('access_token');

return {
useAccessToken
}
}
export default () => {
const useAccessToken = () => useState('access_token');

return {
useAccessToken
}
}
And id middleware authUser.ts:
const { useAccessToken } = useAuth();

export default defineNuxtRouteMiddleware(async (to, from) => {
const token: any = useAccessToken();

console.log(token);

if(!token.value) {
//return navigateTo('/logowanie');
} else {
const response = await $fetch('/api/auth/getAuthUser', {
headers: {
authorization: `Bearer ${token.value}`
}
});

if(!response) {
return navigateTo('/logowanie');
}
}
});
const { useAccessToken } = useAuth();

export default defineNuxtRouteMiddleware(async (to, from) => {
const token: any = useAccessToken();

console.log(token);

if(!token.value) {
//return navigateTo('/logowanie');
} else {
const response = await $fetch('/api/auth/getAuthUser', {
headers: {
authorization: `Bearer ${token.value}`
}
});

if(!response) {
return navigateTo('/logowanie');
}
}
});
My problem is that during SSR the "token.value" is undefined so even if user is logged he will be navigated to /login page.
5 Replies
kapa.ai
kapa.ai2mo ago
Hi @Uzyxen. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided in your question and the available knowledge sources, I can offer some insights and suggestions to address your issue with middleware and token handling during server-side rendering (SSR) in Nuxt 3. The problem you're experiencing is likely due to the fact that the token is not available during the initial server-side render. Here are a few approaches you can consider: 1. Use useCookie instead of useState: Instead of using useState to store the access token, you can use useCookie. This will ensure that the token is available on both client and server sides. Here's how you can modify your useAuth composable:
export default () => {
const useAccessToken = () => useCookie('access_token');

return {
useAccessToken
}
}
export default () => {
const useAccessToken = () => useCookie('access_token');

return {
useAccessToken
}
}
2. Skip middleware on initial server-side render: You can modify your middleware to skip execution during the initial server-side render. This can be done by checking if the app is hydrating:
export default defineNuxtRouteMiddleware(async (to, from) => {
const nuxtApp = useNuxtApp();
if (import.meta.server && nuxtApp.isHydrating) {
return;
}

// Rest of your middleware code
// ...
});
export default defineNuxtRouteMiddleware(async (to, from) => {
const nuxtApp = useNuxtApp();
if (import.meta.server && nuxtApp.isHydrating) {
return;
}

// Rest of your middleware code
// ...
});
This approach is mentioned in the Nuxt documentation on middleware (https://nuxt.com/docs/guide/directory-structure/middleware#when-middleware-runs). 3. Handle token refresh in a plugin: Instead of handling token refresh in App.vue, you can create a plugin to handle authentication state. This ensures that the token is available before any middleware runs. Here's an example:
// plugins/auth.ts
export default defineNuxtPlugin(async (nuxtApp) => {
const { useAccessToken } = useAuth();
const token = useAccessToken();
if (!token.value) {
const { data } = await useFetch('/api/auth/refresh', {
method: 'post'
});
if (data.value?.access_token) {
token.value = data.value.access_token;
}
}
});
// plugins/auth.ts
export default defineNuxtPlugin(async (nuxtApp) => {
const { useAccessToken } = useAuth();
const token = useAccessToken();
if (!token.value) {
const { data } = await useFetch('/api/auth/refresh', {
method: 'post'
});
if (data.value?.access_token) {
token.value = data.value.access_token;
}
}
});
4. Use useRequestHeaders in server-side API calls: When making API calls on the server side, you can use useRequestHeaders to pass the cookie headers to your API. This ensures that the server-side request includes the necessary authentication information:
const headers = useRequestHeaders(['cookie'])
const response = await $fetch('/api/auth/getAuthUser', {
headers: {
...headers,
authorization: `Bearer ${token.value}`
}
});
const headers = useRequestHeaders(['cookie'])
const response = await $fetch('/api/auth/getAuthUser', {
headers: {
...headers,
authorization: `Bearer ${token.value}`
}
});
This approach is described in the Nuxt documentation on passing headers and cookies (https://nuxt.com/docs/getting-started/data-fetching#passing-headers-and-cookies). By implementing one or a combination of these approaches, you should be able to handle the token correctly during both client-side and server-side rendering, preventing the unnecessary navigation to the login page for authenticated users. Remember to test your implementation thoroughly to ensure it works as expected in both SSR and client-side navigation scenarios. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@361925793397473281> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Uzyxen
UzyxenOP2mo ago
@kapa.ai I used method 3. And it works. I have question. Is it a good idea to fetch initial JWT token in plugin? How many times api will be called?
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'm glad to hear that method 3 worked for you. Let's address your questions about fetching the initial JWT token in a plugin. Is it a good idea to fetch the initial JWT token in a plugin? Yes, fetching the initial JWT token in a plugin can be a good approach. Here's why: 1. Plugins in Nuxt run before the rest of your application, ensuring that authentication is set up early in the application lifecycle Nuxt Plugins (https://nuxt.com/docs/guide/directory-structure/plugins). 2. It centralizes your authentication logic, making it easier to manage and maintain. 3. It ensures that the token is available before any middleware or components that might need it are executed. How many times will the API be called? The number of API calls depends on how you've implemented the plugin and how Nuxt handles it. Here are some key points to consider: 1. By default, Nuxt plugins run once on the server-side during SSR, and once on the client-side during hydration Nuxt Plugins (https://nuxt.com/docs/guide/directory-structure/plugins). 2. If you're using useFetch in your plugin (as shown in the example), it has built-in deduplication. This means that if the same request is made multiple times during the same render cycle, only one API call will be made Nuxt Data Fetching (https://nuxt.com/docs/getting-started/data-fetching). 3. To further optimize and control when the API is called, you can add conditions to your plugin. For example:
export default defineNuxtPlugin(async (nuxtApp) => {
const { useAccessToken } = useAuth();
const token = useAccessToken();
if (!token.value) {
const { data } = await useFetch('/api/auth/refresh', {
method: 'post',
key: 'auth-refresh', // Add a unique key
});
if (data.value?.access_token) {
token.value = data.value.access_token;
}
}
});

export default defineNuxtPlugin(async (nuxtApp) => {
const { useAccessToken } = useAuth();
const token = useAccessToken();
if (!token.value) {
const { data } = await useFetch('/api/auth/refresh', {
method: 'post',
key: 'auth-refresh', // Add a unique key
});
if (data.value?.access_token) {
token.value = data.value.access_token;
}
}
});

By adding a key to useFetch, you ensure that this specific request is only made once per render cycle useFetch (https://nuxt.com/docs/api/composables/use-fetch). 4. If you want the plugin to run only on the client-side, you can use the client directory for your plugin, which will prevent it from running during SSR Nuxt Plugins (https://nuxt.com/docs/guide/directory-structure/plugins#plugin-registration). In summary, fetching the initial JWT token in a plugin is generally a good practice. With proper implementation, the API should typically be called once per render cycle (server-side during SSR and client-side during hydration) unless the token already exists. Always monitor your application's network requests to ensure it's behaving as expected in different scenarios. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@361925793397473281> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Want results from more Discord servers?
Add your server