05/17 enhance codes
This commit is contained in:
@@ -235,6 +235,13 @@ function isMatch(item) {
|
||||
|
||||
// ── API helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
// Singleton promise for the in-flight token refresh.
|
||||
// When a 401 triggers a refresh, all concurrent requests that also receive a
|
||||
// 401 await this same promise instead of starting their own — preventing the
|
||||
// second refresh from using an already-rotated (and therefore blacklisted)
|
||||
// refresh token, which would cause an unexpected sign-out.
|
||||
let _refreshPromise = null;
|
||||
|
||||
async function apiFetch(path, options = {}) {
|
||||
const { access_token } = await chrome.storage.session.get("access_token");
|
||||
const headers = {
|
||||
@@ -246,7 +253,13 @@ async function apiFetch(path, options = {}) {
|
||||
let res = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
||||
|
||||
if (res.status === 401) {
|
||||
const refreshed = await tryRefreshToken();
|
||||
// Coalesce concurrent 401 retries onto a single refresh attempt.
|
||||
if (!_refreshPromise) {
|
||||
_refreshPromise = tryRefreshToken().finally(() => {
|
||||
_refreshPromise = null;
|
||||
});
|
||||
}
|
||||
const refreshed = await _refreshPromise;
|
||||
if (!refreshed) {
|
||||
signOut();
|
||||
return null;
|
||||
@@ -899,7 +912,9 @@ function renderList() {
|
||||
if (!item?.plain?.totp_uri) return;
|
||||
const code = await getTotpCode(item.plain.totp_uri).catch(() => null);
|
||||
if (code) {
|
||||
navigator.clipboard.writeText(code);
|
||||
// Use _copyWithAutoClear so the 2FA code is wiped from the clipboard
|
||||
// after 30 s, consistent with password copy behaviour.
|
||||
_copyWithAutoClear(code);
|
||||
btn.title = "Copied!";
|
||||
btn.style.color = "#16a34a";
|
||||
setTimeout(() => {
|
||||
@@ -1163,7 +1178,7 @@ async function saveCredential(data) {
|
||||
const res = await apiFetch("/api/vault", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
name: "password",
|
||||
name, // actual site name (plaintext fallback for legacy clients)
|
||||
item_type: "password",
|
||||
folder_id,
|
||||
enc_data,
|
||||
@@ -1232,9 +1247,9 @@ function generatePassword(length, useLower, useUpper, useNumbers, useSymbols) {
|
||||
if (useSymbols)
|
||||
required.push(GEN_SETS.symbols[_cryptoRandInt(GEN_SETS.symbols.length)]);
|
||||
|
||||
const arr = new Uint32Array(length);
|
||||
crypto.getRandomValues(arr);
|
||||
const rest = Array.from(arr).map((n) => pool[n % pool.length]);
|
||||
// Use _cryptoRandInt() for each character to avoid modulo bias that arises
|
||||
// when pool.length is not a power of 2.
|
||||
const rest = Array.from({ length }, () => pool[_cryptoRandInt(pool.length)]);
|
||||
|
||||
// Splice required chars into random positions and trim to length.
|
||||
const combined = [...rest];
|
||||
@@ -1462,7 +1477,7 @@ async function addItemToVault() {
|
||||
const res = await apiFetch("/api/vault", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
name: "password",
|
||||
name, // actual site name (plaintext fallback for legacy clients)
|
||||
item_type: "password",
|
||||
folder_id,
|
||||
enc_data,
|
||||
|
||||
Reference in New Issue
Block a user