Skip to content

Auth Module

The auth module provides credential management and configuration.

Explore on DeepWiki

🤖 Configuration Reference →

Ask questions about credential management, ConfigManager, or account configuration.

Overview

from mixpanel_data.auth import ConfigManager, Credentials, AccountInfo

# Manage accounts
config = ConfigManager()
config.add_account("production", username="...", secret="...", project_id="...", region="us")
accounts = config.list_accounts()

# Resolve credentials
creds = config.resolve_credentials(account="production")

ConfigManager

Manages accounts stored in the TOML config file (~/.mp/config.toml).

mixpanel_data.auth.ConfigManager

ConfigManager(config_path: Path | None = None)

Manages Mixpanel project credentials and configuration.

Handles: - Adding, removing, and listing project accounts - Setting the default account - Resolving credentials from environment variables or config file

Config file location (in priority order): 1. Explicit config_path parameter 2. MP_CONFIG_PATH environment variable 3. Default: ~/.mp/config.toml

Initialize ConfigManager.

PARAMETER DESCRIPTION
config_path

Override config file location. Default: ~/.mp/config.toml

TYPE: Path | None DEFAULT: None

Source code in src/mixpanel_data/_internal/config.py
def __init__(self, config_path: Path | None = None) -> None:
    """Initialize ConfigManager.

    Args:
        config_path: Override config file location.
                     Default: ~/.mp/config.toml
    """
    if config_path is not None:
        self._config_path = config_path
    elif "MP_CONFIG_PATH" in os.environ:
        self._config_path = Path(os.environ["MP_CONFIG_PATH"])
    else:
        self._config_path = self.DEFAULT_CONFIG_PATH

config_path property

config_path: Path

Return the config file path.

resolve_credentials

resolve_credentials(account: str | None = None) -> Credentials

Resolve credentials using priority order.

Resolution order: 1. Environment variables (MP_USERNAME, MP_SECRET, MP_PROJECT_ID, MP_REGION) 2. Named account from config file (if account parameter provided) 3. Default account from config file

PARAMETER DESCRIPTION
account

Optional account name to use instead of default.

TYPE: str | None DEFAULT: None

RETURNS DESCRIPTION
Credentials

Immutable Credentials object.

RAISES DESCRIPTION
ConfigError

If no credentials can be resolved.

AccountNotFoundError

If named account doesn't exist.

Source code in src/mixpanel_data/_internal/config.py
def resolve_credentials(self, account: str | None = None) -> Credentials:
    """Resolve credentials using priority order.

    Resolution order:
    1. Environment variables (MP_USERNAME, MP_SECRET, MP_PROJECT_ID, MP_REGION)
    2. Named account from config file (if account parameter provided)
    3. Default account from config file

    Args:
        account: Optional account name to use instead of default.

    Returns:
        Immutable Credentials object.

    Raises:
        ConfigError: If no credentials can be resolved.
        AccountNotFoundError: If named account doesn't exist.
    """
    # Priority 1: Environment variables
    env_creds = self._resolve_from_env()
    if env_creds is not None:
        return env_creds

    # Priority 2 & 3: Config file (named account or default)
    config = self._read_config()
    accounts = config.get("accounts", {})

    if not accounts:
        raise ConfigError(
            "No credentials configured. "
            "Set MP_USERNAME, MP_SECRET, MP_PROJECT_ID, MP_REGION environment variables, "
            "or add an account with add_account()."
        )

    # Determine which account to use
    account_name: str
    if account is not None:
        account_name = account
    else:
        default_account = config.get("default")
        if default_account is not None and isinstance(default_account, str):
            account_name = default_account
        else:
            # Use the first account if no default set
            account_name = next(iter(accounts.keys()))

    if account_name not in accounts:
        raise AccountNotFoundError(
            account_name,
            available_accounts=list(accounts.keys()),
        )

    account_data = accounts[account_name]
    return Credentials(
        username=account_data["username"],
        secret=SecretStr(account_data["secret"]),
        project_id=account_data["project_id"],
        region=account_data["region"],
    )

list_accounts

list_accounts() -> list[AccountInfo]

List all configured accounts.

RETURNS DESCRIPTION
list[AccountInfo]

List of AccountInfo objects (secrets not included).

Source code in src/mixpanel_data/_internal/config.py
def list_accounts(self) -> list[AccountInfo]:
    """List all configured accounts.

    Returns:
        List of AccountInfo objects (secrets not included).
    """
    config = self._read_config()
    accounts = config.get("accounts", {})
    default_name = config.get("default")

    result: list[AccountInfo] = []
    for name, data in accounts.items():
        result.append(
            AccountInfo(
                name=name,
                username=data.get("username", ""),
                project_id=data.get("project_id", ""),
                region=data.get("region", ""),
                is_default=(name == default_name),
            )
        )

    return result

add_account

add_account(
    name: str, username: str, secret: str, project_id: str, region: str
) -> None

Add a new account configuration.

PARAMETER DESCRIPTION
name

Display name for the account.

TYPE: str

username

Service account username.

TYPE: str

secret

Service account secret.

TYPE: str

project_id

Mixpanel project ID.

TYPE: str

region

Data residency region (us, eu, in).

TYPE: str

RAISES DESCRIPTION
AccountExistsError

If account name already exists.

ValueError

If region is invalid.

Source code in src/mixpanel_data/_internal/config.py
def add_account(
    self,
    name: str,
    username: str,
    secret: str,
    project_id: str,
    region: str,
) -> None:
    """Add a new account configuration.

    Args:
        name: Display name for the account.
        username: Service account username.
        secret: Service account secret.
        project_id: Mixpanel project ID.
        region: Data residency region (us, eu, in).

    Raises:
        AccountExistsError: If account name already exists.
        ValueError: If region is invalid.
    """
    # Validate region
    region_lower = region.lower()
    if region_lower not in VALID_REGIONS:
        valid = ", ".join(VALID_REGIONS)
        raise ValueError(f"Region must be one of: {valid}. Got: {region}")

    config = self._read_config()
    accounts = config.setdefault("accounts", {})

    if name in accounts:
        raise AccountExistsError(name)

    accounts[name] = {
        "username": username,
        "secret": secret,
        "project_id": project_id,
        "region": region_lower,
    }

    # If this is the first account, make it the default
    if "default" not in config:
        config["default"] = name

    self._write_config(config)

remove_account

remove_account(name: str) -> None

Remove an account configuration.

PARAMETER DESCRIPTION
name

Account name to remove.

TYPE: str

RAISES DESCRIPTION
AccountNotFoundError

If account doesn't exist.

Source code in src/mixpanel_data/_internal/config.py
def remove_account(self, name: str) -> None:
    """Remove an account configuration.

    Args:
        name: Account name to remove.

    Raises:
        AccountNotFoundError: If account doesn't exist.
    """
    config = self._read_config()
    accounts = config.get("accounts", {})

    if name not in accounts:
        raise AccountNotFoundError(name, available_accounts=list(accounts.keys()))

    del accounts[name]

    # If we removed the default, clear it or set to another account
    if config.get("default") == name:
        if accounts:
            config["default"] = next(iter(accounts.keys()))
        else:
            config.pop("default", None)

    self._write_config(config)

set_default

set_default(name: str) -> None

Set the default account.

PARAMETER DESCRIPTION
name

Account name to set as default.

TYPE: str

RAISES DESCRIPTION
AccountNotFoundError

If account doesn't exist.

Source code in src/mixpanel_data/_internal/config.py
def set_default(self, name: str) -> None:
    """Set the default account.

    Args:
        name: Account name to set as default.

    Raises:
        AccountNotFoundError: If account doesn't exist.
    """
    config = self._read_config()
    accounts = config.get("accounts", {})

    if name not in accounts:
        raise AccountNotFoundError(name, available_accounts=list(accounts.keys()))

    config["default"] = name
    self._write_config(config)

get_account

get_account(name: str) -> AccountInfo

Get information about a specific account.

PARAMETER DESCRIPTION
name

Account name.

TYPE: str

RETURNS DESCRIPTION
AccountInfo

AccountInfo object (secret not included).

RAISES DESCRIPTION
AccountNotFoundError

If account doesn't exist.

Source code in src/mixpanel_data/_internal/config.py
def get_account(self, name: str) -> AccountInfo:
    """Get information about a specific account.

    Args:
        name: Account name.

    Returns:
        AccountInfo object (secret not included).

    Raises:
        AccountNotFoundError: If account doesn't exist.
    """
    config = self._read_config()
    accounts = config.get("accounts", {})

    if name not in accounts:
        raise AccountNotFoundError(name, available_accounts=list(accounts.keys()))

    data = accounts[name]
    default_name = config.get("default")

    return AccountInfo(
        name=name,
        username=data.get("username", ""),
        project_id=data.get("project_id", ""),
        region=data.get("region", ""),
        is_default=(name == default_name),
    )

Credentials

Immutable container for authentication credentials.

mixpanel_data.auth.Credentials

Bases: BaseModel

Immutable credentials for Mixpanel API authentication.

This is a frozen Pydantic model that ensures: - All fields are validated on construction - The secret is never exposed in repr/str output - The object cannot be modified after creation

username instance-attribute

username: str

Service account username.

secret instance-attribute

secret: SecretStr

Service account secret (redacted in output).

project_id instance-attribute

project_id: str

Mixpanel project identifier.

region instance-attribute

region: RegionType

Data residency region (us, eu, or in).

validate_region classmethod

validate_region(v: str) -> str

Validate and normalize region to lowercase.

Source code in src/mixpanel_data/_internal/config.py
@field_validator("region", mode="before")
@classmethod
def validate_region(cls, v: str) -> str:
    """Validate and normalize region to lowercase."""
    if not isinstance(v, str):
        raise ValueError(f"Region must be a string. Got: {type(v).__name__}")
    v_lower = v.lower()
    if v_lower not in VALID_REGIONS:
        valid = ", ".join(VALID_REGIONS)
        raise ValueError(f"Region must be one of: {valid}. Got: {v}")
    return v_lower

validate_non_empty classmethod

validate_non_empty(v: str) -> str

Validate string fields are non-empty.

Source code in src/mixpanel_data/_internal/config.py
@field_validator("username", "project_id")
@classmethod
def validate_non_empty(cls, v: str) -> str:
    """Validate string fields are non-empty."""
    if not v or not v.strip():
        raise ValueError("Field cannot be empty")
    return v

__repr__

__repr__() -> str

Return string representation with redacted secret.

Source code in src/mixpanel_data/_internal/config.py
def __repr__(self) -> str:
    """Return string representation with redacted secret."""
    return (
        f"Credentials(username={self.username!r}, secret=***, "
        f"project_id={self.project_id!r}, region={self.region!r})"
    )

__str__

__str__() -> str

Return string representation with redacted secret.

Source code in src/mixpanel_data/_internal/config.py
def __str__(self) -> str:
    """Return string representation with redacted secret."""
    return self.__repr__()

AccountInfo

Account metadata (without the secret).

mixpanel_data.auth.AccountInfo dataclass

AccountInfo(
    name: str, username: str, project_id: str, region: str, is_default: bool
)

Information about a configured account (without secret).

Used for listing accounts without exposing sensitive credentials.

name instance-attribute

name: str

Account display name.

username instance-attribute

username: str

Service account username.

project_id instance-attribute

project_id: str

Mixpanel project identifier.

region instance-attribute

region: str

Data residency region.

is_default instance-attribute

is_default: bool

Whether this is the default account.