Handle flexible auth provider IDs in cache

This commit is contained in:
thePR0M3TH3AN committed 2025-10-21 09:59:46 -04:00
1 parent 147e57845b
commit 3d1486ef79
3 files changed
+53 -30

No files matched your search

+14 -12
View File
@@ -9,7 +9,8 @@ updates can evolve it without breaking existing users.
- `bitvid:profileCache:v1` – short-lived metadata cache for profile names and
avatars. See `js/app.js` for TTL and eviction rules.
- `bitvid:savedProfiles:v1` – persistent list of accounts the user authenticated
with in this browser.
with in this browser. The payload embeds a `version` field that is currently
`2`.
Both keys live in the main origin scope. Clearing one should not affect the
other.
@@ -20,14 +21,14 @@ other.
```json
{
"version": 1,
"version": 2,
"entries": [
{
"pubkey": "<hex-encoded pubkey>",
"npub": "<bech32 npub, optional>",
"name": "<cached display name>",
"picture": "<cached avatar URL>",
"authType": "<auth strategy>"
"authType": "<auth strategy or null>"
}
],
"activePubkey": "<hex pubkey or null>"
@@ -46,17 +47,18 @@ Notes:
- `activePubkey` tracks the last account that successfully authenticated in this
browser. It may be `null` when the user logs out but keeps saved entries.
### `authType` enum
### `authType` handling
`authType` describes how the profile authenticated:
`authType` describes how the profile authenticated and is stored exactly as the
provider reported it (after trimming whitespace). Bitvid does not attempt to
normalise these strings, so future auth integrations can safely persist custom
identifiers.
- `"nip07"` – Browser extension flow (current default).
- `"nsec"` – Reserved for future direct key import or signer integrations. New
auth strategies should pick a distinct string and document how migration works
alongside any recovery tooling.
When the app reads stored entries it normalises unknown values back to
`"nip07"` but keeps recognised alternatives intact.
- Legacy payloads (version `1`) did not include the field, so the loader treats
those entries as `"nip07"` and rewrites the payload at version `2`.
- Newer providers may surface alternative IDs (for example, Wallet Connect
flows). UI components use provider metadata when available and otherwise fall
back to a generic label so unknown identifiers do not break rendering.
## Migration notes
+38 -17
View File
@@ -9,7 +9,7 @@ const PROFILE_CACHE_VERSION = 1;
const PROFILE_CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
const SAVED_PROFILES_STORAGE_KEY = "bitvid:savedProfiles:v1";
const SAVED_PROFILES_STORAGE_VERSION = 1;
const SAVED_PROFILES_STORAGE_VERSION = 2;
const URL_HEALTH_TTL_MS = 45 * 60 * 1000; // 45 minutes
const URL_HEALTH_TIMEOUT_RETRY_MS = 5 * 60 * 1000; // 5 minutes
@@ -261,17 +261,19 @@ export function persistSavedProfiles({ persistActive = true } = {}) {
const payload = {
version: SAVED_PROFILES_STORAGE_VERSION,
entries: savedProfiles.map((entry) => ({
pubkey: entry.pubkey,
npub:
typeof entry.npub === "string" && entry.npub.trim() ? entry.npub.trim() : null,
name: typeof entry.name === "string" ? entry.name : "",
picture: typeof entry.picture === "string" ? entry.picture : "",
authType:
typeof entry.authType === "string" && entry.authType.trim()
? entry.authType.trim()
: "nip07",
})),
entries: savedProfiles.map((entry) => {
const sanitizedAuthType =
typeof entry.authType === "string" ? sanitizeProfileString(entry.authType) : "";
return {
pubkey: entry.pubkey,
npub:
typeof entry.npub === "string" && entry.npub.trim() ? entry.npub.trim() : null,
name: typeof entry.name === "string" ? entry.name : "",
picture: typeof entry.picture === "string" ? entry.picture : "",
authType: sanitizedAuthType || null,
};
}),
activePubkey: activePubkeyToPersist,
};
@@ -294,7 +296,19 @@ export function loadSavedProfilesFromStorage() {
let needsRewrite = false;
try {
const parsed = JSON.parse(raw);
if (parsed && typeof parsed === "object" && parsed.version === SAVED_PROFILES_STORAGE_VERSION) {
const parsedVersion =
parsed && typeof parsed === "object" && typeof parsed.version === "number"
? parsed.version
: 1;
if (
parsed &&
typeof parsed === "object" &&
parsedVersion <= SAVED_PROFILES_STORAGE_VERSION &&
parsed.entries
) {
if (parsedVersion !== SAVED_PROFILES_STORAGE_VERSION) {
needsRewrite = true;
}
const entries = Array.isArray(parsed.entries) ? parsed.entries : [];
const seenPubkeys = new Set();
for (const candidate of entries) {
@@ -309,10 +323,13 @@ export function loadSavedProfilesFromStorage() {
}
seenPubkeys.add(normalizedPubkey);
const storedAuthType =
typeof candidate.authType === "string" && candidate.authType.trim()
? candidate.authType.trim()
: "nip07";
const storedAuthTypeRaw =
typeof candidate.authType === "string" ? sanitizeProfileString(candidate.authType) : "";
const isLegacyEntry = !("authType" in candidate) || parsedVersion < 2;
const storedAuthType = storedAuthTypeRaw || (isLegacyEntry ? "nip07" : null);
if (isLegacyEntry && storedAuthType !== candidate.authType) {
needsRewrite = true;
}
const npub =
typeof candidate.npub === "string" && candidate.npub.trim()
? candidate.npub.trim()
@@ -336,6 +353,10 @@ export function loadSavedProfilesFromStorage() {
needsRewrite = true;
}
if (storedAuthTypeRaw && storedAuthTypeRaw !== candidate.authType) {
needsRewrite = true;
}
savedProfiles.push(entry);
}
+1 -1
View File
@@ -39,7 +39,7 @@ function resolveAuthTypeLabel(authType) {
return "Saved profile";
}
return trimmed;
return `Connected via ${trimmed}`;
}
const DEFAULT_INTERNAL_NWC_SETTINGS = Object.freeze({