From 6c7e8c2f28498da9a9ce719cd97d561e492ca12a Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 09:04:46 +0000 Subject: [PATCH 1/2] test(server): one-time pairing codes typed with separators are accepted Failing reproduction for #4889: a code typed as XXXX-XXXX-XXXX or with spaces is rejected as an unknown credential. AI-assisted; verified with the tests and tools listed above. --- apps/server/src/auth/PairingGrantStore.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/server/src/auth/PairingGrantStore.test.ts b/apps/server/src/auth/PairingGrantStore.test.ts index 5184beb096807..3f95ce46ec695 100644 --- a/apps/server/src/auth/PairingGrantStore.test.ts +++ b/apps/server/src/auth/PairingGrantStore.test.ts @@ -70,6 +70,22 @@ it.layer(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer))("PairingGrantStor ), ); + it.effect("accepts a one-time token typed in hyphen- or space-separated groups", () => + Effect.gen(function* () { + const bootstrapCredentials = yield* PairingGrantStore.PairingGrantStore; + const issued = yield* bootstrapCredentials.issueOneTimeToken(); + const formatted = issued.credential.match(/.{1,4}/g)!.join("-"); + const consumed = yield* bootstrapCredentials.consume(formatted.toLowerCase()); + const second = yield* bootstrapCredentials.issueOneTimeToken(); + const spaced = yield* bootstrapCredentials.consume( + second.credential.match(/.{1,4}/g)!.join(" "), + ); + + expect(consumed.method).toBe("one-time-token"); + expect(spaced.method).toBe("one-time-token"); + }).pipe(Effect.provide(layerPairingGrantStore())), + ); + it.effect("atomically consumes a one-time token when multiple requests race", () => Effect.gen(function* () { const bootstrapCredentials = yield* PairingGrantStore.PairingGrantStore; From 82370154833883e680185c784511f877334980c6 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 09:30:18 +0000 Subject: [PATCH 2/2] fix(server): pairing codes typed in hyphen- or space-separated groups are accepted A one-time pairing code typed as XXXX-XXXX-XXXX or with spaces was sent as typed and rejected as an unknown credential. Strip whitespace and hyphens before matching the issued 12-character code format. Fixes pingdotgg/t3code#4889 AI-assisted; verified with the tests and tools listed above. --- apps/server/src/auth/PairingGrantStore.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/server/src/auth/PairingGrantStore.ts b/apps/server/src/auth/PairingGrantStore.ts index bf9eccee8c51b..7af7f9ebc7a31 100644 --- a/apps/server/src/auth/PairingGrantStore.ts +++ b/apps/server/src/auth/PairingGrantStore.ts @@ -561,8 +561,10 @@ const make = Effect.gen(function* () { } // Only credentials in the issued pairing-code formats are case-insensitive. - const pairingCredential = PAIRING_TOKEN_PATTERN.test(credential) - ? credential.toUpperCase() + // People type codes in groups such as XXXX-XXXX-XXXX. + const compactCredential = credential.replace(/[\s-]/g, ""); + const pairingCredential = PAIRING_TOKEN_PATTERN.test(compactCredential) + ? compactCredential.toUpperCase() : (parseMcpApprovalCode(credential)?.code ?? credential); // Check scopes in the UPDATE so rejected requests cannot spend one-time links.