music-assistant-server
18.8 KB•MD
README.md
18.8 KB • 490 lines • markdown
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
76### 3. RemoteAccessManager ([remote_access/](remote_access/))
77
78Manages WebRTC-based remote access for external connectivity:
79
80**Architecture:**
81- **Signaling Server**: Cloud-based WebSocket server for WebRTC signaling (hosted at `wss://signaling.music-assistant.io/ws`)
82- **WebRTC Gateway**: Local component that bridges WebRTC data channels to the WebSocket API
83- **Remote ID**: Unique identifier (format: `MA-XXXX-XXXX`) for connecting to specific instances
84
85**How it works:**
861. Remote access can be enabled regardless of Home Assistant Cloud subscription
872. A unique Remote ID is generated and stored in config
883. The gateway connects to the signaling server and registers with the Remote ID
894. Remote clients (PWA or mobile apps) connect via WebRTC using the Remote ID
905. Data channel messages are bridged to/from the local WebSocket API
91
92**Connection Modes:**
93
94- **Basic Mode** (default, no HA Cloud required):
95 - Uses public STUN servers (Home Assistant, Google, Cloudflare)
96 - Works in most network configurations
97 - May not work behind complex NAT setups or corporate firewalls
98 - Free for all users
99
100- **Optimized Mode** (with HA Cloud subscription):
101 - Uses Home Assistant Cloud STUN/TURN servers
102 - Reliable connections in all network configurations
103 - TURN relay servers ensure connectivity even in restrictive networks
104 - Requires active Home Assistant Cloud subscription
105
106**Key features:**
107- Automatic reconnection on signaling server disconnect
108- Multiple concurrent WebRTC sessions supported
109- No port forwarding required
110- End-to-end encryption via WebRTC (DTLS-SRTP)
111- Automatic mode switching when HA Cloud status changes
112
113### 4. WebSocket Client Handler ([websocket_client.py](websocket_client.py))
114
115Manages individual WebSocket connections:
116- Authentication enforcement (auth or login command must be first)
117- Command routing and response handling
118- Event subscription and broadcasting
119- Connection lifecycle management
120- Token validation and user context
121
122### 5. Authentication Helpers
123
124**Middleware ([helpers/auth_middleware.py](helpers/auth_middleware.py)):**
125- Request authentication for HTTP endpoints
126- User context management (thread-local storage)
127- Ingress detection (Home Assistant add-on)
128- Token extraction from Authorization header
129
130**Providers ([helpers/auth_providers.py](helpers/auth_providers.py)):**
131- Base classes for authentication providers
132- Built-in username/password provider
133- Home Assistant OAuth provider
134- Rate limiting implementation
135
136## Authentication System
137
138### First-Time Setup Flow
139
1401. **Initial State**: No users exist
1412. **Setup Required**: User is redirected to `/setup`
1423. **Admin Creation**: User creates the first admin account with username/password
1434. **Setup completes** User gets redirected to the frontend
1445. **Onboarding wizard** The frontend shows the onboarding wizard if it detects 'onboard_done' is False
1454. **Onboarding Complete**: User completes onboarding and the `onboard_done` flag is set to `true`
146
147### First-Time Setup Flow when HA Ingress is used
148
1491. **Initial State**: No users exist
1502. **Auto user creation**: User is auto created based on HA user
1514. **Setup completes** User gets redirected to the frontend
1525. **Onboarding wizard** The frontend shows the onboarding wizard if it detects 'onboard_done' is False
1534. **Onboarding Complete**: User completes onboarding and the `onboard_done` flag is set to `true`
154
155### Login Flow (Standard)
156
1571. **Client Request**: POST to `/auth/login` with credentials
1582. **Provider Authentication**: Credentials validated by authentication provider
1593. **Token Generation**: Short-lived token created for the user
1604. **Response**: Token and user info returned to frontend
1615. **Subsequent Requests**: Token included in Authorization header or WebSocket auth command
162
163### Login Flow (Home Assistant OAuth)
164
1651. **Initiate OAuth**: GET `/auth/authorize?provider_id=homeassistant&return_url=...`
1662. **Redirect to HA**: User is redirected to Home Assistant OAuth consent page
1673. **OAuth Callback**: HA redirects back to `/auth/callback` with code and state
1684. **Token Exchange**: Code exchanged for HA access token
1695. **User Lookup/Creation**: User found or created with HA provider link
1706. **Token Generation**: MA token created and returned via redirect with `code` parameter
1717. **Client Handling**: Client extracts token from URL and stores it
172
173### Remote Client OAuth Flow
174
175For remote clients (PWA over WebRTC), OAuth requires special handling since redirect URLs can't point to localhost:
176
1771. **Request Session**: Remote client calls `auth/authorization_url` with `for_remote_client=true`
1782. **Session Created**: Server creates a pending OAuth session and returns session_id and auth URL
1793. **User Opens Browser**: Client opens auth URL in system browser
1804. **OAuth Flow**: User completes OAuth in browser
1815. **Token Stored**: Server stores token in pending session (using special return URL format)
1826. **Polling**: Client polls `auth/oauth_status` with session_id
1837. **Token Retrieved**: Once complete, client receives token and can authenticate
184
185### Ingress Authentication (Home Assistant Add-on)
186
187When running as a Home Assistant add-on:
188- A dedicated webserver TCP site is hosted (on port 8094) bound to the internal HA docker network only
189- Ingress requests include HA user headers (`X-Remote-User-ID`, `X-Remote-User-Name`)
190- Users are auto-created on first access
191- No password required (authentication handled by HA)
192- System user created for HA integration communication
193
194### WebSocket Authentication
195
1961. **Connection Established**: Client connects to `/ws`
1972. **Auth Command Required**: First command must be `auth` with token
1983. **Token Validation**: Token validated and user context set
1994. **Authenticated Session**: All subsequent commands executed in user context
2005. **Auto-Disconnect**: Connection closed on token revocation or user disable
201
202## Remote Access (WebRTC)
203
204### Architecture Overview
205
206Remote access enables users to connect to their Music Assistant instance from anywhere without port forwarding or VPN:
207
208```
209[Remote Client (PWA or app)]
210 |
211 | WebRTC Data Channel
212 v
213[Signaling Server] ââ [WebRTC Gateway]
214 |
215 | WebSocket
216 v
217 [Local WebSocket API]
218```
219
220### Components
221
222**Signaling Server** (`wss://signaling.music-assistant.io/ws`):
223- Cloud-based WebSocket server for WebRTC signaling
224- Handles SDP offer/answer exchange
225- Routes ICE candidates between peers
226- Maintains Remote ID registry
227
228**WebRTC Gateway** ([remote_access/gateway.py](remote_access/gateway.py)):
229- Runs locally as part of the webserver controller
230- Connects to signaling server and registers Remote ID
231- Accepts incoming WebRTC connections from remote clients
232- Bridges WebRTC data channel messages to local WebSocket API
233- Handles multiple concurrent sessions
234
235**Remote ID**:
236- Format: `MA-XXXX-XXXX` (e.g., `MA-K7G3-P2M4`)
237- Uniquely identifies a Music Assistant instance
238- Generated once and stored in controller config
239- Used by remote clients to connect to specific instance
240
241### Connection Flow
242
2431. **Initialization**:
244 - Remote access is enabled by user in settings
245 - Remote ID generated/retrieved from config
246 - HA Cloud status checked (determines mode)
247 - Gateway connects to signaling server with appropriate ICE servers
248 - Remote ID registered with signaling server
249
2502. **Remote Client Connection**:
251 - User opens PWA (https://app.music-assistant.io) and enters Remote ID
252 - PWA creates WebRTC peer connection
253 - PWA sends SDP offer via signaling server
254 - Gateway receives offer and creates peer connection
255 - Gateway sends SDP answer via signaling server
256 - ICE candidates exchanged for NAT traversal
257 - WebRTC data channel established
258
2593. **Message Bridging**:
260 - Remote client sends WebSocket-format messages over data channel
261 - Gateway forwards messages to local WebSocket API
262 - Responses and events sent back through data channel
263 - Authentication and authorization work identically to local WebSocket
264
265### ICE Servers (STUN/TURN)
266
267NAT traversal is critical for WebRTC connections. Music Assistant uses:
268
269- **STUN servers**: Servers for discovering public IP addresses and port mappings
270- **TURN servers**: Relay servers for cases where direct peer-to-peer connection fails
271
272**Basic Mode (Public STUN):**
273- `stun:stun.home-assistant.io:3478` (Home Assistant public STUN)
274- `stun:stun.l.google.com:19302` (Google public STUN)
275- `stun:stun1.l.google.com:19302` (Google public STUN backup)
276- `stun:stun.cloudflare.com:3478` (Cloudflare public STUN)
277
278Most connections succeed with public STUN servers alone, but they may fail in:
279- Symmetric NAT configurations
280- Corporate firewalls that block UDP
281- Networks with restrictive firewall policies
282
283**Optimized Mode (HA Cloud):**
284- STUN/TURN servers provided by Home Assistant Cloud
285- Includes TURN relay servers for guaranteed connectivity
286
287### Availability
288
289Remote access is available to all users:
290- **Basic Mode**: Always available, no subscription required
291- **Optimized Mode**: Requires active Home Assistant Cloud subscription
292
293### API Endpoints
294
295**`remote_access/info`** (WebSocket command):
296Returns remote access status:
297```json
298{
299 "enabled": true,
300 "running": true,
301 "connected": true,
302 "remote_id": "MA-K7G3-P2M4",
303 "using_ha_cloud": false,
304 "signaling_url": "wss://signaling.music-assistant.io/ws"
305}
306```
307
308**`remote_access/configure`** (WebSocket command, admin only):
309Enable or disable remote access:
310```json
311{
312 "enabled": true
313}
314```
315
316## Request Flow
317
318### HTTP Request Flow
319
320```
321HTTP Request â Webserver â Auth Middleware â Command Handler â Response
322 |
323 ââ Ingress? â Auto-authenticate with HA headers
324 ââ Regular? â Validate Bearer token
325```
326
327### WebSocket Request Flow
328
329```
330WebSocket Connect â WebsocketClientHandler
331 |
332 ââ First command: auth â Validate token â Set user context
333 ââ Subsequent commands â Check auth/role â Execute â Respond
334```
335
336### Remote WebRTC Request Flow
337
338```
339Remote Client â WebRTC Data Channel â Gateway â Local WebSocket API
340 |
341 ââ Message forwarding (bidirectional)
342```
343
344## Security Considerations
345
346### Authentication
347
348- **Mandatory authentication**: All API access requires authentication (except Ingress)
349- **Secure token generation**: Uses `secrets.token_urlsafe(48)` for cryptographically secure tokens
350- **Password hashing**: bcrypt with user-specific salts
351- **Rate limiting**: Progressive delays on failed login attempts
352- **Token expiration**: Both short-lived (30 days sliding) and long-lived (10 years) tokens supported
353
354### Authorization
355
356- **Role-based access**: Admin vs User roles
357- **Command-level enforcement**: API commands can require specific roles
358- **Player/Provider filtering**: Users can be restricted to specific players/providers
359- **Token revocation**: Immediate WebSocket disconnect on token revocation
360
361### Network Security
362
363**Local Network:**
364- Webserver is unencrypted (HTTP) by design (runs on local network)
365- Users should use reverse proxy or VPN for external access
366- Never expose webserver directly to internet
367
368**Remote Access:**
369- End-to-end encryption via WebRTC (DTLS/SRTP)
370- Authentication required (same as local access)
371- Signaling server only routes encrypted signaling messages
372- Cannot decrypt or inspect user data
373
374### Data Protection
375
376- **Token storage**: Only hashed tokens stored in database
377- **Password storage**: bcrypt with user-specific salts
378- **Session cleanup**: Expired tokens automatically deleted
379- **User disable**: Immediate disconnect of all user sessions
380
381## Development Guide
382
383### Adding New Authentication Providers
384
3851. Create provider class inheriting from `LoginProvider` in [helpers/auth_providers.py](helpers/auth_providers.py)
3862. Implement required methods: `authenticate()`, `get_authorization_url()` (if OAuth), `handle_oauth_callback()` (if OAuth)
3873. Register provider in `AuthenticationManager._setup_login_providers()`
3884. Add provider configuration to webserver config entries if needed
389
390### Adding New API Endpoints
391
3921. Define route handler in [controller.py](controller.py) (for HTTP endpoints)
3932. Use `@api_command()` decorator for WebSocket commands (in respective controllers)
3943. Specify authentication requirements: `authenticated=True` or `required_role="admin"`
395
396### Testing Authentication
397
3981. **Local Testing**: Use `/setup` to create admin user, then `/auth/login` to get token
3992. **HTTP API Testing**: Use curl with `Authorization: Bearer <token>` header
4003. **WebSocket Testing**: Connect to `/ws` and send auth command with token
4014. **Role Testing**: Create users with different roles and test access restrictions
402
403### Common Patterns
404
405**Getting current user in command handler:**
406```python
407from music_assistant.controllers.webserver.helpers.auth_middleware import get_current_user
408
409@api_command("my_command")
410async def my_command():
411 user = get_current_user()
412 if not user:
413 raise AuthenticationRequired("Not authenticated")
414 # ... use user ...
415```
416
417**Getting current token (for revocation):**
418```python
419from music_assistant.controllers.webserver.helpers.auth_middleware import get_current_token
420
421@api_command("my_command")
422async def my_command():
423 token = get_current_token()
424 # ... use token ...
425```
426
427**Requiring admin role:**
428```python
429@api_command("admin_only_command", required_role="admin")
430async def admin_command():
431 # Only admins can call this
432 pass
433```
434
435### Database Migrations
436
437When modifying the auth database schema:
4381. Increment `DB_SCHEMA_VERSION` in [auth.py](auth.py)
4392. Add migration logic to `_migrate_database()` method
4403. Test migration from previous version
4414. Consider backwards compatibility
442
443### Testing Remote Access
444
4451. **Enable Remote Access**: Toggle remote access in settings UI or via API
4462. **Verify Remote ID**: Check webserver config for generated Remote ID
4473. **Test Gateway**: Check logs for "Starting remote access in basic/optimized mode" message
4484. **Test Connection**: Use PWA with Remote ID to connect externally
4495. **Monitor Sessions**: Check `remote_access/info` command for status and mode
4506. **Test Mode Switching**: Enable/disable HA Cloud and verify automatic mode switching
451
452## File Structure
453
454```
455webserver/
456âââ __init__.py # Module exports
457âââ controller.py # Main webserver controller
458âââ auth.py # Authentication manager
459âââ websocket_client.py # WebSocket client handler
460âââ api_docs.py # API documentation generator
461âââ README.md # This file
462âââ helpers/
463â âââ auth_middleware.py # HTTP auth middleware
464â âââ auth_providers.py # Authentication providers
465âââ remote_access/
466 âââ __init__.py # Remote access manager
467 âââ gateway.py # WebRTC gateway implementation
468```
469
470## Additional Resources
471
472- [API Documentation](http://localhost:8095/api-docs) - Auto-generated API docs
473- [Commands Reference](http://localhost:8095/api-docs/commands) - List of all API commands
474- [Schemas Reference](http://localhost:8095/api-docs/schemas) - Data model documentation
475- [Swagger UI](http://localhost:8095/api-docs/swagger) - Interactive API explorer
476
477## Contributing
478
479When contributing to the webserver/auth system:
4801. Follow the existing patterns for consistency
4812. Add comprehensive docstrings with Sphinx-style parameter documentation
4823. Update this README if adding significant new features
4834. Test authentication flows thoroughly
4845. Consider security implications of all changes
4856. The API documentation will be auto updated if adding new commands (based on docstrings and type hints)
486
487---
488
489*This architecture document is maintained alongside the code and should be updated when significant changes are made to the provider's design or functionality.*
490