Compare commits
3 Commits
1af2e37a7f
...
renovate/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6fbb4909e | ||
| d8fec0e5d7 | |||
| c9b09b8250 |
@@ -1,6 +1,10 @@
|
|||||||
from fastapi import APIRouter
|
import urllib.request
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Request
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
from app.core.auth import _build_jwks_url, _extract_token, _get_jwks_client
|
||||||
|
from app.core.config import settings
|
||||||
from app.core.database import async_session
|
from app.core.database import async_session
|
||||||
|
|
||||||
router = APIRouter(tags=["health"])
|
router = APIRouter(tags=["health"])
|
||||||
@@ -23,3 +27,45 @@ async def health_check():
|
|||||||
async def root():
|
async def root():
|
||||||
"""Root endpoint."""
|
"""Root endpoint."""
|
||||||
return {"message": "Nuzlocke Tracker API", "docs": "/docs"}
|
return {"message": "Nuzlocke Tracker API", "docs": "/docs"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/auth-debug")
|
||||||
|
async def auth_debug(request: Request):
|
||||||
|
"""Temporary diagnostic endpoint for auth debugging."""
|
||||||
|
result: dict = {}
|
||||||
|
|
||||||
|
# Config
|
||||||
|
result["supabase_url"] = settings.supabase_url
|
||||||
|
result["has_jwt_secret"] = bool(settings.supabase_jwt_secret)
|
||||||
|
result["jwks_url"] = (
|
||||||
|
_build_jwks_url(settings.supabase_url) if settings.supabase_url else None
|
||||||
|
)
|
||||||
|
|
||||||
|
# JWKS fetch
|
||||||
|
jwks_url = result["jwks_url"]
|
||||||
|
if jwks_url:
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(jwks_url, timeout=5) as resp:
|
||||||
|
result["jwks_status"] = resp.status
|
||||||
|
result["jwks_body"] = resp.read().decode()
|
||||||
|
except Exception as e:
|
||||||
|
result["jwks_fetch_error"] = str(e)
|
||||||
|
|
||||||
|
# JWKS client
|
||||||
|
client = _get_jwks_client()
|
||||||
|
result["jwks_client_exists"] = client is not None
|
||||||
|
|
||||||
|
# Token info (header only, no secrets)
|
||||||
|
token = _extract_token(request)
|
||||||
|
if token:
|
||||||
|
import jwt
|
||||||
|
|
||||||
|
try:
|
||||||
|
header = jwt.get_unverified_header(token)
|
||||||
|
result["token_header"] = header
|
||||||
|
except Exception as e:
|
||||||
|
result["token_header_error"] = str(e)
|
||||||
|
else:
|
||||||
|
result["token"] = "not provided"
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -26,11 +26,21 @@ class AuthUser:
|
|||||||
role: str | None = None
|
role: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def _build_jwks_url(base_url: str) -> str:
|
||||||
|
"""Build the JWKS URL, adding /auth/v1 prefix for Supabase Cloud."""
|
||||||
|
base = base_url.rstrip("/")
|
||||||
|
if "/auth/v1" in base:
|
||||||
|
return f"{base}/.well-known/jwks.json"
|
||||||
|
# Supabase Cloud URLs need the /auth/v1 prefix;
|
||||||
|
# local GoTrue serves JWKS at root but uses HS256 fallback anyway.
|
||||||
|
return f"{base}/auth/v1/.well-known/jwks.json"
|
||||||
|
|
||||||
|
|
||||||
def _get_jwks_client() -> PyJWKClient | None:
|
def _get_jwks_client() -> PyJWKClient | None:
|
||||||
"""Get or create a cached JWKS client."""
|
"""Get or create a cached JWKS client."""
|
||||||
global _jwks_client
|
global _jwks_client
|
||||||
if _jwks_client is None and settings.supabase_url:
|
if _jwks_client is None and settings.supabase_url:
|
||||||
jwks_url = f"{settings.supabase_url.rstrip('/')}/.well-known/jwks.json"
|
jwks_url = _build_jwks_url(settings.supabase_url)
|
||||||
_jwks_client = PyJWKClient(jwks_url, cache_jwk_set=True, lifespan=300)
|
_jwks_client = PyJWKClient(jwks_url, cache_jwk_set=True, lifespan=300)
|
||||||
return _jwks_client
|
return _jwks_client
|
||||||
|
|
||||||
@@ -80,7 +90,7 @@ def _verify_jwt(token: str) -> dict | None:
|
|||||||
except PyJWKSetError as e:
|
except PyJWKSetError as e:
|
||||||
logger.warning("JWKS set error: %s", e)
|
logger.warning("JWKS set error: %s", e)
|
||||||
else:
|
else:
|
||||||
logger.debug("No JWKS client available (SUPABASE_URL not set?)")
|
logger.warning("No JWKS client available (SUPABASE_URL not set?)")
|
||||||
return _verify_jwt_hs256(token)
|
return _verify_jwt_hs256(token)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
16
frontend/package-lock.json
generated
16
frontend/package-lock.json
generated
@@ -22,7 +22,7 @@
|
|||||||
"sonner": "2.0.7"
|
"sonner": "2.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@axe-core/playwright": "4.11.1",
|
"@axe-core/playwright": "4.11.3",
|
||||||
"@playwright/test": "1.58.2",
|
"@playwright/test": "1.58.2",
|
||||||
"@tailwindcss/vite": "4.2.2",
|
"@tailwindcss/vite": "4.2.2",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
@@ -73,13 +73,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@axe-core/playwright": {
|
"node_modules/@axe-core/playwright": {
|
||||||
"version": "4.11.1",
|
"version": "4.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.3.tgz",
|
||||||
"integrity": "sha512-mKEfoUIB1MkVTht0BGZFXtSAEKXMJoDkyV5YZ9jbBmZCcWDz71tegNsdTkIN8zc/yMi5Gm2kx7Z5YQ9PfWNAWw==",
|
"integrity": "sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axe-core": "~4.11.1"
|
"axe-core": "~4.11.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"playwright-core": ">= 1.0.0"
|
"playwright-core": ">= 1.0.0"
|
||||||
@@ -2257,9 +2257,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axe-core": {
|
"node_modules/axe-core": {
|
||||||
"version": "4.11.1",
|
"version": "4.11.4",
|
||||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz",
|
||||||
"integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==",
|
"integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"sonner": "2.0.7"
|
"sonner": "2.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@axe-core/playwright": "4.11.1",
|
"@axe-core/playwright": "4.11.3",
|
||||||
"@playwright/test": "1.58.2",
|
"@playwright/test": "1.58.2",
|
||||||
"@tailwindcss/vite": "4.2.2",
|
"@tailwindcss/vite": "4.2.2",
|
||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user