opnform-host-nginx/client/composables/useAuth.js

183 lines
5.6 KiB
JavaScript

import { WindowMessageTypes, useWindowMessage } from "~/composables/useWindowMessage"
export const useAuth = () => {
const authStore = useAuthStore()
const workspaceStore = useWorkspacesStore()
const formsStore = useFormsStore()
const logEvent = useAmplitude().logEvent
/**
* Core authentication logic used by both social and direct login
*/
const authenticateUser = async ({ tokenData, source, isNewUser = false }) => {
// Set token first
authStore.setToken(tokenData.token, tokenData.expires_in)
// Fetch initial data
const [userData, workspaces] = await Promise.all([
opnFetch("user"),
fetchAllWorkspaces()
])
// Setup stores
authStore.setUser(userData)
workspaceStore.set(workspaces.data.value)
// Load forms for current workspace
await formsStore.loadAll(workspaceStore.currentId)
// Track analytics
const eventName = isNewUser ? 'register' : 'login'
logEvent(eventName, { source })
try {
// Check if GTM is available before using it
const gtm = typeof useGtm === 'function' ? useGtm() : null
if (gtm && typeof gtm.trackEvent === 'function') {
gtm.trackEvent({
event: eventName,
source
})
}
} catch (error) {
console.error(error)
}
return { userData, workspaces, isNewUser }
}
/**
* Verify that authentication is complete and user data is loaded
* Useful for social auth flows where token might be set but user data not loaded yet
*/
const verifyAuthentication = async () => {
// If we already have user data, no need to verify
if (authStore.check) {
return true
}
// If we have a token but no user data, fetch the user data
if (authStore.token && !authStore.check) {
// Create a promise with retry logic
return new Promise((resolve, reject) => {
const maxRetries = 3
let retryCount = 0
const attemptFetch = async () => {
try {
const userData = await opnFetch("user")
if (userData) {
authStore.setUser(userData)
resolve(true)
} else {
handleRetry("No user data returned")
}
} catch (error) {
handleRetry(`Auth verification failed: ${error.message}`)
}
}
const handleRetry = (reason) => {
retryCount++
if (retryCount < maxRetries) {
console.log(`Retrying auth verification (${retryCount}/${maxRetries}): ${reason}`)
// Exponential backoff
setTimeout(attemptFetch, 100 * Math.pow(2, retryCount))
} else {
console.error(`Auth verification failed after ${maxRetries} attempts`)
reject(new Error(`Auth verification failed after ${maxRetries} attempts`))
}
}
// Start the first attempt
attemptFetch()
})
}
return false
}
/**
* Handle direct login with form validation
*/
const loginWithCredentials = async (form, remember) => {
const tokenData = await form.submit('post', '/login', { data: { remember: remember } })
return authenticateUser({
tokenData,
source: 'credentials'
})
}
/**
* Handle social login callback
*/
const handleSocialCallback = async (provider, code, utmData) => {
const tokenData = await opnFetch(`/oauth/${provider}/callback`, {
method: 'POST',
body: { code, utm_data: utmData }
})
console.log(`[useAuth] handleSocialCallback executed for provider: ${provider}`)
console.log(`[useAuth] Checking window.opener:`, window.opener, `Is closed:`, window.opener ? window.opener.closed : 'N/A')
// Send message to parent window if applicable
if (window.opener && !window.opener.closed) {
console.log(`[useAuth] Attempting to send message ${WindowMessageTypes.OAUTH_PROVIDER_CONNECTED}:${provider} to opener`, window.opener)
useWindowMessage(WindowMessageTypes.OAUTH_PROVIDER_CONNECTED).send(window.opener, {
eventType: `${WindowMessageTypes.OAUTH_PROVIDER_CONNECTED}:${provider}`,
useMessageChannel: false,
waitForAcknowledgment: false
})
console.log(`[useAuth] Message supposedly sent for ${provider}`)
} else {
console.log('[useAuth] Not sending message: window.opener check failed.', { opener: window.opener, closed: window.opener ? window.opener.closed : 'N/A' })
}
return authenticateUser({
tokenData,
source: provider,
isNewUser: tokenData.new_user
})
}
/**
* Handle user registration
*/
const registerUser = async (form) => {
// Register the user first
const data = await form.submit('post', '/register')
// Login the user
const tokenData = await form.submit('post', '/login')
const result = await authenticateUser({
tokenData,
source: form.hear_about_us,
isNewUser: true
})
// Handle AppSumo license if present
if (data.appsumo_license === false) {
useAlert().error(
"Invalid AppSumo license. This probably happened because this license was already" +
" attached to another OpnForm account. Please contact support."
)
} else if (data.appsumo_license === true) {
useAlert().success(
"Your AppSumo license was successfully activated! You now have access to all the" +
" features of the AppSumo deal."
)
}
return { ...result, data }
}
return {
loginWithCredentials,
handleSocialCallback,
registerUser,
verifyAuthentication
}
}