Authentication: using server-side (SSR) only: Nuxt3 + Laravel/Sanctum using SPA authentication - how to?
Hi all. I'm having some issues with SPA authentication using Laravel with Sanctum and Nuxt 3. Everything is working fine using client side with nuxt using client while loggin, but my goal is to use server side to prevent having a blank page when I'm authenticating my user.
homepage when clicking on my "login" button, this function is called
<script setup>
const login = async () => {
navigateTo(
`...discord.c../api/oauth2/authorize?client_id=CLIENT_ID_XXXXXXXXXXXXXXX&redirect_uri=..tp://localhost:3000/login&response_type=code&scope=identify%20email%20guilds.join%20guilds`,
{ external: true }
)
}
</script>
After discord authorisation, the user is redirected to localhost:3000/login?code=xxxxxxx And now I'm calling my own laravel sanctum api to auth my user.
When the user is on this page, I call a middleware "catchDiscordToken". With this token I authenticate my user to my laravel sanctum API.
catchDiscordToken.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
if (process.server) return
const code = to.query.code as string
if (!code) {
return navigateTo('/')
}
const auth = useAuthStore()
return navigateTo('/')
})
As you can see above I'm using if (process.server) return' but I'd like to do the opposite by using if (process.client) return' but I'm having so many problems here. Like I can't use the useCookie('XSRF-TOKEN') mentioned below.
useAuthStore.ts
import { defineStore } from 'pinia'
type User = {
id: number
email: string
global_name: string
}
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null)
const isLoggedIn = computed(() => !!user.value)
async function logout() {
await useFetchApi('/api/logout', { method: 'POST' })
user.value = null
return navigateTo('/')
}
async function fetchUser() {
const { data, error } = await useFetchApi('/api/user')
user.value = data.value as User
}
async function login(code: string) {
await useFetchApi('/sanctum/csrf-cookie')
const { data, error } = await useFetchApi('/login/discord', {
method: 'POST',
params: {
code: code,
},
})
user.value = data.value.user as User
}
return { user, isLoggedIn, fetchUser, login, logout }
})
useFetchApi.ts
import type { UseFetchOptions } from '#app'
export function useFetchApi<T>(path: string, options: UseFetchOptions<T> = {}) {
let headers: any = {}
const token = useCookie('XSRF-TOKEN')
if (token.value) {
headers['X-XSRF-TOKEN'] = decodeURIComponent(token.value) as string
}
// Pour envoyer les infos nécessaires à l'authentification lors d'une requête envoyée depuis le serveur
const runtimeConfig = useRuntimeConfig()
if (process.server) {
headers = {
...headers,
...useRequestHeaders(['referer', 'cookie']),
}
}
return useFetch(`${runtimeConfig.public.apiUrl}${path}`, {
credentials: 'include',
watch: false,
...options,
headers: {
...headers,
...options?.headers,
Accept: 'application/json',
'Content-Type': 'application/json',
},
onRequestError(error) {
console.log(error)
},
})
}
Sometimes I get this error when I've switched to server-side "Accessing Nuxt composables inside useAsyncData after awaiting some other promise throws error: "A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.". But now it doesn't. When I call this middleware on my server side only, useCookie returns an undefined value.
What am I doing wrong here? Everything works when I'm using this middleware on the client side but not on the server side. Can I achieve this goal using only my server? I want my user to be redirected to my homepage immediately after auth.
Please or to participate in this conversation.