/
/
/
1# Webserver and Authentication Architecture
2
3This document provides a comprehensive overview of the Music Assistant webserver architecture, authentication system, and remote access capabilities.
4
5## Table of Contents
6
7- [Overview](#overview)
8- [Core Components](#core-components)
9- [Authentication System](#authentication-system)
10- [Remote Access (WebRTC)](#remote-access-webrtc)
11- [Request Flow](#request-flow)
12- [Security Considerations](#security-considerations)
13- [Development Guide](#development-guide)
14
15## Overview
16
17The Music Assistant webserver is a core controller that provides:
18- WebSocket-based real-time API for bidirectional communication
19- HTTP/JSON-RPC API for simple request-response interactions
20- User authentication and authorization system
21- Frontend hosting (Vue-based PWA)
22- Remote access via WebRTC for external connectivity
23- Home Assistant integration via Ingress
24
25The webserver runs on port `8095` by default and can be configured via the webserver controller settings.
26
27## Core Components
28
29### 1. WebserverController ([controller.py](controller.py))
30
31The main orchestrator that manages:
32- HTTP server setup and lifecycle
33- Route registration (static files, API endpoints, auth endpoints)
34- WebSocket client management
35- Authentication manager initialization
36- Remote access manager initialization
37- Home Assistant Supervisor announcement (when running as add-on)
38
39**Key responsibilities:**
40- Serves the frontend application (PWA)
41- Hosts the WebSocket API endpoint (`/ws`)
42- Provides HTTP/JSON-RPC API endpoint (`/api`)
43- Manages authentication routes (`/login`, `/auth/*`, `/setup`)
44- Serves API documentation (`/api-docs`)
45- Handles image proxy and audio preview endpoints
46
47### 2. AuthenticationManager ([auth.py](auth.py))
48
49Handles all authentication and user management:
50
51**Database Schema:**
52- `users` - User accounts with roles (admin/user)
53- `user_auth_providers` - Links users to authentication providers (many-to-many)
54- `auth_tokens` - Access tokens with expiration tracking
55- `settings` - Schema version and configuration
56
57**Authentication Providers:**
58- **Built-in Provider** - Username/password authentication with bcrypt hashing
59- **Home Assistant OAuth** - OAuth2 flow for Home Assistant users (auto-enabled when HA provider is configured)
60
61**Token Types:**
62- **Short-lived tokens**: Auto-renewing on use, 30-day sliding expiration window (for user sessions)
63- **Long-lived tokens**: No auto-renewal, 10-year expiration (for integrations/API access)
64
65**Security Features:**
66- Rate limiting on login attempts (progressive delays)
67- Password hashing with bcrypt and user- and server specific salts
68- Secure token generation with secrets.token_urlsafe()
69- WebSocket disconnect on token revocation
70- Session management and cleanup
71
72**User Roles:**
73- `ADMIN` - Full access to all commands and settings
74- `USER` - Standard access (configurable via player/provider filters)
75- `GUEST` - Read-only library access plus player/queue control
76- `SERVICE` - Standard access plus player config, reading user accounts and impersonation
77 (used by the Home Assistant integration)
78
79### 3. RemoteAccessManager ([remote_access/](remote_access/))
80
81Manages WebRTC-based remote access for external connectivity:
82
83**Architecture:**
84- **Signaling Server**: Cloud-based WebSocket server for WebRTC signaling (hosted at `wss://signaling.music-assistant.io/ws`)
85- **WebRTC Gateway**: Local component that bridges WebRTC data channels to the WebSocket API
86- **Remote ID**: Unique identifier (format: `MA-XXXX-XXXX`) for connecting to specific instances
87
88**How it works:**
891. Remote access can be enabled regardless of Home Assistant Cloud subscription
902. A unique Remote ID is generated and stored in config
913. The gateway connects to the signaling server and registers with the Remote ID
924. Remote clients (PWA or mobile apps) connect via WebRTC using the Remote ID
935. Data channel messages are bridged to/from the local WebSocket API
94
95**Connection Modes:**
96
97- **Basic Mode** (default, no HA Cloud required):
98 - Uses public STUN servers (Home Assistant, Google, Cloudflare)
99 - Works in most network configurations
100 - May not work behind complex NAT setups or corporate firewalls
101 - Free for all users
102
103- **Optimized Mode** (with HA Cloud subscription):
104 - Uses Home Assistant Cloud STUN/TURN servers
105 - Reliable connections in all network configurations
106 - TURN relay servers ensure connectivity even in restrictive networks
107 - Requires active Home Assistant Cloud subscription
108
109**Key features:**
110- Automatic reconnection on signaling server disconnect
111- Multiple concurrent WebRTC sessions supported
112- No port forwarding required
113- End-to-end encryption via WebRTC (DTLS-SRTP)
114- Automatic mode switching when HA Cloud status changes
115
116### 4. WebSocket Client Handler ([websocket_client.py](websocket_client.py))
117
118Manages individual WebSocket connections:
119- Authentication enforcement (auth or login command must be first)
120- Command routing and response handling
121- Event subscription and broadcasting
122- Connection lifecycle management
123- Token validation and user context
124
125### 5. Authentication Helpers
126
127**Helpers ([helpers/auth_middleware.py](helpers/auth_middleware.py)):**
128- Request authentication for HTTP endpoints, called per handler (there is no aiohttp middleware)
129- User context management (thread-local storage)
130- Ingress detection (Home Assistant add-on)
131- Token extraction from Authorization header
132
133**Providers ([helpers/auth_providers.py](helpers/auth_providers.py)):**
134- Base classes for authentication providers
135- Built-in username/password provider
136- Home Assistant OAuth provider
137- Rate limiting implementation
138
139## Authentication System
140
141### First-Time Setup Flow
142
1431. **Initial State**: No users exist
1442. **Setup Required**: User is redirected to `/setup`
1453. **Admin Creation**: User creates the first admin account with username/password
1464. **Setup completes** User gets redirected to the frontend
1475. **Onboarding wizard** The frontend shows the onboarding wizard if it detects 'onboard_done' is False
1484. **Onboarding Complete**: User completes onboarding and the `onboard_done` flag is set to `true`
149
150### First-Time Setup Flow when HA Ingress is used
151
1521. **Initial State**: No users exist
1532. **Auto user creation**: User is auto created based on HA user
1544. **Setup completes** User gets redirected to the frontend
1555. **Onboarding wizard** The frontend shows the onboarding wizard if it detects 'onboard_done' is False
1564. **Onboarding Complete**: User completes onboarding and the `onboard_done` flag is set to `true`
157
158### Login Flow (Standard)
159
1601. **Client Request**: POST to `/auth/login` with credentials
1612. **Provider Authentication**: Credentials validated by authentication provider
1623. **Token Generation**: Short-lived token created for the user
1634. **Response**: Token and user info returned to frontend
1645. **Subsequent Requests**: Token included in Authorization header or WebSocket auth command
165
166### Login Flow (Home Assistant OAuth)
167
1681. **Initiate OAuth**: GET `/auth/authorize?provider_id=homeassistant&return_url=...`
1692. **Redirect to HA**: User is redirected to Home Assistant OAuth consent page
1703. **OAuth Callback**: HA redirects back to `/auth/callback` with code and state
1714. **Token Exchange**: Code exchanged for HA access token
1725. **User Lookup/Creation**: User found or created with HA provider link
1736. **Token Generation**: MA token created and returned via redirect with `code` parameter
1747. **Client Handling**: Client extracts token from URL and stores it
175
176### Remote Client OAuth Flow
177
178For remote clients (PWA over WebRTC), OAuth requires special handling since redirect URLs can't point to localhost:
179
1801. **Request Session**: Remote client calls `auth/authorization_url` with `for_remote_client=true`
1812. **Session Created**: Server creates a pending OAuth session and returns session_id and auth URL
1823. **User Opens Browser**: Client opens auth URL in system browser
1834. **OAuth Flow**: User completes OAuth in browser
1845. **Token Stored**: Server stores token in pending session (using special return URL format)
1856. **Polling**: Client polls `auth/oauth_status` with session_id
1867. **Token Retrieved**: Once complete, client receives token and can authenticate
187
188### Ingress Authentication (Home Assistant Add-on)
189
190When running as a Home Assistant add-on:
191- A dedicated webserver TCP site is hosted (on port 8094) bound to the internal HA docker network only
192- Ingress requests include HA user headers (`X-Remote-User-ID`, `X-Remote-User-Name`)
193- Users are auto-created on first access
194- No password required (authentication handled by HA)
195- System user created for HA integration communication
196
197### WebSocket Authentication
198
1991. **Connection Established**: Client connects to `/ws`
2002. **Auth Command Required**: First command must be `auth` with token
2013. **Token Validation**: Token validated and user context set
2024. **Authenticated Session**: All subsequent commands executed in user context
2035. **Auto-Disconnect**: Connection closed on token revocation or user disable
204
205## Remote Access (WebRTC)
206
207### Architecture Overview
208
209Remote access enables users to connect to their Music Assistant instance from anywhere without port forwarding or VPN:
210
211```
212[Remote Client (PWA or app)]
213 |
214 | WebRTC Data Channel
215 v
216[Signaling Server] ââ [WebRTC Gateway]
217 |
218 | WebSocket
219 v
220 [Local WebSocket API]
221```
222
223### Components
224
225**Signaling Server** (`wss://signaling.music-assistant.io/ws`):
226- Cloud-based WebSocket server for WebRTC signaling
227- Handles SDP offer/answer exchange
228- Routes ICE candidates between peers
229- Maintains Remote ID registry
230
231**WebRTC Gateway** ([remote_access/gateway.py](remote_access/gateway.py)):
232- Runs locally as part of the webserver controller
233- Connects to signaling server and registers Remote ID
234- Accepts incoming WebRTC connections from remote clients
235- Bridges WebRTC data channel messages to local WebSocket API
236- Handles multiple concurrent sessions
237
238**Remote ID**:
239- Format: `MA-XXXX-XXXX` (e.g., `MA-K7G3-P2M4`)
240- Uniquely identifies a Music Assistant instance
241- Generated once and stored in controller config
242- Used by remote clients to connect to specific instance
243
244### Connection Flow
245
2461. **Initialization**:
247 - Remote access is enabled by user in settings
248 - Remote ID generated/retrieved from config
249 - HA Cloud status checked (determines mode)
250 - Gateway connects to signaling server with appropriate ICE servers
251 - Remote ID registered with signaling server
252
2532. **Remote Client Connection**:
254 - User opens PWA (https://app.music-assistant.io) and enters Remote ID
255 - PWA creates WebRTC peer connection
256 - PWA sends SDP offer via signaling server
257 - Gateway receives offer and creates peer connection
258 - Gateway sends SDP answer via signaling server
259 - ICE candidates exchanged for NAT traversal
260 - WebRTC data channel established
261
2623. **Message Bridging**:
263 - Remote client sends WebSocket-format messages over data channel
264 - Gateway forwards messages to local WebSocket API
265 - Responses and events sent back through data channel
266 - Authentication and authorization work identically to local WebSocket
267
268### ICE Servers (STUN/TURN)
269
270NAT traversal is critical for WebRTC connections. Music Assistant uses:
271
272- **STUN servers**: Servers for discovering public IP addresses and port mappings
273- **TURN servers**: Relay servers for cases where direct peer-to-peer connection fails
274
275**Basic Mode (Public STUN):**
276- `stun:stun.home-assistant.io:3478` (Home Assistant public STUN)
277- `stun:stun.l.google.com:19302` (Google public STUN)
278- `stun:stun1.l.google.com:19302` (Google public STUN backup)
279- `stun:stun.cloudflare.com:3478` (Cloudflare public STUN)
280
281Most connections succeed with public STUN servers alone, but they may fail in:
282- Symmetric NAT configurations
283- Corporate firewalls that block UDP
284- Networks with restrictive firewall policies
285
286**Optimized Mode (HA Cloud):**
287- STUN/TURN servers provided by Home Assistant Cloud
288- Includes TURN relay servers for guaranteed connectivity
289
290### Availability
291
292Remote access is available to all users:
293- **Basic Mode**: Always available, no subscription required
294- **Optimized Mode**: Requires active Home Assistant Cloud subscription
295
296### API Endpoints
297
298**`remote_access/info`** (WebSocket command):
299Returns remote access status:
300```json
301{
302 "enabled": true,
303 "running": true,
304 "connected": true,
305 "remote_id": "MA-K7G3-P2M4",
306 "using_ha_cloud": false,
307 "signaling_url": "wss://signaling.music-assistant.io/ws"
308}
309```
310
311**`remote_access/configure`** (WebSocket command, admin only):
312Enable or disable remote access:
313```json
314{
315 "enabled": true
316}
317```
318
319## Request Flow
320
321### HTTP Request Flow
322
323```
324HTTP Request â Webserver â Command Handler â Response
325 |
326 ââ get_authenticated_user()
327 ââ Ingress? â Auto-authenticate with HA headers
328 ââ Regular? â Validate Bearer token
329```
330
331### WebSocket Request Flow
332
333```
334WebSocket Connect â WebsocketClientHandler
335 |
336 ââ First command: auth â Validate token â Set user context
337 ââ Subsequent commands â Check auth/role â Execute â Respond
338```
339
340### Remote WebRTC Request Flow
341
342```
343Remote Client â WebRTC Data Channel â Gateway â Local WebSocket API
344 |
345 ââ Message forwarding (bidirectional)
346```
347
348## Security Considerations
349
350### Authentication
351
352- **Mandatory authentication**: All API access requires authentication (except Ingress)
353- **Secure token generation**: Uses `secrets.token_urlsafe(48)` for cryptographically secure tokens
354- **Password hashing**: bcrypt with user-specific salts
355- **Rate limiting**: Progressive delays on failed login attempts
356- **Token expiration**: Both short-lived (30 days sliding) and long-lived (10 years) tokens supported
357
358### Authorization
359
360- **Role-based access**: Admin vs User roles
361- **Command-level enforcement**: API commands can require specific roles
362- **Player/Provider filtering**: Users can be restricted to specific players/providers
363- **Token revocation**: Immediate WebSocket disconnect on token revocation
364
365### Network Security
366
367**Local Network:**
368- Webserver is unencrypted (HTTP) by design (runs on local network)
369- Users should use reverse proxy or VPN for external access
370- Never expose webserver directly to internet
371
372**Remote Access:**
373- End-to-end encryption via WebRTC (DTLS/SRTP)
374- Authentication required (same as local access)
375- Signaling server only routes encrypted signaling messages
376- Cannot decrypt or inspect user data
377
378### Data Protection
379
380- **Token storage**: Only hashed tokens stored in database
381- **Password storage**: bcrypt with user-specific salts
382- **Session cleanup**: Expired tokens automatically deleted
383- **User disable**: Immediate disconnect of all user sessions
384
385## Development Guide
386
387### Adding New Authentication Providers
388
3891. Create provider class inheriting from `LoginProvider` in [helpers/auth_providers.py](helpers/auth_providers.py)
3902. Implement required methods: `authenticate()`, `get_authorization_url()` (if OAuth), `handle_oauth_callback()` (if OAuth)
3913. Register provider in `AuthenticationManager._setup_login_providers()`
3924. Add provider configuration to webserver config entries if needed
393
394### Adding New API Endpoints
395
3961. Define route handler in [controller.py](controller.py) (for HTTP endpoints)
3972. Use `@api_command()` decorator for WebSocket commands (in respective controllers)
3983. Specify authentication requirements: `authenticated=True` and/or `required_scope=Scope.<SCOPE>`
3994. Optionally set `allow_impersonation=True` to let callers execute the command on behalf of
400 another user via the injected `user` argument (requires the `users.impersonate` scope
401 when targeting another user)
402
403### Testing Authentication
404
4051. **Local Testing**: Use `/setup` to create admin user, then `/auth/login` to get token
4062. **HTTP API Testing**: Use curl with `Authorization: Bearer <token>` header
4073. **WebSocket Testing**: Connect to `/ws` and send auth command with token
4084. **Role Testing**: Create users with different roles and test access restrictions
409
410### Common Patterns
411
412**Getting current user in command handler:**
413```python
414from music_assistant.controllers.webserver.helpers.auth_middleware import get_current_user
415
416@api_command("my_command")
417async def my_command():
418 user = get_current_user()
419 if not user:
420 raise AuthenticationRequired("Not authenticated")
421 # ... use user ...
422```
423
424**Getting current token (for revocation):**
425```python
426from music_assistant.controllers.webserver.helpers.auth_middleware import get_current_token
427
428@api_command("my_command")
429async def my_command():
430 token = get_current_token()
431 # ... use token ...
432```
433
434**Requiring a scope:**
435```python
436from music_assistant_models.auth import Scope
437
438@api_command("admin_only_command", required_scope=Scope.CONFIG_CORE_WRITE)
439async def admin_command():
440 # Only users whose role grants the config.core.write scope can call this
441 pass
442```
443
444Scopes are granted to users through their role, see `ROLE_SCOPES` in
445[helpers/auth_middleware.py](helpers/auth_middleware.py) for the builtin role definitions.
446
447### Database Migrations
448
449When modifying the auth database schema:
4501. Increment `DB_SCHEMA_VERSION` in [auth.py](auth.py)
4512. Add migration logic to `_migrate_database()` method
4523. Test migration from previous version
4534. Consider backwards compatibility
454
455### Testing Remote Access
456
4571. **Enable Remote Access**: Toggle remote access in settings UI or via API
4582. **Verify Remote ID**: Check webserver config for generated Remote ID
4593. **Test Gateway**: Check logs for "Starting remote access in basic/optimized mode" message
4604. **Test Connection**: Use PWA with Remote ID to connect externally
4615. **Monitor Sessions**: Check `remote_access/info` command for status and mode
4626. **Test Mode Switching**: Enable/disable HA Cloud and verify automatic mode switching
463
464## File Structure
465
466```
467webserver/
468âââ __init__.py # Module exports
469âââ controller.py # Main webserver controller
470âââ auth.py # Authentication manager
471âââ websocket_client.py # WebSocket client handler
472âââ api_docs.py # API documentation generator
473âââ README.md # This file
474âââ helpers/
475â âââ auth_middleware.py # HTTP/WebSocket auth helpers
476â âââ auth_providers.py # Authentication providers
477âââ remote_access/
478 âââ __init__.py # Remote access manager
479 âââ gateway.py # WebRTC gateway implementation
480```
481
482## Additional Resources
483
484- [API Documentation](http://localhost:8095/api-docs) - Auto-generated API docs
485- [Commands Reference](http://localhost:8095/api-docs/commands) - List of all API commands
486- [Schemas Reference](http://localhost:8095/api-docs/schemas) - Data model documentation
487- [Swagger UI](http://localhost:8095/api-docs/swagger) - Interactive API explorer
488
489## Contributing
490
491When contributing to the webserver/auth system:
4921. Follow the existing patterns for consistency
4932. Add comprehensive docstrings with Sphinx-style parameter documentation
4943. Update this README if adding significant new features
4954. Test authentication flows thoroughly
4965. Consider security implications of all changes
4976. The API documentation will be auto updated if adding new commands (based on docstrings and type hints)
498
499---
500
501*This architecture document is maintained alongside the code and should be updated when significant changes are made to the provider's design or functionality.*
502