fix(workspace): validate Keycloak JWT for production auth

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Mortezakoohjani 2026-09-12 12:02:03 +03:30
parent a483b47369
commit a846b3e7f1
3 changed files with 36 additions and 6 deletions

View File

@ -22,14 +22,22 @@ class Settings(BaseSettings):
auth_required: bool = True
entitlement_stub: bool = True
keycloak_enabled: bool = True
keycloak_server_url: str = "http://localhost:8080"
keycloak_public_url: str = ""
keycloak_realm: str = "superapp"
keycloak_server_url: str = Field(default="http://localhost:8080", validation_alias="KEYCLOAK_SERVER_URL")
keycloak_public_url: str = Field(default="", validation_alias="KEYCLOAK_PUBLIC_URL")
keycloak_realm: str = Field(default="superapp", validation_alias="KEYCLOAK_REALM")
jwt_algorithm: str = "RS256"
jwt_audience: str = "account"
jwt_verify_signature: bool = True
cors_origins: str = "http://localhost:3000,http://127.0.0.1:3000"
@property
def keycloak_public_base(self) -> str:
return (self.keycloak_public_url or self.keycloak_server_url).rstrip("/")
@property
def keycloak_public_realm_url(self) -> str:
return f"{self.keycloak_public_base}/realms/{self.keycloak_realm}"
@property
def cors_origin_list(self):
return [x.strip() for x in self.cors_origins.split(",") if x.strip()]

View File

@ -1,16 +1,34 @@
from functools import lru_cache
from fastapi import Depends
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from app.core.config import settings
from shared.auth import JWTSettings, JWTValidator
from shared.exceptions import UnauthorizedError
from shared.security import CurrentUser
bearer = HTTPBearer(auto_error=False)
@lru_cache
def get_jwt_validator() -> JWTValidator:
return JWTValidator(
JWTSettings(
keycloak_enabled=settings.keycloak_enabled,
keycloak_server_url=settings.keycloak_server_url,
keycloak_realm=settings.keycloak_realm,
jwt_algorithm=settings.jwt_algorithm,
jwt_audience=settings.jwt_audience,
jwt_verify_signature=settings.jwt_verify_signature,
jwt_issuer=settings.keycloak_public_realm_url,
)
)
async def get_current_user(credentials: HTTPAuthorizationCredentials | None = Depends(bearer)):
if not settings.auth_required:
return CurrentUser(user_id="test-user", username="test", roles=["tenant_admin"])
if not credentials:
raise UnauthorizedError("Authentication required")
return CurrentUser(user_id="token-user", username="token", roles=[])
if credentials is None or not credentials.credentials:
raise UnauthorizedError("توکن احراز هویت ارائه نشده است")
return await get_jwt_validator().validate(credentials.credentials)

View File

@ -460,6 +460,10 @@ services:
WORKSPACE_DATABASE_URL_SYNC: ${WORKSPACE_DATABASE_URL_SYNC:-postgresql+psycopg://superapp:superapp_password@postgres:5432/workspace_db}
CORE_SERVICE_URL: ${CORE_SERVICE_URL:-http://core-service:8000}
AUTH_REQUIRED: ${AUTH_REQUIRED:-true}
KEYCLOAK_SERVER_URL: ${KEYCLOAK_SERVER_URL:-http://keycloak:8080}
KEYCLOAK_PUBLIC_URL: ${KEYCLOAK_PUBLIC_URL:-http://localhost:8080}
KEYCLOAK_REALM: ${KEYCLOAK_REALM:-superapp}
JWT_VERIFY_SIGNATURE: ${JWT_VERIFY_SIGNATURE:-true}
ports:
- "8013:8013"
volumes: