diff --git a/src/servers/api/vault/router.ts b/src/servers/api/vault/router.ts index 4fe9af78..b3833069 100644 --- a/src/servers/api/vault/router.ts +++ b/src/servers/api/vault/router.ts @@ -83,7 +83,19 @@ vaultRouter.post('/session/login', async (ctx) => { clientId, }); - // Ciphertext to us — passed straight to the client, which decrypts on-device with the master key. + // TEMP diagnostic — field NAMES only (values redacted) to empirically confirm the crypto shape. + const names = (o: unknown) => (o && typeof o === 'object' ? Object.keys(o as object) : typeof o); + const udo = (d as { UserDecryptionOptions?: { MasterPasswordUnlock?: unknown } }).UserDecryptionOptions; + console.log( + `[vault] connect/token fields: ${Object.keys(d).join(',')} | UserDecryptionOptions=${JSON.stringify(names(udo))} | MasterPasswordUnlock=${JSON.stringify(names(udo?.MasterPasswordUnlock))} | AccountKeys=${JSON.stringify(names((d as { AccountKeys?: unknown }).AccountKeys))}`, + ); + + // Everything except the transport tokens (which the platform holds) is crypto material the SDK unlocks + // on-device — ciphertext to us. Vaultwarden returns BOTH classic (Key/PrivateKey/Kdf*) and v2 + // (UserDecryptionOptions.MasterPasswordUnlock, AccountKeys); pass the whole native response through + // under `connectToken` so the SDK gets whatever it uses, alongside the spec's named aliases. + const connectToken: Record = { ...d }; + for (const k of ['access_token', 'refresh_token', 'token_type', 'expires_in']) delete connectToken[k]; return ctx.json({ ok: true, protectedUserKey: pick(d, 'Key') ?? null, @@ -94,6 +106,7 @@ vaultRouter.post('/session/login', async (ctx) => { kdfMemory: pick(d, 'KdfMemory') ?? null, kdfParallelism: pick(d, 'KdfParallelism') ?? null, }, + connectToken, }); });