Skip to content

Commit 6c95b94

Browse files
committed
Fix: Completes Google OAuth user fetch on client
Completes the Google OAuth flow by ensuring the user is fetched on the client-side after a successful OAuth redirect. It achieves this by: - Enabling permissions in the playground config. - Modifying the Google callback to append an `oauth_success` flag to the redirect URL. - Updating the authorization middleware to conditionally fetch the user using SSR via `useFetch` when the `oauth_success` flag is present. This ensures `httpOnly` cookies are correctly handled.
1 parent 8bd3e58 commit 6c95b94

File tree

4 files changed

+53
-20
lines changed

4 files changed

+53
-20
lines changed

playground/nuxt.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export default defineNuxtConfig({
1212
tokenExpiration: 10,
1313
// TODO: if it is uncommented it makes `yarn test:types` fails - for some unknown reason
1414
// INFO: if it is commented out you can not login to the playground
15-
/* permissions: {
15+
permissions: {
1616
admin: ['*'],
17-
}, */
17+
},
1818
google: {
1919
clientId: process.env.GOOGLE_CLIENT_ID || 'demo-client-id',
2020
clientSecret: process.env.GOOGLE_CLIENT_SECRET || 'demo-client-secret',

src/runtime/composables/useAuthentication.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useRuntimeConfig } from '#app'
1+
import { useState, useRuntimeConfig, useFetch } from '#app'
22
import { computed, readonly } from 'vue'
33
import type { User, UserWithoutPassword } from 'nuxt-users/utils'
44

@@ -55,10 +55,35 @@ export const useAuthentication = () => {
5555
}
5656
}
5757

58-
const fetchUser = async () => {
58+
const fetchUser = async (useSSR = false) => {
5959
try {
60-
const response = await $fetch<{ user: UserWithoutPassword }>(`${apiBasePath}/me`, { method: 'GET' })
61-
user.value = response.user
60+
let userData: UserWithoutPassword | null = null
61+
62+
if (useSSR) {
63+
// Use useFetch for SSR - properly handles cookies
64+
const { data, error } = await useFetch<{ user: UserWithoutPassword }>(`${apiBasePath}/me`, {
65+
server: true,
66+
lazy: false
67+
})
68+
69+
if (error.value) {
70+
throw new Error(error.value.message || 'Failed to fetch user')
71+
}
72+
73+
userData = data.value?.user || null
74+
} else {
75+
// Use $fetch for client-only requests
76+
const response = await $fetch<{ user: UserWithoutPassword }>(`${apiBasePath}/me`, {
77+
method: 'GET'
78+
})
79+
userData = response.user
80+
}
81+
82+
if (!userData) {
83+
throw new Error('No user data received')
84+
}
85+
86+
user.value = userData
6287

6388
// Update the appropriate storage with fresh user data
6489
if (import.meta.client) {
@@ -67,17 +92,17 @@ export const useAuthentication = () => {
6792
const wasInSessionStorage = sessionStorage.getItem('user') !== null
6893

6994
if (wasInLocalStorage) {
70-
localStorage.setItem('user', JSON.stringify(response.user))
95+
localStorage.setItem('user', JSON.stringify(userData))
7196
}
7297
else if (wasInSessionStorage) {
73-
sessionStorage.setItem('user', JSON.stringify(response.user))
98+
sessionStorage.setItem('user', JSON.stringify(userData))
7499
}
75100
else {
76101
// Default to sessionStorage for new sessions without rememberMe
77-
sessionStorage.setItem('user', JSON.stringify(response.user))
102+
sessionStorage.setItem('user', JSON.stringify(userData))
78103
}
79104
}
80-
return response.user
105+
return userData
81106
}
82107
catch (error) {
83108
console.error('Failed to fetch user:', error)

src/runtime/middleware/authorization.client.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { hasPermission, isWhitelisted } from '../utils/permissions'
44
import { NO_AUTH_PATHS, NO_AUTH_API_PATHS } from '../constants'
55
import type { ModuleOptions } from 'nuxt-users/utils'
66

7-
export default defineNuxtRouteMiddleware((to) => {
7+
export default defineNuxtRouteMiddleware(async (to) => {
88
const { public: { nuxtUsers: publicNuxtUsers } } = useRuntimeConfig()
99
const publicOptions = publicNuxtUsers as ModuleOptions
1010
const base = publicOptions.apiBasePath || '/api/nuxt-users'
@@ -27,7 +27,18 @@ export default defineNuxtRouteMiddleware((to) => {
2727
return
2828
}
2929

30-
const { isAuthenticated, user } = useAuthentication()
30+
const { isAuthenticated, user, fetchUser } = useAuthentication()
31+
32+
// If not authenticated but oauth_success flag is present, fetch user using SSR
33+
if (!isAuthenticated.value && to.query.oauth_success === 'true') {
34+
try {
35+
await fetchUser(true) // Use SSR to properly handle httpOnly cookies
36+
}
37+
catch (error) {
38+
console.error('[Nuxt Users] Failed to fetch user after OAuth:', error)
39+
}
40+
}
41+
3142
if (!isAuthenticated.value) {
3243
console.log(`[Nuxt Users] client.middleware.auth.global: Unauthenticated ${to.path}, redirecting to /login`)
3344
return navigateTo('/login')
@@ -38,10 +49,4 @@ export default defineNuxtRouteMiddleware((to) => {
3849
console.log(`[Nuxt Users] client.middleware.auth.global: User with role ${user.value?.role} denied access to ${to.path}`)
3950
return navigateTo('/login')
4051
}
41-
42-
console.log('[Nuxt Users] client.middleware.auth.global', {
43-
isAuthenticated: isAuthenticated.value,
44-
userRole: user.value?.role,
45-
to: to.path
46-
})
4752
})

src/runtime/server/api/nuxt-users/auth/google/callback.get.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,12 @@ export default defineEventHandler(async (event) => {
7878

7979
console.log(`[Nuxt Users] Google OAuth login successful for user: ${user.email}`)
8080

81-
// Redirect to success page
81+
// Redirect to success page with oauth_success flag
8282
const successRedirect = options.auth.google.successRedirect || '/'
83-
return sendRedirect(event, successRedirect)
83+
const redirectUrl = successRedirect.includes('?')
84+
? `${successRedirect}&oauth_success=true`
85+
: `${successRedirect}?oauth_success=true`
86+
return sendRedirect(event, redirectUrl)
8487
}
8588
catch (error) {
8689
console.error('[Nuxt Users] Google OAuth callback error:', error)

0 commit comments

Comments
 (0)