UNPKG

@polymindslabs/widget-sdk

Version:

Universal Job Widget SDK for embedding job boards and apply buttons

176 lines (160 loc) 5.47 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Inzif OAuth Callback</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } .container { text-align: center; padding: 2rem; } .spinner { border: 3px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top: 3px solid white; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto 1rem; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .error { background: rgba(255, 255, 255, 0.1); padding: 1rem; border-radius: 8px; margin-top: 1rem; } .close-button { background: white; color: #667eea; border: none; padding: 0.75rem 2rem; border-radius: 4px; font-size: 16px; cursor: pointer; margin-top: 1rem; transition: opacity 0.2s; } .close-button:hover { opacity: 0.9; } </style> </head> <body> <div class="container"> <div class="spinner"></div> <h2>Completing Authentication...</h2> <p>Please wait while we finalize your login.</p> </div> <script> (async function() { const container = document.querySelector('.container'); try { // Get URL parameters const params = new URLSearchParams(window.location.search); const code = params.get('code'); const state = params.get('state'); const error = params.get('error'); const error_description = params.get('error_description'); // Handle OAuth errors if (error) { throw new Error(error_description || error); } // Validate state for CSRF protection const storedState = sessionStorage.getItem('inzif_widget_state'); if (!state || state !== storedState) { throw new Error('Invalid state parameter'); } // Validate code if (!code) { throw new Error('No authorization code received'); } // Exchange code for token const apiUrl = sessionStorage.getItem('job_widget_api_url'); const tenantId = sessionStorage.getItem('job_widget_tenant_id'); const response = await fetch(`${apiUrl}/auth/v1/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: code, client_id: `widget_${tenantId}`, redirect_uri: window.location.origin + '/oauth-callback.html' }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error_description || 'Failed to exchange code for token'); } const tokenData = await response.json(); // Store token const token = { access_token: tokenData.access_token, expires_at: Date.now() + (tokenData.expires_in * 1000), scope: tokenData.scope?.split(' ') || ['profile:basic', 'email'] }; sessionStorage.setItem('inzif_widget_token', JSON.stringify(token)); // Send success message to parent window if (window.opener) { window.opener.postMessage({ type: 'inzif-oauth-success', token: token }, window.location.origin); } // Show success message container.innerHTML = ` <svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M20 6L9 17L4 12" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <h2>Authentication Successful!</h2> <p>You can now close this window and continue with your application.</p> <button class="close-button" onclick="window.close()">Close Window</button> `; // Auto-close after 3 seconds setTimeout(() => { window.close(); }, 3000); } catch (error) { // Send error message to parent window if (window.opener) { window.opener.postMessage({ type: 'inzif-oauth-error', error: { code: 'AUTH_FAILED', message: error.message, details: null } }, window.location.origin); } // Show error message container.innerHTML = ` <svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M18 6L6 18M6 6L18 18" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <h2>Authentication Failed</h2> <div class="error"> <p>${error.message || 'An error occurred during authentication'}</p> </div> <button class="close-button" onclick="window.close()">Close Window</button> `; } })(); </script> </body> </html>