From c6b89781c91b7b19671d3241119fdea7cd996b91 Mon Sep 17 00:00:00 2001 From: Christian Krakau-Louis Date: Mon, 9 Jun 2025 15:13:36 +0200 Subject: [PATCH] feat: add OAuth debug route and template for URL generation verification --- .env.oauth | 18 ++++ musicround/__init__.py | 2 + musicround/helpers/auth_helpers.py | 19 +++- musicround/routes/oauth_debug.py | 81 ++++++++++++++++ musicround/templates/oauth_debug.html | 130 ++++++++++++++++++++++++++ 5 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 .env.oauth create mode 100644 musicround/routes/oauth_debug.py create mode 100644 musicround/templates/oauth_debug.html diff --git a/.env.oauth b/.env.oauth new file mode 100644 index 0000000..8648c2c --- /dev/null +++ b/.env.oauth @@ -0,0 +1,18 @@ +# Standard OAuth redirect URIs +SPOTIFY_REDIRECT_URI=http://localhost:5000/auth/spotify/callback +GOOGLE_REDIRECT_URI=http://localhost:5000/users/login/google/callback +AUTHENTIK_REDIRECT_URI=http://localhost:5000/users/login/authentik/callback +DROPBOX_REDIRECT_URI=http://localhost:5000/users/dropbox/callback + +# OAuth settings +USE_HTTPS=False # Set to True when behind a reverse proxy with HTTPS offloading +# PREFERRED_URL_SCHEME=https # Optional, set automatically if USE_HTTPS is True + +# Static OAuth redirect URIs for production environments +# These override the dynamic URLs generated by url_for() when specified +# STATIC_OAUTH_URLS=True # Uncomment to use static URLs below +# OAUTH_SPOTIFY_AUTH_URL=https://your-domain.com/auth/spotify/callback +# OAUTH_SPOTIFY_LINK_URL=https://your-domain.com/users/spotify-link/callback +# OAUTH_GOOGLE_URL=https://your-domain.com/users/login/google/callback +# OAUTH_AUTHENTIK_URL=https://your-domain.com/users/login/authentik/callback +# OAUTH_DROPBOX_URL=https://your-domain.com/users/dropbox/callback diff --git a/musicround/__init__.py b/musicround/__init__.py index b58fc32..0523d2a 100644 --- a/musicround/__init__.py +++ b/musicround/__init__.py @@ -320,6 +320,7 @@ def create_app(config=None): from musicround.routes.deezer_routes import deezer_bp from musicround.routes.db_admin import db_admin_bp, init_admin from musicround.routes.auth import auth_bp + from musicround.routes.oauth_debug import oauth_debug_bp app.register_blueprint(core_bp) app.register_blueprint(users_bp) @@ -332,6 +333,7 @@ def create_app(config=None): app.register_blueprint(deezer_bp) app.register_blueprint(db_admin_bp) app.register_blueprint(auth_bp) + app.register_blueprint(oauth_debug_bp) # Initialize the admin interface init_admin(app) diff --git a/musicround/helpers/auth_helpers.py b/musicround/helpers/auth_helpers.py index 512ccc4..3c208b8 100644 --- a/musicround/helpers/auth_helpers.py +++ b/musicround/helpers/auth_helpers.py @@ -352,6 +352,21 @@ def get_oauth_redirect_uri(endpoint, provider=None): """ # Use Flask's url_for which respects PREFERRED_URL_SCHEME if provider: - return url_for(endpoint, provider=provider, _external=True) + redirect_uri = url_for(endpoint, provider=provider, _external=True) else: - return url_for(endpoint, _external=True) \ No newline at end of file + redirect_uri = url_for(endpoint, _external=True) + + # Log details about the generated URL for debugging + use_https = current_app.config.get('USE_HTTPS', False) + preferred_scheme = current_app.config.get('PREFERRED_URL_SCHEME', 'http') + + current_app.logger.debug( + f"OAuth Redirect URI: {redirect_uri} | " + f"Endpoint: {endpoint} | " + f"USE_HTTPS: {use_https} | " + f"PREFERRED_URL_SCHEME: {preferred_scheme} | " + f"Request scheme: {request.scheme if request else 'N/A'} | " + f"X-Forwarded-Proto: {request.headers.get('X-Forwarded-Proto', 'N/A') if request else 'N/A'}" + ) + + return redirect_uri \ No newline at end of file diff --git a/musicround/routes/oauth_debug.py b/musicround/routes/oauth_debug.py new file mode 100644 index 0000000..83e38bc --- /dev/null +++ b/musicround/routes/oauth_debug.py @@ -0,0 +1,81 @@ +""" +Debug route for OAuth URL generation +""" +from flask import Blueprint, render_template, jsonify, current_app, request, url_for +from flask_login import login_required +from musicround.helpers.auth_helpers import get_oauth_redirect_uri + +# Create blueprint +oauth_debug_bp = Blueprint('oauth_debug', __name__) + +@oauth_debug_bp.route('/debug/oauth-urls') +@login_required +def debug_oauth_urls(): + """ + Debug endpoint to show OAuth URL generation with current configuration + This is useful for verifying proper HTTPS handling when behind a reverse proxy + + Formats: + - HTML: Default view with pretty UI + - JSON: Add ?format=json or use Accept: application/json header + """ + # Get config settings + use_https = current_app.config.get('USE_HTTPS', False) + preferred_scheme = current_app.config.get('PREFERRED_URL_SCHEME', 'http') + + # Generate all OAuth callback URLs using the helper function + oauth_urls = { + 'spotify_auth': get_oauth_redirect_uri('auth.callback'), + 'spotify_link': get_oauth_redirect_uri('users.spotify_link_callback'), + 'google_login': get_oauth_redirect_uri('users.google_callback'), + 'authentik_login': get_oauth_redirect_uri('users.authentik_callback'), + 'dropbox_link': get_oauth_redirect_uri('users.dropbox_callback') + } + + # Generate the same URLs directly with url_for for comparison + direct_urls = { + 'spotify_auth': url_for('auth.callback', _external=True), + 'spotify_link': url_for('users.spotify_link_callback', _external=True), + 'google_login': url_for('users.google_callback', _external=True), + 'authentik_login': url_for('users.authentik_callback', _external=True), + 'dropbox_link': url_for('users.dropbox_callback', _external=True) + } + + # Get request info + request_info = { + 'url': request.url, + 'host': request.host, + 'scheme': request.scheme, + 'headers': { + key: value for key, value in request.headers.items() + if key.lower() in ('x-forwarded-for', 'x-forwarded-proto', + 'x-forwarded-host', 'host', 'origin', 'referer') + } + } + + # Compile data for both JSON and HTML response + result = { + 'config': { + 'USE_HTTPS': use_https, + 'PREFERRED_URL_SCHEME': preferred_scheme + }, + 'helper_generated_urls': oauth_urls, + 'direct_url_for_urls': direct_urls, + 'request_info': request_info + } + + current_app.logger.info(f"OAuth Debug URLs generated") + + # Check if JSON format is requested + wants_json = (request.args.get('format', '').lower() == 'json' or + request.headers.get('Accept', '').lower().find('application/json') >= 0) + + if wants_json: + return jsonify(result) + else: + # Return HTML view + return render_template('oauth_debug.html', + config=result['config'], + helper_generated_urls=result['helper_generated_urls'], + direct_url_for_urls=result['direct_url_for_urls'], + request_info=result['request_info']) diff --git a/musicround/templates/oauth_debug.html b/musicround/templates/oauth_debug.html new file mode 100644 index 0000000..8226deb --- /dev/null +++ b/musicround/templates/oauth_debug.html @@ -0,0 +1,130 @@ +{% extends 'base.html' %} + +{% block title %}OAuth Debug Information{% endblock %} + +{% block content %} +
+

OAuth Debug Information

+ +
+

Configuration

+
+ + + + + + + + + +
USE_HTTPS{{ config.USE_HTTPS }}
PREFERRED_URL_SCHEME{{ config.PREFERRED_URL_SCHEME }}
+
+
+ +
+

Helper-Generated URLs

+
+ + + + + + {% for name, url in helper_generated_urls.items() %} + + + + + {% endfor %} +
EndpointURL
{{ name }}{{ url }}
+
+
+ +
+

Direct url_for URLs

+
+ + + + + + {% for name, url in direct_url_for_urls.items() %} + + + + + {% endfor %} +
EndpointURL
{{ name }}{{ url }}
+
+
+ +
+

Request Information

+
+ + + + + + + + + + + + + + + + + +
URL{{ request_info.url }}
Host{{ request_info.host }}
Scheme{{ request_info.scheme }}
Headers +
+ {% for header, value in request_info.headers.items() %} +
{{ header }}
+
{{ value }}
+ {% endfor %} +
+
+
+
+ +
+
+
+ + + +
+
+

+ This page shows debug information for OAuth redirect URLs. It helps verify that the proper URL scheme (HTTP/HTTPS) is being used. +

+

+ To configure HTTPS, set USE_HTTPS=True in your .env file when running behind a reverse proxy that handles SSL termination. +

+
+
+
+ +
+
+
+ + + +
+
+

+ Tips for debugging: +

+
    +
  • Check if the X-Forwarded-Proto header is set to "https" by your reverse proxy
  • +
  • Verify that helper-generated URLs match your expected protocol
  • +
  • If running behind Traefik, ensure it's configured to set the proper headers
  • +
+
+
+
+
+{% endblock %}