feat: add Atlassian OAuth support for Jira and Confluence

- Introduced a shared schema for Atlassian OAuth 2.0 credentials, accommodating both Jira and Confluence.
- Updated Jira connector routes to utilize the new AtlassianAuthCredentialsBase for handling OAuth tokens.
- Enhanced configuration to include new environment variables for Jira OAuth integration.
- Refactored token handling in Jira indexing logic to support the new shared credential structure.
This commit is contained in:
Anish Sarkar 2026-01-06 01:27:29 +05:30
parent 982b9ceb76
commit bf8c3bfcf7
4 changed files with 27 additions and 7 deletions

View file

@ -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")

View file

@ -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()

View file

@ -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

View file

@ -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,