diff --git a/backend/services/workspace/app/core/config.py b/backend/services/workspace/app/core/config.py index 2f87d3f..8d55dbd 100644 --- a/backend/services/workspace/app/core/config.py +++ b/backend/services/workspace/app/core/config.py @@ -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()] diff --git a/backend/services/workspace/app/core/security.py b/backend/services/workspace/app/core/security.py index e9da78c..daf52cc 100644 --- a/backend/services/workspace/app/core/security.py +++ b/backend/services/workspace/app/core/security.py @@ -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) diff --git a/docker-compose.yml b/docker-compose.yml index 1c47186..a07de84 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: