fix(auth): the Subsonic password is generated, never the login password (M462 #5026)
release / govulncheck (push) Successful in 39s
release / web (push) Successful in 1m8s
release / go (push) Successful in 1m30s
release / integration (push) Successful in 4m37s
release / android (push) Successful in 5m56s
release / Build signed APK (releases and dev) (push) Successful in 5m55s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m23s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 39s
release / web (push) Successful in 1m8s
release / go (push) Successful in 1m30s
release / integration (push) Successful in 4m37s
release / android (push) Successful in 5m56s
release / Build signed APK (releases and dev) (push) Successful in 5m55s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m23s
release / Verify release artifacts (tag releases only) (push) Skipped
`minstrel admin reset-password` copied the new login password into subsonic_password, which is stored in plain text because Subsonic t/s sign-in needs it. Every account recovered through the CLI had its login password readable in the database, and changing the password later left the copy behind. - reset-password now changes only password_hash. - Migration 0064 clears every subsonic_password, removing the copies. - Settings gets a Subsonic password card: the server generates a random password, shows it once, and it can be regenerated or turned off (GET/POST/DELETE /api/me/subsonic-password, audited). Generated rather than user-chosen so it can never be a reused password. - docs/security.md describes the separate password instead of the known issue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,25 @@ export async function regenerateAPIToken(): Promise<APITokenResponse> {
|
||||
return api.post<APITokenResponse>('/api/me/api-token', {});
|
||||
}
|
||||
|
||||
// Subsonic password (#5026) ------------------------------------------------
|
||||
// For Subsonic clients that only sign in with a username and password (the
|
||||
// t/s scheme). The server generates it, so it is never the login password;
|
||||
// like the API key it is shown once, when generated.
|
||||
|
||||
export type SubsonicPasswordStatus = { enabled: boolean };
|
||||
|
||||
export async function getSubsonicPasswordStatus(): Promise<SubsonicPasswordStatus> {
|
||||
return api.get<SubsonicPasswordStatus>('/api/me/subsonic-password');
|
||||
}
|
||||
|
||||
export async function generateSubsonicPassword(): Promise<{ password: string }> {
|
||||
return api.post<{ password: string }>('/api/me/subsonic-password', {});
|
||||
}
|
||||
|
||||
export async function clearSubsonicPassword(): Promise<void> {
|
||||
await api.del('/api/me/subsonic-password');
|
||||
}
|
||||
|
||||
// Submits the browser's current IANA timezone for the authenticated
|
||||
// user. Called from the auth store on login + bootstrap + once weekly
|
||||
// (cadence tracked client-side in localStorage). Failures are
|
||||
|
||||
Reference in New Issue
Block a user