Split monorepo into separate units for future independent repositories: - media-server/: Standalone FastAPI server with own README, requirements, config example, and CLAUDE.md - haos-integration/: HACS-ready Home Assistant integration with hacs.json, own README, and CLAUDE.md Both components now have their own .gitignore files and can be easily extracted into separate repositories. Also adds custom icon support for scripts configuration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
112 lines
3.2 KiB
Python
112 lines
3.2 KiB
Python
"""Authentication middleware and utilities."""
|
|
|
|
from typing import Optional
|
|
|
|
from fastapi import Depends, HTTPException, Query, Request, status
|
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
|
|
from .config import settings
|
|
|
|
security = HTTPBearer(auto_error=False)
|
|
|
|
|
|
async def verify_token(
|
|
request: Request,
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
) -> str:
|
|
"""Verify the API token from the Authorization header.
|
|
|
|
Args:
|
|
request: The incoming request
|
|
credentials: The bearer token credentials
|
|
|
|
Returns:
|
|
The validated token
|
|
|
|
Raises:
|
|
HTTPException: If the token is missing or invalid
|
|
"""
|
|
if credentials is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Missing authentication token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
if credentials.credentials != settings.api_token:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid authentication token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
return credentials.credentials
|
|
|
|
|
|
class TokenAuth:
|
|
"""Dependency class for token authentication."""
|
|
|
|
def __init__(self, auto_error: bool = True):
|
|
self.auto_error = auto_error
|
|
|
|
async def __call__(
|
|
self,
|
|
request: Request,
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
) -> str | None:
|
|
"""Verify the token and return it or raise an exception."""
|
|
if credentials is None:
|
|
if self.auto_error:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Missing authentication token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
return None
|
|
|
|
if credentials.credentials != settings.api_token:
|
|
if self.auto_error:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid authentication token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
return None
|
|
|
|
return credentials.credentials
|
|
|
|
|
|
async def verify_token_or_query(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
token: Optional[str] = Query(None, description="API token as query parameter"),
|
|
) -> str:
|
|
"""Verify the API token from header or query parameter.
|
|
|
|
Useful for endpoints that need to be accessed via URL (like images).
|
|
|
|
Args:
|
|
credentials: The bearer token credentials from header
|
|
token: Token from query parameter
|
|
|
|
Returns:
|
|
The validated token
|
|
|
|
Raises:
|
|
HTTPException: If the token is missing or invalid
|
|
"""
|
|
# Try header first
|
|
if credentials is not None:
|
|
if credentials.credentials == settings.api_token:
|
|
return credentials.credentials
|
|
|
|
# Try query parameter
|
|
if token is not None:
|
|
if token == settings.api_token:
|
|
return token
|
|
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Missing or invalid authentication token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|