mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: add compatibility for fido2Credentials counter and implement no-op device token update handler
This commit is contained in:
@@ -5,6 +5,31 @@ import { generateUUID } from '../utils/uuid';
|
||||
import { deleteAllAttachmentsForCipher } from './attachments';
|
||||
import { parsePagination, encodeContinuationToken } from '../utils/pagination';
|
||||
|
||||
// Android 2026.2.0 expects fido2Credentials[].counter to be a string.
|
||||
export function normalizeCipherLoginForCompatibility(login: any): any {
|
||||
if (!login || typeof login !== 'object') return login ?? null;
|
||||
|
||||
const fido2 = Array.isArray(login.fido2Credentials)
|
||||
? login.fido2Credentials.map((cred: any) => {
|
||||
if (!cred || typeof cred !== 'object') return cred;
|
||||
const rawCounter = cred.counter;
|
||||
const counter =
|
||||
rawCounter === null || rawCounter === undefined
|
||||
? '0'
|
||||
: String(rawCounter);
|
||||
return {
|
||||
...cred,
|
||||
counter,
|
||||
};
|
||||
})
|
||||
: login.fido2Credentials;
|
||||
|
||||
return {
|
||||
...login,
|
||||
fido2Credentials: fido2,
|
||||
};
|
||||
}
|
||||
|
||||
// Format attachments for API response
|
||||
export function formatAttachments(attachments: Attachment[]): any[] | null {
|
||||
if (attachments.length === 0) return null;
|
||||
@@ -27,6 +52,7 @@ export function formatAttachments(attachments: Attachment[]): any[] | null {
|
||||
export function cipherToResponse(cipher: Cipher, attachments: Attachment[] = []): CipherResponse {
|
||||
// Strip internal-only fields that must not appear in the API response
|
||||
const { userId, createdAt, updatedAt, deletedAt, ...passthrough } = cipher;
|
||||
const normalizedLogin = normalizeCipherLoginForCompatibility((passthrough as any).login ?? null);
|
||||
|
||||
return {
|
||||
// Pass through ALL stored cipher fields (known + unknown)
|
||||
@@ -48,6 +74,7 @@ export function cipherToResponse(cipher: Cipher, attachments: Attachment[] = [])
|
||||
object: 'cipher',
|
||||
collectionIds: [],
|
||||
attachments: formatAttachments(attachments),
|
||||
login: normalizedLogin,
|
||||
encryptedFor: null,
|
||||
};
|
||||
}
|
||||
@@ -137,6 +164,7 @@ export async function handleCreateCipher(request: Request, env: Env, userId: str
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
};
|
||||
cipher.login = normalizeCipherLoginForCompatibility(cipher.login);
|
||||
|
||||
await storage.saveCipher(cipher);
|
||||
await storage.updateRevisionDate(userId);
|
||||
@@ -179,6 +207,7 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
|
||||
updatedAt: new Date().toISOString(),
|
||||
deletedAt: existingCipher.deletedAt,
|
||||
};
|
||||
cipher.login = normalizeCipherLoginForCompatibility(cipher.login);
|
||||
|
||||
await storage.saveCipher(cipher);
|
||||
await storage.updateRevisionDate(userId);
|
||||
|
||||
@@ -40,3 +40,19 @@ export async function handleGetDevices(request: Request, env: Env, userId: strin
|
||||
});
|
||||
}
|
||||
|
||||
// PUT /api/devices/identifier/{deviceIdentifier}/token
|
||||
// Bitwarden mobile reports push token updates to this endpoint.
|
||||
// NodeWarden does not implement push notifications, so accept and no-op.
|
||||
export async function handleUpdateDeviceToken(
|
||||
request: Request,
|
||||
env: Env,
|
||||
userId: string,
|
||||
deviceIdentifier: string
|
||||
): Promise<Response> {
|
||||
void request;
|
||||
void env;
|
||||
void userId;
|
||||
void deviceIdentifier;
|
||||
return new Response(null, { status: 200 });
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { StorageService } from '../services/storage';
|
||||
import { errorResponse } from '../utils/response';
|
||||
import { generateUUID } from '../utils/uuid';
|
||||
import { LIMITS } from '../config/limits';
|
||||
import { normalizeCipherLoginForCompatibility } from './ciphers';
|
||||
|
||||
// Bitwarden client import request format
|
||||
interface CiphersImportRequest {
|
||||
@@ -221,6 +222,7 @@ export async function handleCiphersImport(request: Request, env: Env, userId: st
|
||||
updatedAt: now,
|
||||
deletedAt: null,
|
||||
};
|
||||
cipher.login = normalizeCipherLoginForCompatibility(cipher.login);
|
||||
|
||||
cipherRows.push(cipher);
|
||||
}
|
||||
|
||||
+8
-1
@@ -38,7 +38,7 @@ import { handleSync } from './handlers/sync';
|
||||
|
||||
// Setup handlers
|
||||
import { handleSetupPage, handleSetupStatus, handleDisableSetup } from './handlers/setup';
|
||||
import { handleKnownDevice, handleGetDevices } from './handlers/devices';
|
||||
import { handleKnownDevice, handleGetDevices, handleUpdateDeviceToken } from './handlers/devices';
|
||||
|
||||
// Import handler
|
||||
import { handleCiphersImport } from './handlers/import';
|
||||
@@ -547,6 +547,13 @@ export async function handleRequest(request: Request, env: Env): Promise<Respons
|
||||
return handleGetDevices(request, env, userId);
|
||||
}
|
||||
|
||||
// Device push token endpoint (no-op compatibility handler)
|
||||
const deviceTokenMatch = path.match(/^\/api\/devices\/identifier\/([^/]+)\/token$/i);
|
||||
if (deviceTokenMatch && (method === 'PUT' || method === 'POST')) {
|
||||
const deviceIdentifier = decodeURIComponent(deviceTokenMatch[1]);
|
||||
return handleUpdateDeviceToken(request, env, userId, deviceIdentifier);
|
||||
}
|
||||
|
||||
// Not found
|
||||
return errorResponse('Not found', 404);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user