Nuxt Plugin using `useCookie` composable error.

Hi, I have a custom Auth fetch nuxt Plugin for fetching data behind a authentication. But I keep getting errors about using Nuxt composables. I am using the useCookie composable. How should I run this function?: Error:
[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables.
[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables.
My custom fetch plugin:
import { useAuthStore } from '~/store/auth';

export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const store= useAuthStore()

const $authFetch = $fetch.create({
baseURL: config.public.apiBaseURL,
onRequest: async function ({request, options, error}) {

let userSession = useCookie('authToken').value;

if(!userSession?.token || (!userSession?.expiration || new Date(userSession.expiration) <= new Date())) {
console.log("RETRIEVE A NEW TOKEN PLEASE:")
const refreshed = await store.refreshTokenAction();
if(!refreshed) throw new Error('Token refresh failed. Aborting request.');
}
// Retrieve again, I could use refreshCookie()
userSession = useCookie('authToken').value;

if (userSession.token) {
options.headers = options.headers || {}
options.headers.Authorization = `Bearer ${userSession.token}`
}
},
onRequestError({ request, options, error }) {
},
onResponseError({ request, response, options }) {
}
})
return { provide: { authFetch: $authFetch }
}
})
import { useAuthStore } from '~/store/auth';

export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const store= useAuthStore()

const $authFetch = $fetch.create({
baseURL: config.public.apiBaseURL,
onRequest: async function ({request, options, error}) {

let userSession = useCookie('authToken').value;

if(!userSession?.token || (!userSession?.expiration || new Date(userSession.expiration) <= new Date())) {
console.log("RETRIEVE A NEW TOKEN PLEASE:")
const refreshed = await store.refreshTokenAction();
if(!refreshed) throw new Error('Token refresh failed. Aborting request.');
}
// Retrieve again, I could use refreshCookie()
userSession = useCookie('authToken').value;

if (userSession.token) {
options.headers = options.headers || {}
options.headers.Authorization = `Bearer ${userSession.token}`
}
},
onRequestError({ request, options, error }) {
},
onResponseError({ request, response, options }) {
}
})
return { provide: { authFetch: $authFetch }
}
})
example page that retrieves the data using the authFetch.
<script lang="ts" setup>
const { data, error } = await useAuthFetch(`user/blog/${blogGuid}`);
<script lang="ts" setup>
const { data, error } = await useAuthFetch(`user/blog/${blogGuid}`);
Does somebody know how I can fix this? This might be a SRR issue?
4 Replies
Cue
Cue2mo ago
Most likely it’s propagating from useCookie which accesses the Nuxt instance on the server. I would move it to the top level out of the callback.
patchamamma
patchamamma2mo ago
Thanks for having a look. Sadly putting the useCookie to the top level like this did not fix the problem:
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const store = useAuthStore()

// Function to ensure we have a valid token
const ensureValidToken = async () => {
const authTokenCookie = useCookie('authToken')
let userSession = authTokenCookie.value

if (!userSession?.token || (!userSession?.expiration || new Date(userSession.expiration) <= new Date())) {
console.log("RETRIEVE A NEW TOKEN PLEASE:")
const refreshed = await store.refreshTokenAction()
if (!refreshed) {
throw new Error('Token refresh failed.')
}
// Get the updated cookie value after refresh
userSession = useCookie('authToken').value
}

return userSession.token
}

const $authFetch = $fetch.create({
baseURL: config.public.apiBaseURL,
async onRequest({ request, options }) {
const token = await ensureValidToken()
options.headers = {
...options.headers,
Authorization: `Bearer ${token}`
}
},
onRequestE.................
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const store = useAuthStore()

// Function to ensure we have a valid token
const ensureValidToken = async () => {
const authTokenCookie = useCookie('authToken')
let userSession = authTokenCookie.value

if (!userSession?.token || (!userSession?.expiration || new Date(userSession.expiration) <= new Date())) {
console.log("RETRIEVE A NEW TOKEN PLEASE:")
const refreshed = await store.refreshTokenAction()
if (!refreshed) {
throw new Error('Token refresh failed.')
}
// Get the updated cookie value after refresh
userSession = useCookie('authToken').value
}

return userSession.token
}

const $authFetch = $fetch.create({
baseURL: config.public.apiBaseURL,
async onRequest({ request, options }) {
const token = await ensureValidToken()
options.headers = {
...options.headers,
Authorization: `Bearer ${token}`
}
},
onRequestE.................
Some context: I am using a jwt auth endpoint on a custom nodeJS server, so I am not using the nuxt auth package or SSR functionality (server folder). When setting the ssr to false in the nuxt.config.ts this works perfectly fine. But of course I don't want to lose out on SSR in some pages/components.
Cue
Cue2mo ago
SSR does not require the use of the server directory. It works with SSR disabled because of the very issue I outlined. Lastly, I don’t see, in your revised code, useCookie moved to the top. It should be within the scope of the plugin, where you’ve placed your config and store.
patchamamma
patchamamma2mo ago
Thanks a lot, I did manage to fix the composable problem (example below). Now I need to find out why the authToken.value is empty after a refreshCookie() 😂 probably again a SSR or browser thing, since the refreshToken is for the browser.
export default defineNuxtPlugin((nuxtApp) => {

const config = useRuntimeConfig()
const store= useAuthStore()
const authToken = useCookie('authToken');

const $authFetch = $fetch.create({
baseURL: config.public.apiBaseURL,
onRequest: async function ({request, options, error}) {
let userSession = authToken.value;

if(!userSession?.token || (!userSession?.expiration || new Date(userSession.expiration) <= new Date())) {
console.log("RETRIEVE A NEW TOKEN PLEASE:")
const refreshed = await store.refreshTokenAction();
if(!refreshed) throw new Error('Token refresh failed. Aborting request.');
}
// Retrieve again, I could use refreshCookie()
refreshCookie('authToken');
console.log('auth token from (refreshed) cookie:', authToken.value);
export default defineNuxtPlugin((nuxtApp) => {

const config = useRuntimeConfig()
const store= useAuthStore()
const authToken = useCookie('authToken');

const $authFetch = $fetch.create({
baseURL: config.public.apiBaseURL,
onRequest: async function ({request, options, error}) {
let userSession = authToken.value;

if(!userSession?.token || (!userSession?.expiration || new Date(userSession.expiration) <= new Date())) {
console.log("RETRIEVE A NEW TOKEN PLEASE:")
const refreshed = await store.refreshTokenAction();
if(!refreshed) throw new Error('Token refresh failed. Aborting request.');
}
// Retrieve again, I could use refreshCookie()
refreshCookie('authToken');
console.log('auth token from (refreshed) cookie:', authToken.value);
Well of course I can fix this with just returning the authToken when calling the refreshTokenAction function. But I am just curious how the flow is working now in Nuxt, this is an aspect I'm having difficulty wrapping my head around.
Want results from more Discord servers?
Add your server