<script src="https://unpkg.com/@descope/[email protected]/dist/index.js"></script> <style> #descope-container { max-width: 420px; margin: 0 auto 80px; text-align: center; } #libby-continue { display: none; margin-top: 24px; padding: 14px 20px; font-size: 16px; font-weight: 600; background: #1e1e1e; color: #fff; border: none; border-radius: 6px; cursor: pointer; transition: opacity 0.2s; } #libby-continue:hover:not(:disabled) { opacity: 0.9; } #libby-continue:disabled { opacity: 0.6; cursor: not-allowed; } #auth-error { display: none; margin-top: 16px; padding: 12px; background: #fee; border: 1px solid #fcc; border-radius: 4px; color: #c33; font-size: 14px; } </style> <script> const LIBBY_URL = 'https://sentry.libbyapp.com/auth/link/900741?ils=queerliblib&continuation=https%3A%2F%2Flibbyapp.com%2Finterview%2Fauthenticate%2Fcard%3Fkey%3Dqueerliblib%26origination%3Dlibrary%252Fqueerliblib'; function isInAppBrowser() { const ua = navigator.userAgent || navigator.vendor || window.opera; return ( /FBAN|FBAV|Instagram|Line|Twitter|LinkedIn|Snapchat|Libby|OverDrive/i.test(ua) || /wv/.test(ua) || (/iPhone|iPad|iPod/.test(ua) && !/Safari/.test(ua) && !/CriOS/.test(ua)) ); } function isMobileDevice() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } function shouldShowButton() { return isInAppBrowser() || isMobileDevice(); } function logAuthEvent(eventType, data = {}) { const debugInfo = { timestamp: new Date().toISOString(), event: eventType, userAgent: navigator.userAgent, isInApp: isInAppBrowser(), isMobile: isMobileDevice(), screenWidth: window.innerWidth, viewportWidth: document.documentElement.clientWidth, ...data }; console.log('[QLL Auth]', eventType, debugInfo); // Optional: send to analytics if available if (window.gtag) { gtag('event', 'auth_' + eventType, { event_category: 'authentication', event_label: debugInfo.userAgent.substring(0, 100) }); } return debugInfo; } function showError(message) { const errorDiv = document.getElementById('auth-error'); if (errorDiv) { errorDiv.textContent = message; errorDiv.style.display = 'block'; } logAuthEvent('error', { message }); } function performRedirect(url, isButtonClick = false) { try { logAuthEvent('redirect_attempt', { url, method: isButtonClick ? 'button_click' : 'auto', shouldShowButton: shouldShowButton() }); window.location.href = url; } catch (err) { logAuthEvent('redirect_failed', { error: err.message }); showError('Redirect failed. Please click the button below to continue.'); const continueBtn = document.getElementById('libby-continue'); if (continueBtn) { continueBtn.style.display = 'inline-block'; continueBtn.disabled = false; } } } document.addEventListener('DOMContentLoaded', () => { logAuthEvent('page_load'); // Prevent re-running auth when returning from Libby const params = new URLSearchParams(window.location.search); if (params.has('continuation') || params.has('libby')) { logAuthEvent('skip_auth', { reason: 'returning_from_libby' }); return; } const main = document.querySelector('main'); if (!main) { logAuthEvent('error', { reason: 'main_element_not_found' }); return; } // Create container const container = document.createElement('div'); container.id = 'descope-container'; const header = document.querySelector('header'); if (header) { container.style.marginTop = `${header.offsetHeight + 40}px`; } main.prepend(container); // Create Descope component const descope = document.createElement('descope-wc'); descope.setAttribute('project-id', 'P2fYs52LoB3Po48CcWl3XkgBw8z5'); descope.setAttribute('flow-id', 'sign-up-qll-magic-link'); descope.setAttribute('base-url', 'https://auth.queerliberationlibrary.org'); container.appendChild(descope); // Create Continue button const continueBtn = document.createElement('button'); continueBtn.id = 'libby-continue'; continueBtn.textContent = 'Continue to Libby'; continueBtn.onclick = () => { continueBtn.disabled = true; continueBtn.textContent = 'Opening Libby...'; performRedirect(LIBBY_URL, true); }; container.appendChild(continueBtn); // Create error message div const errorDiv = document.createElement('div'); errorDiv.id = 'auth-error'; container.appendChild(errorDiv); // Wait for Descope to be ready customElements.whenDefined('descope-wc').then(() => { logAuthEvent('descope_ready'); descope.addEventListener('success', (e) => { const sessionData = e.detail || {}; logAuthEvent('auth_success', { hasSessionJwt: !!sessionData.sessionJwt, hasUser: !!sessionData.user }); // Verify session token exists if (!sessionData.sessionJwt && !sessionData.session) { logAuthEvent('warning', { reason: 'no_session_token_in_response' }); } setTimeout(() => { if (shouldShowButton()) { logAuthEvent('showing_button', { isInApp: isInAppBrowser(), isMobile: isMobileDevice() }); continueBtn.style.display = 'inline-block'; } else { logAuthEvent('auto_redirect', { reason: 'desktop_browser' }); performRedirect(LIBBY_URL, false); } }, 400); }); descope.addEventListener('error', (err) => { const errorMessage = err.detail?.message || err.message || 'Authentication error occurred'; logAuthEvent('auth_error', { error: errorMessage, detail: err.detail }); showError(`Authentication failed: ${errorMessage}. Please try again.`); }); }).catch(err => { logAuthEvent('descope_load_error', { error: err.message }); showError('Failed to load authentication component. Please refresh the page.'); }); }); </script> <script> // Dark mode theme switcher (keep existing functionality) function updateDescopeTheme() { const descope = document.querySelector('descope-wc'); if (!descope) return; const htmlElement = document.documentElement; if (htmlElement.classList.contains('darkmode-dark')) { descope.setAttribute('theme', 'dark'); } else { descope.setAttribute('theme', 'light'); } } updateDescopeTheme(); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.attributeName === 'class') { updateDescopeTheme(); } } }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); </script>