diff --git a/surfsense_backend/app/config/__init__.py b/surfsense_backend/app/config/__init__.py index f65a94cc0..4abdf915a 100644 --- a/surfsense_backend/app/config/__init__.py +++ b/surfsense_backend/app/config/__init__.py @@ -95,6 +95,11 @@ class Config: NOTION_CLIENT_SECRET = os.getenv("NOTION_CLIENT_SECRET") NOTION_REDIRECT_URI = os.getenv("NOTION_REDIRECT_URI") + # Jira OAuth + JIRA_CLIENT_ID = os.getenv("JIRA_CLIENT_ID") + JIRA_CLIENT_SECRET = os.getenv("JIRA_CLIENT_SECRET") + JIRA_REDIRECT_URI = os.getenv("JIRA_REDIRECT_URI") + # Linear OAuth LINEAR_CLIENT_ID = os.getenv("LINEAR_CLIENT_ID") LINEAR_CLIENT_SECRET = os.getenv("LINEAR_CLIENT_SECRET") diff --git a/surfsense_backend/app/routes/jira_add_connector_route.py b/surfsense_backend/app/routes/jira_add_connector_route.py index 5b752912a..302a118db 100644 --- a/surfsense_backend/app/routes/jira_add_connector_route.py +++ b/surfsense_backend/app/routes/jira_add_connector_route.py @@ -24,7 +24,7 @@ from app.db import ( User, get_async_session, ) -from app.schemas.jira_auth_credentials import JiraAuthCredentialsBase +from app.schemas.atlassian_auth_credentials import AtlassianAuthCredentialsBase from app.users import current_active_user from app.utils.oauth_security import OAuthStateManager, TokenEncryption @@ -392,7 +392,7 @@ async def refresh_jira_token( try: logger.info(f"Refreshing Jira token for connector {connector.id}") - credentials = JiraAuthCredentialsBase.from_dict(connector.config) + credentials = AtlassianAuthCredentialsBase.from_dict(connector.config) # Decrypt tokens if they are encrypted token_encryption = get_token_encryption() diff --git a/surfsense_backend/app/schemas/jira_auth_credentials.py b/surfsense_backend/app/schemas/atlassian_auth_credentials.py similarity index 81% rename from surfsense_backend/app/schemas/jira_auth_credentials.py rename to surfsense_backend/app/schemas/atlassian_auth_credentials.py index 0e1cfdee2..3290e5d67 100644 --- a/surfsense_backend/app/schemas/jira_auth_credentials.py +++ b/surfsense_backend/app/schemas/atlassian_auth_credentials.py @@ -1,9 +1,23 @@ +""" +Atlassian OAuth 2.0 Authentication Credentials Schema. + +Shared schema for both Jira and Confluence OAuth credentials. +Both products use the same Atlassian OAuth 2.0 (3LO) flow and token structure. +""" + from datetime import UTC, datetime from pydantic import BaseModel, field_validator -class JiraAuthCredentialsBase(BaseModel): +class AtlassianAuthCredentialsBase(BaseModel): + """ + Base model for Atlassian OAuth 2.0 credentials. + + Used for both Jira and Confluence connectors since they share + the same Atlassian OAuth infrastructure and token structure. + """ + access_token: str refresh_token: str | None = None token_type: str = "Bearer" @@ -39,7 +53,7 @@ class JiraAuthCredentialsBase(BaseModel): } @classmethod - def from_dict(cls, data: dict) -> "JiraAuthCredentialsBase": + def from_dict(cls, data: dict) -> "AtlassianAuthCredentialsBase": """Create credentials from dictionary.""" expires_at = None if data.get("expires_at"): @@ -70,3 +84,4 @@ class JiraAuthCredentialsBase(BaseModel): if isinstance(v, datetime): return v if v.tzinfo else v.replace(tzinfo=UTC) return v + diff --git a/surfsense_backend/app/tasks/connector_indexers/jira_indexer.py b/surfsense_backend/app/tasks/connector_indexers/jira_indexer.py index 0bb54aea6..cd7dabeaf 100644 --- a/surfsense_backend/app/tasks/connector_indexers/jira_indexer.py +++ b/surfsense_backend/app/tasks/connector_indexers/jira_indexer.py @@ -131,8 +131,8 @@ async def index_jira_issues( return 0, f"Failed to decrypt Jira tokens: {e!s}" try: - from app.schemas.jira_auth_credentials import JiraAuthCredentialsBase - credentials = JiraAuthCredentialsBase.from_dict(config_data) + from app.schemas.atlassian_auth_credentials import AtlassianAuthCredentialsBase + credentials = AtlassianAuthCredentialsBase.from_dict(config_data) except Exception as e: await task_logger.log_task_failure( log_entry, @@ -160,7 +160,7 @@ async def index_jira_issues( config_data["access_token"] = token_encryption.decrypt_token( config_data["access_token"] ) - credentials = JiraAuthCredentialsBase.from_dict(config_data) + credentials = AtlassianAuthCredentialsBase.from_dict(config_data) except Exception as e: await task_logger.log_task_failure( log_entry,