Fix all 7 launch blockers from security/QA review
SEC-16: Add frontend ports mapping (80:80) to docker-compose.yml UI-01: Add routes for Welcome, SettingsIntegrations, MealPlan in App.jsx SEC-01: Remove traceback leak from auth error responses SEC-02: Fix require_admin to use is_admin column (was undefined ADMIN_USERNAME) SEC-03: Add API_TOKEN auth for MCP connector -> backend communication SEC-04: OAuth state now uses signed JWT tokens (was raw user UUID) OS-01: First registered user gets admin immediately during registration 10 files changed, 97 insertions(+), 25 deletions(-)
This commit is contained in:
parent
50eae17c34
commit
69320e1e82
10 changed files with 97 additions and 25 deletions
|
|
@ -13,7 +13,7 @@ from app.config import get_settings
|
|||
from app.database import get_db
|
||||
|
||||
settings = get_settings()
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login", auto_error=False)
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
|
|
@ -31,7 +31,7 @@ def create_access_token(user_id: str, expires_delta: timedelta | None = None) ->
|
|||
|
||||
|
||||
async def get_current_user(
|
||||
token: Annotated[str, Depends(oauth2_scheme)],
|
||||
token: Annotated[str | None, Depends(oauth2_scheme)],
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
):
|
||||
credentials_exception = HTTPException(
|
||||
|
|
@ -39,6 +39,30 @@ async def get_current_user(
|
|||
detail="Invalid authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
if not token:
|
||||
raise credentials_exception
|
||||
|
||||
from app.models.user import User
|
||||
|
||||
# Check if this is an API token (MCP connector auth)
|
||||
if settings.api_token and token == settings.api_token:
|
||||
# Map to the first admin user
|
||||
result = await db.execute(
|
||||
select(User).where(User.is_admin == True).order_by(User.created_at.asc()).limit(1)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
if user is None:
|
||||
# Fall back to first user if no admin exists yet
|
||||
result = await db.execute(
|
||||
select(User).order_by(User.created_at.asc()).limit(1)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
# Standard JWT validation
|
||||
try:
|
||||
payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])
|
||||
user_id: str = payload.get("sub")
|
||||
|
|
@ -47,7 +71,6 @@ async def get_current_user(
|
|||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
from app.models.user import User
|
||||
result = await db.execute(select(User).where(User.id == uuid.UUID(user_id)))
|
||||
user = result.scalar_one_or_none()
|
||||
if user is None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue