Skip to content

ccproxy.plugins.oauth_claude.storage

ccproxy.plugins.oauth_claude.storage

Token storage for Claude OAuth plugin.

ClaudeOAuthStorage

ClaudeOAuthStorage(storage_path=None)

Bases: BaseJsonStorage[ClaudeCredentials]

Claude OAuth-specific token storage implementation.

Parameters:

Name Type Description Default
storage_path Path | None

Path to storage file

None
Source code in ccproxy/plugins/oauth_claude/storage.py
def __init__(self, storage_path: Path | None = None):
    """Initialize Claude OAuth token storage.

    Args:
        storage_path: Path to storage file
    """
    if storage_path is None:
        # Default to standard Claude credentials location
        storage_path = Path.home() / ".claude" / ".credentials.json"

    super().__init__(storage_path)
    self.provider_name = "claude-api"

save async

save(credentials)

Save Claude credentials.

Parameters:

Name Type Description Default
credentials ClaudeCredentials

Claude credentials to save

required

Returns:

Type Description
bool

True if saved successfully, False otherwise

Source code in ccproxy/plugins/oauth_claude/storage.py
async def save(self, credentials: ClaudeCredentials) -> bool:
    """Save Claude credentials.

    Args:
        credentials: Claude credentials to save

    Returns:
        True if saved successfully, False otherwise
    """
    try:
        # Convert to dict for storage (uses by_alias=True by default)
        data = credentials.model_dump(mode="json", exclude_none=True)

        # Use parent class's atomic write with backup
        await self._write_json(data)

        logger.debug(
            "claude_oauth_credentials_saved",
            has_oauth=bool(credentials.claude_ai_oauth),
            storage_path=str(self.file_path),
            category="auth",
        )
        return True
    except Exception as e:
        logger.error(
            "claude_oauth_save_failed", error=str(e), exc_info=e, category="auth"
        )
        return False

load async

load()

Load Claude credentials from file or system keychain.

Claude Code stores credentials in the system keychain and intentionally deletes the plain text file for security. This method tries file first, then falls back to the system keychain (macOS Keychain, Windows Credential Manager, or Linux Secret Service).

Returns:

Type Description
ClaudeCredentials | None

Stored credentials or None

Source code in ccproxy/plugins/oauth_claude/storage.py
async def load(self) -> ClaudeCredentials | None:
    """Load Claude credentials from file or system keychain.

    Claude Code stores credentials in the system keychain and intentionally
    deletes the plain text file for security. This method tries file first,
    then falls back to the system keychain (macOS Keychain, Windows Credential
    Manager, or Linux Secret Service).

    Returns:
        Stored credentials or None
    """
    try:
        # Try file first (works on all platforms, manual setups)
        data = await self._read_json()
        if data:
            credentials = ClaudeCredentials.model_validate(data)
            logger.debug(
                "claude_oauth_credentials_loaded",
                has_oauth=bool(credentials.claude_ai_oauth),
                source="file",
                category="auth",
            )
            return credentials

        # Fallback to system keychain (where Claude Code stores credentials)
        keychain_data = await _read_from_keychain()
        if keychain_data:
            credentials = ClaudeCredentials.model_validate(keychain_data)
            logger.debug(
                "claude_oauth_credentials_loaded",
                has_oauth=bool(credentials.claude_ai_oauth),
                source="keychain",
                category="auth",
            )
            return credentials

        logger.debug(
            "claude_oauth_credentials_not_found",
            checked_file=str(self.file_path),
            checked_keychain=_is_keyring_available(),
            category="auth",
        )
        return None
    except Exception as e:
        logger.error(
            "claude_oauth_credentials_load_error",
            error=str(e),
            exc_info=e,
            category="auth",
        )
        return None

ClaudeProfileStorage

ClaudeProfileStorage(storage_path=None)

Claude profile storage implementation for .account.json.

Parameters:

Name Type Description Default
storage_path Path | None

Path to storage file

None
Source code in ccproxy/plugins/oauth_claude/storage.py
def __init__(self, storage_path: Path | None = None):
    """Initialize Claude profile storage.

    Args:
        storage_path: Path to storage file
    """
    if storage_path is None:
        # Default to standard Claude account location
        storage_path = Path.home() / ".claude" / ".account.json"

    self.file_path = storage_path

save_profile async

save_profile(profile_data)

Save Claude profile data.

Parameters:

Name Type Description Default
profile_data dict[str, Any]

Raw profile data from API

required

Returns:

Type Description
bool

True if saved successfully, False otherwise

Source code in ccproxy/plugins/oauth_claude/storage.py
async def save_profile(self, profile_data: dict[str, Any]) -> bool:
    """Save Claude profile data.

    Args:
        profile_data: Raw profile data from API

    Returns:
        True if saved successfully, False otherwise
    """
    try:
        # Write the raw profile data
        await self._write_json(profile_data)

        # Extract key info for logging
        account = profile_data.get("account", {})
        logger.info(
            "claude_profile_saved",
            account_id=account.get("uuid"),
            email=account.get("email"),
            has_claude_pro=account.get("has_claude_pro"),
            has_claude_max=account.get("has_claude_max"),
            storage_path=str(self.file_path),
            category="auth",
        )
        return True
    except Exception as e:
        logger.error(
            "claude_profile_save_failed",
            error=str(e),
            exc_info=e,
            category="auth",
        )
        return False

load_profile async

load_profile()

Load Claude profile.

Returns:

Type Description
ClaudeProfileInfo | None

ClaudeProfileInfo or None if not found

Source code in ccproxy/plugins/oauth_claude/storage.py
async def load_profile(self) -> ClaudeProfileInfo | None:
    """Load Claude profile.

    Returns:
        ClaudeProfileInfo or None if not found
    """
    try:
        data = await self._read_json()
        if not data:
            return None

        profile = ClaudeProfileInfo.from_api_response(data)
        logger.debug(
            "claude_profile_loaded",
            account_id=profile.account_id,
            email=profile.email,
            category="auth",
        )
        return profile
    except Exception as e:
        logger.error(
            "claude_profile_load_error",
            error=str(e),
            exc_info=e,
            category="auth",
        )
        return None