Security Best Practices for Storing Provider Credentials in Music Assistant
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 (line 250), the QQMusic provider registers its OAuth credential JSON as a secret entry:
# 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:
# 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. 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 module provides this functionality, which is rigorously tested in tests/providers/fastmcp_server/test_debug_logs.py (line 90):
# 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 (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 (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 (line 92) creates high-entropy values:
# 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
SafeLogTailutility 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. 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 automatically scans log output and replaces any detected secret values with <redacted>. Unit tests in 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 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 creates 256-bit URL-safe tokens using secrets.token_urlsafe(32), ensuring high entropy for CSRF protection and state parameters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →