First, let’s modify the Auth0 configuration to handle the authentication state more reliably:
const auth0 = createAuth0({
domain: config.auth_domain,
clientId: config.auth_client_id,
authorizationParams: {
audience: config.auth_audience,
redirect_uri: window.location.origin,
scope: 'openid profile email offline_access',
},
useRefreshTokens: true,
useRefreshTokensFallback: true,
cacheLocation: 'localstorage',
// Add this to ensure Auth0 is ready before mounting
skipRedirectCallback: true
})
Then, modify your route guard to properly handle the callback
router.beforeEach(async (to) => {
// Check if it's a callback URL
const isCallback = to.fullPath.includes('code=') && to.fullPath.includes('state=')
try {
// If it's the callback URL, handle the redirect
if (isCallback) {
// Explicitly handle the redirect
await auth0.handleRedirectCallback()
// After successful callback, redirect to home or intended route
return { path: '/' } // or your desired route
}
// Check authentication state
const isAuthenticated = await auth0.isAuthenticated()
// If route requires auth and user isn't authenticated
if (to.meta.requiresAuth && !isAuthenticated) {
// Redirect to login
await auth0.loginWithRedirect({
appState: { targetUrl: to.fullPath }
})
return false
}
} catch (error) {
console.error('Auth error:', error)
return { path: '/error' } // Redirect to error page
}
})
To handle user data loading, create a dedicated auth store using Pinia:
import { defineStore } from 'pinia'
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null,
isLoading: true,
error: null
}),
actions: {
async initializeAuth() {
try {
// Check if user is authenticated
const isAuthenticated = await auth0.isAuthenticated()
if (isAuthenticated) {
// Get the user info
const user = await auth0.getUser()
this.user = user
}
} catch (error) {
this.error = error
} finally {
this.isLoading = false
}
}
}
})
Finally, in your main App.vue, initialize the auth state:
<script setup>
import { onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'
const authStore = useAuthStore()
onMounted(async () => {
await authStore.initializeAuth()
})
</script>
Key improvements in this solution:
skipRedirectCallback: true
prevents Auth0 from automatically handling the callback, giving you more control over the process.
handleRedirectCallback()
is used explicitly in the route guard, which helps prevent the infinite loop issue you were experiencing.
- Using a Pinia store for auth state management provides a centralized place to handle user data and authentication state.
- The solution properly handles error cases and loading states.
To solve your specific issues:
- The 403 error: This often occurs when the session isn’t properly initialized. By using
skipRedirectCallback
and explicitly handling the callback, we reduce the chance of this happening.
- The infinite loop: This was likely happening because the route guard wasn’t properly resolving the authentication state. The new implementation ensures we only redirect when necessary and properly handles the callback state.
- Waiting for the session: The Pinia store provides a clean way to track the loading state and ensure the application only renders when authentication is properly initialized.