4.0 KiB
4.0 KiB
Authentication
Session-token authentication for the signaling server and product client.
Trust boundaries
| Surface | Identity proof | Notes |
|---|---|---|
| Signaling server REST (mutations) | Authorization: Bearer <token> |
Actor user IDs in request bodies are ignored; server derives authUserId from the token |
| Signaling server REST (discovery) | None | GET /api/servers, featured/trending/search remain public |
| Signaling server WebSocket | identify.token |
Connections must identify before any other message type |
| Electron Local API | Separate in-memory bearer tokens | Proxies login to allowed signaling servers only |
| Product client local DB | OS user account | SQLite and attachments are plaintext at rest |
Login / register response
{
"id": "<uuid>",
"username": "alice",
"displayName": "Alice",
"token": "<opaque-hex>",
"expiresAt": 1710000000000
}
- Tokens are opaque 64-character hex strings stored in server SQLite (
session_tokens). - Default TTL: 10 years (
SESSION_TOKEN_TTL_MSenv override supported on the signaling server). - Passwords are stored with bcrypt; legacy SHA-256 hashes are upgraded transparently on successful login.
Protected REST routes
Require Authorization: Bearer:
PUT/POST/DELETEunder/api/servers/*(except publicGET)PUT /api/requests/:id- Plugin-support mutations under
/api/servers/:serverId/plugins/* /api/users/device-tokens/*POST /api/users/logout
WebSocket identify contract
{
"type": "identify",
"token": "<session-token>",
"oderId": "<user-id>",
"displayName": "Alice",
"connectionScope": "ws://host:3001",
"clientInstanceId": "<per-install-uuid>"
}
oderIdmust match the token's user id when provided.clientInstanceIdis a stable per-install UUID generated by the product client (metoyou.clientInstanceIdinlocalStorage). The signaling server uses it to distinguish multiple WebSocket connections for the same user and to route voice ownership.- Server responds with
auth_errororauth_requiredwhen authentication fails.
Multi-device sessions
- Each login/register issues a new session token; prior tokens remain valid until they expire or the client calls
POST /api/users/logoutwith that token. - The same user may keep multiple WebSocket connections open (different devices or browser profiles). Server broadcasts (chat, typing, voice state, status) exclude only the sending connection, so other connections for that identity still receive updates.
- Voice/WebRTC is exclusive per user: only one
clientInstanceIdmay own active voice at a time. Other connections show passive UI and can sendvoice_client_takeoverto move voice to the local device. - Stale reconnect hygiene: when a client re-identifies with the same
(oderId, connectionScope, clientInstanceId)tuple, the server closes the older socket for that tuple.
Client storage
The product client stores tokens per signaling-server base URL in localStorage (metoyou.authTokens). An HTTP interceptor attaches the bearer token to /api/* requests targeting that server.
Persisted local user state (metoyou_currentUserId + IndexedDB/SQLite profile) is not sufficient to use chat or presence. On startup, loadCurrentUser$ requires a non-expired session token for the user's home/active signaling server (or any stored token as a fallback). Missing or rejected tokens dispatch SESSION_EXPIRED and redirect to /login. WebSocket auth_required / auth_error responses trigger the same path.
Security considerations
- Rate limits: login/register (100 / 15 min), server join (30 / min).
- CORS allowlist: optional
corsAllowlistinserver/data/variables.jsonorCORS_ALLOWLISTenv (comma-separated). Empty allowlist keeps permissive CORS for local development. - Push-token routes require bearer auth and user-id match.
- RTC relay: direct-message/direct-call types always relay; server-icon types require shared server membership; WebRTC offer/answer/ice remain open for cross-server DM WebRTC.