From 5516453b7ff01af617f11f86550fc9c4a4d53567 Mon Sep 17 00:00:00 2001 From: wdunn001 Date: Fri, 14 Aug 2026 12:21:57 -0400 Subject: [PATCH] server: tell pre-0.4 clients they are outdated instead of a bare 403 Old clients pair fine (the handshake is unauthenticated) then fail every authenticated call with a message-less 403, which the desktop app renders as an opaque 'Unrecoverable error'. Gate /client/auth/initiate on the User-Agent the 0.4+ remote crate always sends, and give the non-JWT auth fallthrough an explicit statusMessage/message pointing at cdn.quasarke.net/drop-client. --- server/server/api/v1/client/auth/initiate.post.ts | 15 +++++++++++++++ server/server/internal/clients/event-handler.ts | 9 ++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/server/server/api/v1/client/auth/initiate.post.ts b/server/server/api/v1/client/auth/initiate.post.ts index dddc71e6..fe55073c 100644 --- a/server/server/api/v1/client/auth/initiate.post.ts +++ b/server/server/api/v1/client/auth/initiate.post.ts @@ -18,6 +18,21 @@ const ClientAuthInitiate = type({ }).configure(throwingArktype); export default defineEventHandler(async (h3) => { + // Version gate. Pre-0.4 desktop clients send no User-Agent (0.4+ sends + // "Drop Desktop Client" from the shared remote crate) and use a Nonce auth + // scheme this server no longer speaks. Without this check they pair + // successfully — the handshake is unauthenticated — and then every + // authenticated call 403s, which surfaces as an opaque "Unrecoverable + // error" after sign-in (zack.way, 2026-08-14). Failing here instead hits + // the old client's handshake error path, which DOES display `message`. + const userAgent = getHeader(h3, "User-Agent"); + if (!userAgent) + throw createError({ + statusCode: 400, + message: + "Your Drop client is too old for this server (it uses the pre-0.4 auth protocol). Download the current client from https://cdn.quasarke.net/drop-client/", + }); + const body = await readDropValidatedBody(h3, ClientAuthInitiate); const platformRaw = body.platform; diff --git a/server/server/internal/clients/event-handler.ts b/server/server/internal/clients/event-handler.ts index 103c7579..9295bd68 100644 --- a/server/server/internal/clients/event-handler.ts +++ b/server/server/internal/clients/event-handler.ts @@ -54,9 +54,16 @@ export function defineClientEventHandler(handler: EventHandlerFunction) { break; } default: { + // Pre-0.4 clients authenticate with a "Nonce ..." scheme this server + // no longer speaks. Their pairing handshake still succeeds (it is + // unauthenticated), so this is the first place they fail — and they + // render statusMessage, not message, so both carry the explanation. throw createError({ statusCode: 403, - message: "No authentication", + statusMessage: + "Drop client too old for this server - get the current client at cdn.quasarke.net/drop-client", + message: + "Your Drop client is too old for this server (it uses the pre-0.4 auth protocol). Download the current client from https://cdn.quasarke.net/drop-client/", }); } }