* fix(lastfm): require signed state token on link callback
The Last.fm OAuth callback at /api/lastfm/link/callback trusted a raw
\`uid\` query parameter and wrote the resulting Last.fm session key under
that user with no ownership check. Any authenticated user who learned a
victim's internal user ID (e.g. from playlist ownerId) could redirect the
victim's scrobbles to an attacker-controlled Last.fm account by calling
the callback directly with the victim's uid and a Last.fm token obtained
for their own account.
The callback cannot use the regular auth middleware because it is reached
via a browser redirect from Last.fm, which cannot carry a JWT header.
Instead, GET /api/lastfm/link (authenticated) now also returns a short-
lived (5 min) HMAC-signed link token bound to the requesting user, with a
dedicated "lastfm-link" scope claim. The callback verifies the signature,
scope and expiry before deriving the user ID from the token; the \`uid\`
query value is no longer trusted as a user identifier. The UI fetches
this token at link-flow start and passes it in place of the raw user ID.
Reuses the existing HS256 secret via auth.EncodeToken/DecodeAndVerifyToken
so no new key management is introduced.
* fix(ui): keep Last.fm popup tied to user gesture for Safari
Opening the Last.fm OAuth tab after an awaited fetch causes the popup to
be blocked on Safari and on Firefox with strict popup blocking enabled,
because the browser's transient-activation window has already elapsed by
the time window.open is reached. Linking became impossible on those
browsers in the previous commit.
Move the click handler up to the parent component and open a placeholder
about:blank tab synchronously from the click; the linkToken fetch then
runs in parallel and we redirect the existing tab to Last.fm's auth URL
once it resolves. The user gesture stays attached to the window.open
call, so popup blockers no longer fire.
The polling/progress UI is unchanged; it now receives the openedTab ref
from the parent instead of owning it.
* fix(lastfm): require exp claim on link tokens
jwtauth.VerifyToken treats a JWT without an exp claim as non-expiring, so
verifyLinkToken used to delegate expiry handling entirely. A future
regression in createLinkToken that dropped the exp field would silently
turn link tokens into permanent bearer credentials.
Assert presence of an exp claim explicitly and add a regression test
covering the missing-exp case. Also tightens the wrong-scope test to use
a freshly-minted token with all claims present except the scope, instead
of relying on auth.CreatePublicToken which happens to also be missing
exp.
* style(lastfm): simplify comments in link token code
Trim doc comments on createLinkToken/verifyLinkToken/callback/startLink
to the load-bearing lines: keep the non-obvious 'jwtauth treats missing
exp as non-expiring' note and the popup-blocker hint, drop the rest
since the function names already describe behavior.
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(lastfm): address review feedback on link token PR
- Wrap openInNewTab in a try/catch in startLink: openInNewTab calls
win.focus() unconditionally, so if the browser blocks the popup
(window.open returns null) it throws a TypeError synchronously,
before the catch() on the link-token fetch is attached. The throw
used to escape the click handler, leaving the UI without a
notification. Now the failure is surfaced as lastfmLinkFailure and
the toggle stays usable.
- Rename the link-token "subject" rejection message to "user ID" since
the claim is uid, not the JWT sub field.
---------
Signed-off-by: Deluan <deluan@navidrome.org>
* bug(core/playback/mpv): jukebox mode under windows - #2767
Use named pipe for socket path under windows during mpv playback, change function name, unexport function
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - #2767
Fix typo
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - navidrome#2767
Early return for Close on Windows
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - navidrome#2767
Update import and run prettier
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - navidrome#2767
Update function name
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - navidrome#2767
Create track_close files for both platforms and move MpvTrack Close into new file
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - navidrome#2767
Create SocketName function for both platforms, restore name of TempFileName
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* bug(core/playback/mpv): jukebox mode under windows - navidrome#2767
Add missing params to SocketName on windows
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* Unexport SocketName, use socketName in NewTrack
---------
Signed-off-by: apkatsikas <apkatsikas@gmail.com>
* ReplayGain support
- extract ReplayGain tags from files, expose via native api
- use metadata to normalize audio in web player
* make pre-push happy
* remove unnecessary prints
* remove another unnecessary print
* add tooltips, see metadata
* address comments, use settings instead
* remove console.log
* use better language for gain modes
* Refactor session_keys to its own package
* Adjust play_tracker
- Don't send external NowPlaying/Scrobble for tracks with unknown artist
- Continue to the next agent on error
* Implement ListenBrainz Agent and Auth Router
* Implement frontend for ListenBrainz linking
* Update listenBrainzRequest
- Don't marshal Player to json
- Rename Track to Title
* Return ErrRetryLater on ListenBrainz server errors
* Add tests for listenBrainzAgent
* Add tests for ListenBrainz Client
* Adjust ListenBrainzTokenDialog to handle errors better
* Refactor listenbrainz.formatListen and listenBrainzRequest structs
* Refactor agent auth_routers
* Refactor session_keys to agents package
* Add test for listenBrainzResponse
* Add tests for ListenBrainz auth_router
* Update ListenBrainzTokenDialog and auth_router
* Adjust player scrobble toggle
* Auto theme preference added
* Fix lint
* Add and use AUTO from consts
* Add shared custom hook to get current theme
* Moved up 'Auto' choice
* AUTO -> AUTO_THEME_ID & extract useCurrentTheme to file
* Liberalise theme setting
* Add tests