fix(auth): require a hashed poll secret for desktop handoff (#2357)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-02 17:25:54 +02:00
1 parent b82681b77a
commit f703969e80
45 files changed
+382 -188

No files matched your search

@@ -227,6 +227,7 @@ export type UsernameSuggestionsResponse = z.infer<typeof UsernameSuggestionsResp
export const HandoffInitiateResponse = z.object({
code: z.string().describe('Handoff code to share with the receiving device'),
expires_at: z.iso.datetime().describe('ISO 8601 timestamp when the handoff code expires'),
poll_secret: z.string().optional().describe('Secret the initiating device must present to retrieve the token'),
});
export type HandoffInitiateResponse = z.infer<typeof HandoffInitiateResponse>;
@@ -331,6 +332,18 @@ export const HandoffCodeParam = z.object({
export type HandoffCodeParam = z.infer<typeof HandoffCodeParam>;
export const HandoffStatusRequest = z.object({
poll_secret: createStringType().describe('The poll secret issued when the handoff was initiated'),
});
export type HandoffStatusRequest = z.infer<typeof HandoffStatusRequest>;
export const HandoffCancelRequest = z.object({
poll_secret: createStringType().describe('The poll secret issued when the handoff was initiated'),
});
export type HandoffCancelRequest = z.infer<typeof HandoffCancelRequest>;
export const EnableMfaTotpRequest = z
.object({
secret: createStringType(1, 256).describe('The TOTP secret key'),
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export const DESKTOP_HANDOFF_CODE_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
export const DESKTOP_HANDOFF_CODE_LENGTH = 12;
const DESKTOP_HANDOFF_CODE_GROUP_LENGTH = 6;
const DESKTOP_HANDOFF_CODE_SEPARATOR = '-';
const DESKTOP_HANDOFF_CODE_SEPARATOR_PATTERN = /[^A-Za-z0-9]/gu;
function normalizeDesktopHandoffCode(value: string): string {
return value.replace(DESKTOP_HANDOFF_CODE_SEPARATOR_PATTERN, '').toUpperCase();
}
export function isDesktopHandoffCode(value: string): boolean {
if (value.length !== DESKTOP_HANDOFF_CODE_LENGTH) {
return false;
}
return Array.from(value).every((character) => DESKTOP_HANDOFF_CODE_ALPHABET.includes(character));
}
export function parseDesktopHandoffCodeInput(value: string): string {
return normalizeDesktopHandoffCode(value).slice(0, DESKTOP_HANDOFF_CODE_LENGTH);
}
export function parseDesktopHandoffCode(value: string | null | undefined): string | null {
if (value == null) {
return null;
}
const normalized = normalizeDesktopHandoffCode(value);
if (!isDesktopHandoffCode(normalized)) {
return null;
}
return normalized;
}
export function formatDesktopHandoffCode(value: string): string {
const normalized = parseDesktopHandoffCodeInput(value);
const groups: Array<string> = [];
for (let index = 0; index < normalized.length; index += DESKTOP_HANDOFF_CODE_GROUP_LENGTH) {
groups.push(normalized.slice(index, index + DESKTOP_HANDOFF_CODE_GROUP_LENGTH));
}
return groups.join(DESKTOP_HANDOFF_CODE_SEPARATOR);
}