Fix: Complete OIDC login flow - exchange code for tokens on frontend
The backend redirected to /?oidc_code=<code> after successful OIDC auth, but the frontend never exchanged this code for JWT tokens. The user was redirected back to the login page every time. - app.js: Detect oidc_code in URL params before token check, call POST /api/auth/oidc/exchange, store tokens, reload clean - auth.js: Fallback handler if oidc_code lands on login page Fixes #90
This commit is contained in:
@@ -1597,12 +1597,86 @@ function checkAuthentication() {
|
|||||||
try {
|
try {
|
||||||
// Simplified authentication check - just verify token exists
|
// Simplified authentication check - just verify token exists
|
||||||
const TOKEN_KEY = 'oxicloud_token';
|
const TOKEN_KEY = 'oxicloud_token';
|
||||||
|
const REFRESH_TOKEN_KEY = 'oxicloud_refresh_token';
|
||||||
|
const TOKEN_EXPIRY_KEY = 'oxicloud_token_expiry';
|
||||||
const USER_DATA_KEY = 'oxicloud_user';
|
const USER_DATA_KEY = 'oxicloud_user';
|
||||||
|
|
||||||
// Reset counters to prevent loops
|
// Reset counters to prevent loops
|
||||||
sessionStorage.removeItem('redirect_count');
|
sessionStorage.removeItem('redirect_count');
|
||||||
localStorage.setItem('refresh_attempts', '0');
|
localStorage.setItem('refresh_attempts', '0');
|
||||||
|
|
||||||
|
// --- OIDC exchange code handling ---
|
||||||
|
// After OIDC login, the backend redirects here with ?oidc_code=...
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const oidcCode = urlParams.get('oidc_code');
|
||||||
|
|
||||||
|
if (oidcCode) {
|
||||||
|
console.log('OIDC exchange code detected, exchanging for tokens...');
|
||||||
|
try {
|
||||||
|
const exchangeResponse = await fetch('/api/auth/oidc/exchange', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ code: oidcCode })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!exchangeResponse.ok) {
|
||||||
|
const errText = await exchangeResponse.text();
|
||||||
|
console.error('OIDC token exchange failed:', exchangeResponse.status, errText);
|
||||||
|
window.location.href = '/login.html?source=oidc_error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await exchangeResponse.json();
|
||||||
|
console.log('OIDC token exchange successful');
|
||||||
|
|
||||||
|
// Store tokens (same logic as password login in auth.js)
|
||||||
|
const token = data.access_token || data.token;
|
||||||
|
const refreshToken = data.refresh_token || data.refreshToken;
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
localStorage.setItem(TOKEN_KEY, token);
|
||||||
|
if (refreshToken) localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);
|
||||||
|
|
||||||
|
// Parse JWT expiry
|
||||||
|
let parsedExpiry = false;
|
||||||
|
const tokenParts = token.split('.');
|
||||||
|
if (tokenParts.length === 3) {
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(atob(tokenParts[1]));
|
||||||
|
if (payload.exp) {
|
||||||
|
const expiryDate = new Date(payload.exp * 1000);
|
||||||
|
if (!isNaN(expiryDate.getTime())) {
|
||||||
|
localStorage.setItem(TOKEN_EXPIRY_KEY, expiryDate.toISOString());
|
||||||
|
parsedExpiry = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing JWT:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!parsedExpiry) {
|
||||||
|
const expiry = new Date();
|
||||||
|
expiry.setDate(expiry.getDate() + 30);
|
||||||
|
localStorage.setItem(TOKEN_EXPIRY_KEY, expiry.toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store user data
|
||||||
|
if (data.user) {
|
||||||
|
localStorage.setItem(USER_DATA_KEY, JSON.stringify(data.user));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean URL and reload without the oidc_code param
|
||||||
|
window.history.replaceState({}, document.title, '/');
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('OIDC exchange error:', err);
|
||||||
|
window.location.href = '/login.html?source=oidc_error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Simple token check - just verify it exists
|
// Simple token check - just verify it exists
|
||||||
const token = localStorage.getItem(TOKEN_KEY);
|
const token = localStorage.getItem(TOKEN_KEY);
|
||||||
|
|
||||||
|
|||||||
@@ -531,6 +531,62 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- OIDC exchange code handling (fallback if landing on login page) ---
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const oidcCode = urlParams.get('oidc_code');
|
||||||
|
|
||||||
|
if (oidcCode) {
|
||||||
|
console.log('OIDC exchange code detected on login page, exchanging...');
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const resp = await fetch('/api/auth/oidc/exchange', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ code: oidcCode })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resp.ok) {
|
||||||
|
console.error('OIDC exchange failed:', resp.status);
|
||||||
|
return; // Fall through to normal login page
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await resp.json();
|
||||||
|
const token = data.access_token || data.token;
|
||||||
|
const refreshToken = data.refresh_token || data.refreshToken;
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
localStorage.setItem(TOKEN_KEY, token);
|
||||||
|
if (refreshToken) localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);
|
||||||
|
|
||||||
|
// Parse JWT expiry
|
||||||
|
const tokenParts = token.split('.');
|
||||||
|
if (tokenParts.length === 3) {
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(atob(tokenParts[1]));
|
||||||
|
if (payload.exp) {
|
||||||
|
const expiryDate = new Date(payload.exp * 1000);
|
||||||
|
if (!isNaN(expiryDate.getTime())) {
|
||||||
|
localStorage.setItem(TOKEN_EXPIRY_KEY, expiryDate.toISOString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore parse errors */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.user) {
|
||||||
|
localStorage.setItem(USER_DATA_KEY, JSON.stringify(data.user));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to main app
|
||||||
|
window.location.href = '/?no_redirect=true';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('OIDC exchange error:', err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return; // Don't initialize login page while exchanging
|
||||||
|
}
|
||||||
|
|
||||||
if (authInitialized) {
|
if (authInitialized) {
|
||||||
console.log('Auth already initialized, skipping');
|
console.log('Auth already initialized, skipping');
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user