feat: make authentication optional — no tokens = no auth
Lint & Test / test (push) Successful in 10s

When no api_tokens are configured (the new default), all endpoints
are accessible without authentication. The frontend detects this
via /api/health's auth_required field and skips the login form.

- Backend: auth.py skips verification when api_tokens is empty
- Frontend: shared getAuthHeaders()/hasCredentials() helpers replace
  scattered token logic across all JS modules
- Health endpoint exposes auth_required for frontend discovery
- config.example.yaml ships with tokens commented out
- CLI --show-token and startup log reflect disabled state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 13:59:55 +03:00
parent f80f6e9299
commit 4d1bb78c83
14 changed files with 175 additions and 190 deletions
+32 -22
View File
@@ -59,9 +59,12 @@ async def lifespan(app: FastAPI):
logger = logging.getLogger(__name__)
logger.info(f"Media Server starting on {settings.host}:{settings.port}")
# Log all configured tokens
for label, token in settings.api_tokens.items():
logger.info(f"API Token [{label}]: {token[:8]}...")
# Log authentication status
if settings.api_tokens:
for label, token in settings.api_tokens.items():
logger.info(f"API Token [{label}]: {token[:8]}...")
else:
logger.warning("No API tokens configured — authentication is DISABLED")
# Start WebSocket status monitor
controller = get_media_controller()
@@ -129,24 +132,28 @@ def create_app() -> FastAPI:
@app.middleware("http")
async def token_logging_middleware(request: Request, call_next):
"""Extract token label and set in context for logging."""
token_label = "unknown"
if not settings.api_tokens:
token_label_var.set("anonymous")
else:
token_label = "unknown"
# Try Authorization header
auth_header = request.headers.get("authorization", "")
if auth_header.startswith("Bearer "):
token = auth_header[7:]
label = get_token_label(token)
if label:
token_label = label
# Try Authorization header
auth_header = request.headers.get("authorization", "")
if auth_header.startswith("Bearer "):
token = auth_header[7:]
label = get_token_label(token)
if label:
token_label = label
# Try query parameter (for artwork endpoint)
elif "token" in request.query_params:
token = request.query_params["token"]
label = get_token_label(token)
if label:
token_label = label
# Try query parameter (for artwork endpoint)
elif "token" in request.query_params:
token = request.query_params["token"]
label = get_token_label(token)
if label:
token_label = label
token_label_var.set(token_label)
token_label_var.set(token_label)
response = await call_next(request)
return response
@@ -215,14 +222,17 @@ def main():
if args.generate_config:
config_path = generate_default_config()
print(f"Configuration file generated at: {config_path}")
print("API Token has been saved to the config file.")
print("Authentication is disabled by default. Add api_tokens to enable it.")
return
if args.show_token:
print(f"Config directory: {get_config_dir()}")
print("\nAPI Tokens:")
for label, token in settings.api_tokens.items():
print(f" {label:20} {token}")
if settings.api_tokens:
print("\nAPI Tokens:")
for label, token in settings.api_tokens.items():
print(f" {label:20} {token}")
else:
print("\nAuthentication is DISABLED (no tokens configured)")
return
uvicorn.run(