feat: identity protection UI, duplicate guards, name-lock enforcement (v2.4.6)

- Add password set/change/remove with loading states and double-click prevention
- Add protected identity deletion flow (server-side password removal first)
- Add restore flow for password-protected UUIDs (verify password before saving)
- Add UUID duplicate checks in setUuidForUser (prevent accidental overwrites)
- Add name-locked error handling in launch flow (server enforces registered name)
- Sync shield icon across all identity mutation paths
- Refresh identity dropdown after all password/identity operations
- Propagate force flag through IPC for legitimate overwrites

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sanasol
2026-02-28 18:22:40 +01:00
parent 0b861904ba
commit 7347910fe9
7 changed files with 558 additions and 73 deletions

View File

@@ -82,6 +82,12 @@ async function fetchAuthTokens(uuid, name, password) {
err.usernameTaken = true;
throw err;
}
if (response.status === 403 && errBody.name_locked) {
const err = new Error(`This UUID is locked to username "${errBody.registeredName}". Change your identity name to match.`);
err.nameLocked = true;
err.registeredName = errBody.registeredName;
throw err;
}
throw new Error(`Auth server returned ${response.status}`);
}
@@ -123,7 +129,7 @@ async function fetchAuthTokens(uuid, name, password) {
return { identityToken, sessionToken };
} catch (error) {
// Re-throw authentication errors — must not fall back to local tokens
if (error.passwordRequired || error.lockedOut || error.usernameTaken) {
if (error.passwordRequired || error.lockedOut || error.usernameTaken || error.nameLocked) {
throw error;
}
console.error('Failed to fetch auth tokens:', error.message);
@@ -694,7 +700,7 @@ async function launchGameWithVersionCheck(playerNameOverride = null, progressCal
progressCallback(`Error: ${error.message}`, -1, null, null, null);
}
// Re-throw authentication errors so IPC handler can return proper flags
if (error.passwordRequired || error.lockedOut || error.usernameTaken) {
if (error.passwordRequired || error.lockedOut || error.usernameTaken || error.nameLocked) {
throw error;
}
// Always return an error response instead of throwing