Commit ce475776 authored by Your Name's avatar Your Name

Fix: OAuth2 popup flow now closes properly and redirects main window

- Added md5 filter to Jinja2 templates for Gravatar
- Added popup=1 parameter to OAuth2 URLs
- Added persistent oauth2_popup_mode session flag
- Both Google and GitHub OAuth flows now properly detect popup mode
- Callback now correctly sends postMessage to opener window
- Popup closes automatically and main window redirects to dashboard
parent 15f21aba
......@@ -494,6 +494,10 @@ app = FastAPI(
# Initialize Jinja2 templates with custom globals for proxy-aware URLs
templates = Jinja2Templates(directory="templates")
# Add MD5 filter for Gravatar
import hashlib
templates.env.filters['md5'] = lambda s: hashlib.md5(s.lower().encode('utf-8')).hexdigest() if s else ''
# Add custom template globals for proxy-aware URL generation
def setup_template_globals():
"""Setup Jinja2 template globals for proxy-aware URLs"""
......@@ -3235,6 +3239,14 @@ async def oauth2_google_initiate(request: Request):
'state': oauth._state,
'code_verifier': oauth._code_verifier
}
# Detect if this is a popup request (from Referer header or popup parameter)
referer = request.headers.get('Referer', '')
is_popup = 'popup=1' in referer or request.query_params.get('popup') == '1'
if is_popup:
request.session['oauth2_popup'] = True
# Also store a more persistent flag
request.session['oauth2_popup_mode'] = True
return RedirectResponse(url=auth_url, status_code=303)
except Exception as e:
......@@ -3362,8 +3374,30 @@ async def oauth2_google_callback(request: Request, code: str = Query(...), state
# Cleanup session data
request.session.pop('oauth2_google', None)
# Check if this is a popup window request
is_popup = request.session.pop('oauth2_popup', False) or request.session.pop('oauth2_popup_mode', False)
if is_popup:
# Return HTML with postMessage to opener
return HTMLResponse(content=f'''
<!DOCTYPE html>
<html>
<head><title>Authentication Complete</title></head>
<body>
<script>
if (window.opener) {{
window.opener.postMessage({{
type: 'oauth2_complete',
redirect_url: '{url_for(request, "/dashboard")}'
}}, '*');
}}
window.close();
</script>
</body>
</html>
''')
# Redirect to dashboard
# Redirect to dashboard for regular requests
return RedirectResponse(url=url_for(request, "/dashboard"), status_code=303)
except Exception as e:
......@@ -3399,6 +3433,14 @@ async def oauth2_github_initiate(request: Request):
request.session['oauth2_github'] = {
'state': oauth._state
}
# Detect if this is a popup request (from Referer header or popup parameter)
referer = request.headers.get('Referer', '')
is_popup = 'popup=1' in referer or request.query_params.get('popup') == '1'
if is_popup:
request.session['oauth2_popup'] = True
# Also store a more persistent flag
request.session['oauth2_popup_mode'] = True
return RedirectResponse(url=auth_url, status_code=303)
except Exception as e:
......@@ -3516,8 +3558,30 @@ async def oauth2_github_callback(request: Request, code: str = Query(...), state
# Cleanup session data
request.session.pop('oauth2_github', None)
# Check if this is a popup window request
is_popup = request.session.pop('oauth2_popup', False) or request.session.pop('oauth2_popup_mode', False)
if is_popup:
# Return HTML with postMessage to opener
return HTMLResponse(content=f'''
<!DOCTYPE html>
<html>
<head><title>Authentication Complete</title></head>
<body>
<script>
if (window.opener) {{
window.opener.postMessage({{
type: 'oauth2_complete',
redirect_url: '{url_for(request, "/dashboard")}'
}}, '*');
}}
window.close();
</script>
</body>
</html>
''')
# Redirect to dashboard
# Redirect to dashboard for regular requests
return RedirectResponse(url=url_for(request, "/dashboard"), status_code=303)
except Exception as e:
......
......@@ -75,7 +75,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div style="display: flex; flex-direction: column; gap: 12px;">
{% if config.oauth2.google and config.oauth2.google.enabled %}
<a href="/auth/oauth2/google" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #1a1a2e; color: #e0e0e0; font-weight: 500; text-decoration: none; transition: background 0.2s;">
<button onclick="openOAuthPopup('/auth/oauth2/google')" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #1a1a2e; color: #e0e0e0; font-weight: 500; text-decoration: none; transition: background 0.2s; cursor: pointer; width: 100%;">
<svg viewBox="0 0 24 24" width="20" height="20">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
......@@ -83,18 +83,75 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
</svg>
Continue with Google
</a>
</button>
{% endif %}
{% if config.oauth2.github and config.oauth2.github.enabled %}
<a href="/auth/oauth2/github" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #24292e; color: #ffffff; font-weight: 500; text-decoration: none; transition: background 0.2s;">
<button onclick="openOAuthPopup('/auth/oauth2/github')" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #24292e; color: #ffffff; font-weight: 500; text-decoration: none; transition: background 0.2s; cursor: pointer; width: 100%;">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
Continue with GitHub
</a>
</button>
{% endif %}
</div>
{% endif %}
</div>
<script>
// OAuth2 Popup Handling
function openOAuthPopup(url) {
// Add popup parameter to URL
const separator = url.includes('?') ? '&' : '?';
const popupUrl = url + separator + 'popup=1';
// Calculate popup position (center screen)
const width = 500;
const height = 650;
const left = (window.innerWidth / 2) - (width / 2);
const top = (window.innerHeight / 2) - (height / 2);
// Open popup window
const popup = window.open(
popupUrl,
'OAuth2 Authentication',
`width=${width},height=${height},top=${top},left=${left},resizable=no,scrollbars=yes,status=no`
);
// Listen for popup messages
const messageListener = function(event) {
if (event.data.type === 'oauth2_complete') {
// Close popup
popup.close();
// Remove listener
window.removeEventListener('message', messageListener);
// Redirect to dashboard
window.location.href = event.data.redirect_url || '/dashboard';
} else if (event.data.type === 'oauth2_error') {
// Close popup
popup.close();
// Remove listener
window.removeEventListener('message', messageListener);
// Show error
alert(event.data.error || 'Authentication failed');
}
};
window.addEventListener('message', messageListener);
// Fallback if popup is blocked or closed manually
const popupCheck = setInterval(function() {
if (popup.closed) {
clearInterval(popupCheck);
window.removeEventListener('message', messageListener);
}
}, 500);
}
</script>
{% endif %}
</div>
{% endblock %}
......@@ -87,7 +87,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div style="display: flex; flex-direction: column; gap: 12px;">
{% if config.oauth2.google and config.oauth2.google.enabled %}
<a href="/auth/oauth2/google" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #1a1a2e; color: #e0e0e0; font-weight: 500; text-decoration: none; transition: background 0.2s;">
<button onclick="openOAuthPopup('/auth/oauth2/google')" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #1a1a2e; color: #e0e0e0; font-weight: 500; text-decoration: none; transition: background 0.2s; cursor: pointer; width: 100%;">
<svg viewBox="0 0 24 24" width="20" height="20">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
......@@ -95,16 +95,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
</svg>
Sign up with Google
</a>
</button>
{% endif %}
{% if config.oauth2.github and config.oauth2.github.enabled %}
<a href="/auth/oauth2/github" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #24292e; color: #ffffff; font-weight: 500; text-decoration: none; transition: background 0.2s;">
<button onclick="openOAuthPopup('/auth/oauth2/github')" style="display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px; border: 1px solid #0f3460; border-radius: 4px; background: #24292e; color: #ffffff; font-weight: 500; text-decoration: none; transition: background 0.2s; cursor: pointer; width: 100%;">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
Sign up with GitHub
</a>
</button>
{% endif %}
</div>
{% endif %}
......@@ -150,5 +150,58 @@ document.querySelector('form')?.addEventListener('submit', function(e) {
return true;
});
// OAuth2 Popup Handling
function openOAuthPopup(url) {
// Add popup parameter to URL
const separator = url.includes('?') ? '&' : '?';
const popupUrl = url + separator + 'popup=1';
// Calculate popup position (center screen)
const width = 500;
const height = 650;
const left = (window.innerWidth / 2) - (width / 2);
const top = (window.innerHeight / 2) - (height / 2);
// Open popup window
const popup = window.open(
popupUrl,
'OAuth2 Authentication',
`width=${width},height=${height},top=${top},left=${left},resizable=no,scrollbars=yes,status=no`
);
// Listen for popup messages
const messageListener = function(event) {
if (event.data.type === 'oauth2_complete') {
// Close popup
popup.close();
// Remove listener
window.removeEventListener('message', messageListener);
// Redirect to dashboard
window.location.href = event.data.redirect_url || '/dashboard';
} else if (event.data.type === 'oauth2_error') {
// Close popup
popup.close();
// Remove listener
window.removeEventListener('message', messageListener);
// Show error
alert(event.data.error || 'Authentication failed');
}
};
window.addEventListener('message', messageListener);
// Fallback if popup is blocked or closed manually
const popupCheck = setInterval(function() {
if (popup.closed) {
clearInterval(popupCheck);
window.removeEventListener('message', messageListener);
}
}, 500);
}
</script>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment