diff --git a/README.md b/README.md index 46563564..b314d718 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Nuxt Auth Utils automatically adds some plugins to fetch the current user sessio ```vue ``` @@ -106,6 +108,10 @@ interface UserSessionComposable { * Clear the user session and remove the session cookie. */ clear: () => Promise + /** + * Open the OAuth route in a popup that auto-closes when successful. + */ + openInPopup: (route: string, size?: { width?: number, height?: number }) => void } ``` diff --git a/playground/app.vue b/playground/app.vue index 5fdbd2c2..1ad0c2ca 100644 --- a/playground/app.vue +++ b/playground/app.vue @@ -1,6 +1,7 @@ @@ -259,6 +262,14 @@ const providers = computed(() => +
+ Popup mode +
diff --git a/src/runtime/app/composables/session.ts b/src/runtime/app/composables/session.ts index fc569c98..eadc7c9f 100644 --- a/src/runtime/app/composables/session.ts +++ b/src/runtime/app/composables/session.ts @@ -38,12 +38,41 @@ export function useUserSession(): UserSessionComposable { } } + const popupListener = (e: StorageEvent) => { + if (e.key === 'temp-nuxt-auth-utils-popup') { + fetch() + window.removeEventListener('storage', popupListener) + } + } + const openInPopup = (route: string, size: { width?: number, height?: number } = {}) => { + // Set a local storage item to tell the popup that we pending auth + localStorage.setItem('temp-nuxt-auth-utils-popup', 'true') + + const width = size.width ?? 960 + const height = size.height ?? 600 + const top = (window.top?.outerHeight ?? 0) / 2 + + (window.top?.screenY ?? 0) + - height / 2 + const left = (window.top?.outerWidth ?? 0) / 2 + + (window.top?.screenX ?? 0) + - width / 2 + + window.open( + route, + 'nuxt-auth-utils-popup', + `width=${width}, height=${height}, top=${top}, left=${left}, toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no`, + ) + + window.addEventListener('storage', popupListener) + } + return { ready: computed(() => authReadyState.value), loggedIn: computed(() => Boolean(sessionState.value.user)), user: computed(() => sessionState.value.user || null), session: sessionState, fetch, + openInPopup, clear, } } diff --git a/src/runtime/app/plugins/session.client.ts b/src/runtime/app/plugins/session.client.ts index 13a2991d..146ccccb 100644 --- a/src/runtime/app/plugins/session.client.ts +++ b/src/runtime/app/plugins/session.client.ts @@ -12,4 +12,11 @@ export default defineNuxtPlugin(async (nuxtApp) => { await useUserSession().fetch() }) } + + if (localStorage.getItem('temp-nuxt-auth-utils-popup')) { + // There is a local storage item. That's mean we are coming back in the popup + localStorage.removeItem('temp-nuxt-auth-utils-popup') + const error = useError() + if (!error.value) window.close() + } }) diff --git a/src/runtime/types/session.ts b/src/runtime/types/session.ts index 2533f674..e301da45 100644 --- a/src/runtime/types/session.ts +++ b/src/runtime/types/session.ts @@ -50,4 +50,8 @@ export interface UserSessionComposable { * Clear the user session and remove the session cookie. */ clear: () => Promise + /** + * Open the OAuth route in a popup that auto-closes when successful. + */ + openInPopup: (route: string, size?: { width?: number, height?: number }) => void }