# Security Best Practices for Storing Provider Credentials in Music Assistant

> Secure your provider credentials in Music Assistant. Learn best practices for secret config entries, user-specific storage, and log redaction to protect your data.

- Repository: [Music Assistant/server](https://github.com/music-assistant/server)
- Tags: best-practices
- Published: 2026-06-13

---

**Music Assistant protects provider credentials by marking them as secret config entries, storing them only in the user-specific data directory, and automatically redacting them from all logs and UI responses.**

The `music-assistant/server` repository treats provider credentials as sensitive configuration values that require strict handling throughout their lifecycle. Following the principle of least exposure, the codebase implements multiple layers of defense to ensure tokens and passwords never appear in logs, version control, or frontend responses. These security best practices for storing provider credentials combine declarative secret marking, filesystem isolation, and runtime redaction to minimize the attack surface.

## Mark Provider Credentials as Secret Config Entries

Provider modules declare sensitive values using `ConfigEntry` with the `secret=True` flag. This declarative approach tells the configuration system to mask the value during serialization and display operations.

In [`music_assistant/providers/qqmusic/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/qqmusic/__init__.py) (line 250), the QQMusic provider registers its OAuth credential JSON as a secret entry:

```python

# music_assistant/providers/qqmusic/__init__.py

CONF_CREDENTIAL_JSON: Final[str] = "credential_json"

self.config.register_entry(
    ConfigEntry(
        key=CONF_CREDENTIAL_JSON,
        type=ConfigValueType.STRING,
        title="QQ Music credential JSON",
        description="OAuth credential JSON obtained via QR‑login.",
        secret=True,                # <- marks the entry as sensitive

    )
)

```

When loading the credential, the provider retrieves the value securely without exposing it in stack traces or debug output:

```python

# Inside the provider class

credential_json = self.config.get_value(CONF_CREDENTIAL_JSON) or ""
if credential_json:
    credential = Credential.from_json(credential_json)
else:
    # trigger the QR‑login flow

    credential = await self._login_via_qr()

```

## Isolate Credential Storage in the User Data Directory

All persisted data lives under `$HOME/.musicassistant/`, as documented in [`music_assistant/helpers/security.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/security.py). The server never writes credentials to the source tree or system-wide directories.

This isolation prevents accidental version-control commits of secrets and limits file-system exposure to the local user account. By keeping credentials outside the repository root, the system ensures that tokens survive application updates without risking exposure in development environments.

## Redact Secrets from Log Output Automatically

The logging infrastructure implements **SafeLogTail** to prevent credential leakage through log files. Before writing to disk, the system scans log messages and replaces any detected secret values with `<redacted>`.

The [`music_assistant/helpers/logging.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/logging.py) module provides this functionality, which is rigorously tested in [`tests/providers/fastmcp_server/test_debug_logs.py`](https://github.com/music-assistant/server/blob/main/tests/providers/fastmcp_server/test_debug_logs.py) (line 90):

```python

# tests/providers/fastmcp_server/test_debug_logs.py

def test_tail_redacts_query_string_secrets(tmp_log_dir: Path):
    # Log a URL that contains a token

    logger.info("GET /api?token=super_secret")
    # The tail should replace the token with <redacted>

    assert "<redacted>" in tail.read()

```

This guarantees that even if a crash dump is examined or logs are shared for debugging, credentials remain confidential.

## Prevent UI Echo of Sensitive Configuration Values

The frontend explicitly avoids echoing stored secrets when configuration dialogs reopen. This prevents shoulder-surfing attacks and accidental exposure in shared screen scenarios.

For example, the Yandex Music provider in [`music_assistant/providers/yandex_music/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/yandex_music/__init__.py) (line 292) clears credential fields when the configuration dialog is opened, ensuring that previously stored tokens are not rendered in the HTML or API responses sent to the client.

## Handle Token Refresh Exclusively In-Memory

When refreshing expired credentials, the system performs operations entirely in memory without logging raw tokens. The `refresh_credentials_via_passport` function in [`music_assistant/providers/yandex_music/auth.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/yandex_music/auth.py) (line 335) retrieves a new credential triple and replaces the stored value atomically.

This approach limits the window during which a token could be intercepted and ensures that transient refresh tokens never persist in swap files or hibernation data beyond their necessary lifespan.

## Generate Cryptographically Secure Random Tokens

For temporary authentication data such as CSRF tokens and state parameters, the system uses Python's `secrets` module instead of pseudo-random generators. The `generate_csrf_token()` function in [`music_assistant/helpers/jwt_auth.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/jwt_auth.py) (line 92) creates high-entropy values:

```python

# music_assistant/helpers/jwt_auth.py

def generate_csrf_token() -> str:
    # 32‑byte (256‑bit) URL‑safe token

    return secrets.token_urlsafe(32)

```

Using `secrets.token_urlsafe(32)` guarantees cryptographically strong randomness, preventing predictable token generation that could facilitate cross-site request forgery attacks.

## Summary

- **Mark secrets explicitly**: Use `ConfigEntry(secret=True)` to ensure the system masks credentials during serialization and display.
- **Isolate storage**: Persist credentials only in `$HOME/.musicassistant/` to prevent version control leaks and limit filesystem access.
- **Redact logs**: The `SafeLogTail` utility automatically replaces secret values with `<redacted>` in all log output.
- **Suppress UI echo**: Clear credential fields when configuration dialogs reopen to prevent frontend exposure.
- **Process in-memory**: Handle token refresh operations atomically in memory without logging raw values.
- **Use secure randomness**: Generate temporary tokens with `secrets.token_urlsafe()` to ensure cryptographic strength.

## Frequently Asked Questions

### Where does Music Assistant store provider credentials?

Credentials are persisted in the user-specific data directory at `$HOME/.musicassistant/`, as defined in [`music_assistant/helpers/security.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/security.py). This location prevents accidental commits to version control and limits file system access to the local user account.

### How does Music Assistant prevent credentials from appearing in logs?

The `SafeLogTail` utility in [`music_assistant/helpers/logging.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/logging.py) automatically scans log output and replaces any detected secret values with `<redacted>`. Unit tests in [`tests/providers/fastmcp_server/test_debug_logs.py`](https://github.com/music-assistant/server/blob/main/tests/providers/fastmcp_server/test_debug_logs.py) verify that tokens in URLs or request bodies are never written to disk.

### What happens when a user reopens a provider configuration dialog?

The UI explicitly clears credential fields when the configuration dialog is reopened. For example, the Yandex Music provider in [`music_assistant/providers/yandex_music/__init__.py`](https://github.com/music-assistant/server/blob/main/music_assistant/providers/yandex_music/__init__.py) ensures that previously stored tokens are not echoed back to the frontend, preventing shoulder-surfing or accidental exposure.

### How are temporary authentication tokens generated?

The system uses Python's built-in `secrets` module to generate cryptographically strong random values. The `generate_csrf_token()` function in [`music_assistant/helpers/jwt_auth.py`](https://github.com/music-assistant/server/blob/main/music_assistant/helpers/jwt_auth.py) creates 256-bit URL-safe tokens using `secrets.token_urlsafe(32)`, ensuring high entropy for CSRF protection and state parameters.