@polymindslabs/widget-sdk
Version:
Universal Job Widget SDK for embedding job boards and apply buttons
176 lines (160 loc) • 5.47 kB
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>