ultimate-jekyll-manager
Version:
Ultimate Jekyll dependency manager
140 lines (115 loc) • 4.64 kB
JavaScript
// This file is required by /token page to generate custom auth tokens for extensions/apps
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
import webManager from 'web-manager';
// Module
export default function () {
const $status = document.getElementById('token-status');
const $error = document.getElementById('token-error');
const $errorMessage = document.getElementById('token-error-message');
// Get URL params
const url = new URL(window.location.href);
const authReturnUrl = url.searchParams.get('authReturnUrl');
// Handle DOM ready
webManager.dom().ready()
.then(async () => {
// Log
console.log('[Token] Initialized. authReturnUrl:', authReturnUrl);
// Validate authReturnUrl if present
if (authReturnUrl && !webManager.isValidRedirectUrl(authReturnUrl)) {
showError('Invalid redirect URL');
return;
}
// Wait for auth to be ready and get user
webManager.auth().listen({ once: true }, async (state) => {
const user = state.user;
// Should not happen since page requires auth, but just in case
if (!user) {
showError('Not authenticated. Please sign in first.');
return;
}
try {
// Generate custom token
updateStatus('Generating secure token...');
const token = await generateCustomToken();
// Update status
updateStatus('Token generated successfully!');
// Handle redirect or URL update
if (authReturnUrl) {
// Redirect to return URL with token
updateStatus('Redirecting...');
const returnUrl = new URL(authReturnUrl);
returnUrl.searchParams.set('authToken', token);
// LEGACY: Reformat token for desktop app deep links
// TODO: Remove this block when legacy desktop app support is no longer needed
_legacyTranslateTokenRedirect(returnUrl, token);
const redirectUrl = returnUrl.toString();
console.log('[Token] Redirecting to:', redirectUrl);
// Show retry button after a delay in case the redirect was cancelled (e.g. custom protocol dialog)
setTimeout(() => {
updateStatus('If you were not redirected, <a href="' + webManager.utilities().escapeHTML(redirectUrl) + '">click here to try again</a>.', true);
}, 3000);
window.location.href = redirectUrl;
} else {
// Add token to current URL (for browser extensions)
// Extension background will detect this and close the tab
url.searchParams.set('authToken', token);
window.history.replaceState({}, '', url.toString());
updateStatus('You can close this tab now.');
}
} catch (error) {
console.error('[Token] Error generating token:', error);
showError(error.message || 'Failed to generate token. Please try again.');
}
});
});
// Generate custom token via backend-manager API
async function generateCustomToken() {
const serverApiURL = `${webManager.getApiUrl()}/backend-manager/user/token`;
const response = await authorizedFetch(serverApiURL, {
method: 'POST',
timeout: 60000,
response: 'json',
tries: 2,
});
// Extract token from response
const token = response?.token;
if (!token) {
throw new Error('No token received from server');
}
return token;
}
// Update status message (rawHtml=true only when caller has pre-escaped the content)
function updateStatus(message, rawHtml) {
if (!$status) {
return;
}
const $p = document.createElement('p');
$p.className = 'text-muted small';
if (rawHtml) {
$p.innerHTML = message;
} else {
$p.textContent = message;
}
$status.innerHTML = '';
$status.appendChild($p);
}
// Show error message
function showError(message) {
if ($error && $errorMessage) {
$errorMessage.textContent = message;
$error.classList.remove('d-none');
}
if ($status) {
$status.classList.add('d-none');
}
}
// LEGACY: Reformat token for desktop app deep links
// Legacy desktop apps expect ?payload={"token":"X"} instead of ?authToken=X for custom protocol URLs
// TODO: Remove this function AND its call above when legacy desktop app support is no longer needed
function _legacyTranslateTokenRedirect(returnUrl, token) {
if (returnUrl.protocol !== 'http:' && returnUrl.protocol !== 'https:') {
returnUrl.searchParams.delete('authToken');
returnUrl.searchParams.set('payload', JSON.stringify({ token: token }));
}
}
}