feat: add OAuth debug route and template for URL generation verification
This commit is contained in:
+18
@@ -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
|
||||||
@@ -320,6 +320,7 @@ def create_app(config=None):
|
|||||||
from musicround.routes.deezer_routes import deezer_bp
|
from musicround.routes.deezer_routes import deezer_bp
|
||||||
from musicround.routes.db_admin import db_admin_bp, init_admin
|
from musicround.routes.db_admin import db_admin_bp, init_admin
|
||||||
from musicround.routes.auth import auth_bp
|
from musicround.routes.auth import auth_bp
|
||||||
|
from musicround.routes.oauth_debug import oauth_debug_bp
|
||||||
|
|
||||||
app.register_blueprint(core_bp)
|
app.register_blueprint(core_bp)
|
||||||
app.register_blueprint(users_bp)
|
app.register_blueprint(users_bp)
|
||||||
@@ -332,6 +333,7 @@ def create_app(config=None):
|
|||||||
app.register_blueprint(deezer_bp)
|
app.register_blueprint(deezer_bp)
|
||||||
app.register_blueprint(db_admin_bp)
|
app.register_blueprint(db_admin_bp)
|
||||||
app.register_blueprint(auth_bp)
|
app.register_blueprint(auth_bp)
|
||||||
|
app.register_blueprint(oauth_debug_bp)
|
||||||
|
|
||||||
# Initialize the admin interface
|
# Initialize the admin interface
|
||||||
init_admin(app)
|
init_admin(app)
|
||||||
|
|||||||
@@ -352,6 +352,21 @@ def get_oauth_redirect_uri(endpoint, provider=None):
|
|||||||
"""
|
"""
|
||||||
# Use Flask's url_for which respects PREFERRED_URL_SCHEME
|
# Use Flask's url_for which respects PREFERRED_URL_SCHEME
|
||||||
if provider:
|
if provider:
|
||||||
return url_for(endpoint, provider=provider, _external=True)
|
redirect_uri = url_for(endpoint, provider=provider, _external=True)
|
||||||
else:
|
else:
|
||||||
return url_for(endpoint, _external=True)
|
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
|
||||||
@@ -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'])
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}OAuth Debug Information{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<h1 class="text-3xl font-bold mb-4 text-navy-800">OAuth Debug Information</h1>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Configuration</h2>
|
||||||
|
<div class="bg-white rounded-lg shadow p-4 overflow-x-auto">
|
||||||
|
<table class="min-w-full">
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">USE_HTTPS</td>
|
||||||
|
<td class="py-2 px-4 border-b">{{ config.USE_HTTPS }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">PREFERRED_URL_SCHEME</td>
|
||||||
|
<td class="py-2 px-4 border-b">{{ config.PREFERRED_URL_SCHEME }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Helper-Generated URLs</h2>
|
||||||
|
<div class="bg-white rounded-lg shadow p-4 overflow-x-auto">
|
||||||
|
<table class="min-w-full">
|
||||||
|
<tr>
|
||||||
|
<th class="py-2 px-4 border-b text-left">Endpoint</th>
|
||||||
|
<th class="py-2 px-4 border-b text-left">URL</th>
|
||||||
|
</tr>
|
||||||
|
{% for name, url in helper_generated_urls.items() %}
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">{{ name }}</td>
|
||||||
|
<td class="py-2 px-4 border-b break-all">{{ url }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Direct url_for URLs</h2>
|
||||||
|
<div class="bg-white rounded-lg shadow p-4 overflow-x-auto">
|
||||||
|
<table class="min-w-full">
|
||||||
|
<tr>
|
||||||
|
<th class="py-2 px-4 border-b text-left">Endpoint</th>
|
||||||
|
<th class="py-2 px-4 border-b text-left">URL</th>
|
||||||
|
</tr>
|
||||||
|
{% for name, url in direct_url_for_urls.items() %}
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">{{ name }}</td>
|
||||||
|
<td class="py-2 px-4 border-b break-all">{{ url }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Request Information</h2>
|
||||||
|
<div class="bg-white rounded-lg shadow p-4 overflow-x-auto">
|
||||||
|
<table class="min-w-full">
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">URL</td>
|
||||||
|
<td class="py-2 px-4 border-b break-all">{{ request_info.url }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">Host</td>
|
||||||
|
<td class="py-2 px-4 border-b">{{ request_info.host }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">Scheme</td>
|
||||||
|
<td class="py-2 px-4 border-b">{{ request_info.scheme }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="py-2 px-4 border-b font-medium">Headers</td>
|
||||||
|
<td class="py-2 px-4 border-b">
|
||||||
|
<dl>
|
||||||
|
{% for header, value in request_info.headers.items() %}
|
||||||
|
<dt class="font-medium">{{ header }}</dt>
|
||||||
|
<dd class="pl-4 mb-2">{{ value }}</dd>
|
||||||
|
{% endfor %}
|
||||||
|
</dl>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-6">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<svg class="h-5 w-5 text-yellow-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3">
|
||||||
|
<p class="text-sm text-yellow-700">
|
||||||
|
This page shows debug information for OAuth redirect URLs. It helps verify that the proper URL scheme (HTTP/HTTPS) is being used.
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-yellow-700 mt-2">
|
||||||
|
To configure HTTPS, set <code class="bg-yellow-100 px-1 rounded">USE_HTTPS=True</code> in your <code class="bg-yellow-100 px-1 rounded">.env</code> file when running behind a reverse proxy that handles SSL termination.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-navy-50 border-l-4 border-navy-400 p-4">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<svg class="h-5 w-5 text-navy-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2h-1V9a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3">
|
||||||
|
<p class="text-sm text-navy-700">
|
||||||
|
<strong>Tips for debugging:</strong>
|
||||||
|
</p>
|
||||||
|
<ul class="list-disc pl-5 mt-1 text-sm text-navy-700">
|
||||||
|
<li>Check if the X-Forwarded-Proto header is set to "https" by your reverse proxy</li>
|
||||||
|
<li>Verify that helper-generated URLs match your expected protocol</li>
|
||||||
|
<li>If running behind Traefik, ensure it's configured to set the proper headers</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user