183 lines
5.6 KiB
JavaScript
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
|
|
}
|
|
}
|