From 387d187885210acc8b88eb337b310390601540f4 Mon Sep 17 00:00:00 2001 From: weebney Date: Fri, 6 Dec 2024 14:28:31 -0500 Subject: [PATCH] add email token normalization --- .../src/account-manager/helpers/email-token.ts | 9 ++++++--- packages/pds/src/api/com/atproto/server/util.ts | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/pds/src/account-manager/helpers/email-token.ts b/packages/pds/src/account-manager/helpers/email-token.ts index 7d3690ad0e9..dae7bb2b2f2 100644 --- a/packages/pds/src/account-manager/helpers/email-token.ts +++ b/packages/pds/src/account-manager/helpers/email-token.ts @@ -1,6 +1,9 @@ import { InvalidRequestError } from '@atproto/xrpc-server' import { MINUTE, lessThanAgoMs } from '@atproto/common' -import { getRandomToken } from '../../api/com/atproto/server/util' +import { + getEmailToken, + normalizeEmailToken, +} from '../../api/com/atproto/server/util' import { AccountDb, EmailTokenPurpose } from '../db' export const createEmailToken = async ( @@ -8,7 +11,7 @@ export const createEmailToken = async ( did: string, purpose: EmailTokenPurpose, ): Promise => { - const token = getRandomToken().toUpperCase() + const token = getEmailToken() const now = new Date().toISOString() await db.executeWithRetry( db.db @@ -73,7 +76,7 @@ export const assertValidTokenAndFindDid = async ( .selectFrom('email_token') .selectAll() .where('purpose', '=', purpose) - .where('token', '=', token.toUpperCase()) + .where('token', '=', normalizeEmailToken(token)) .executeTakeFirst() if (!res) { throw new InvalidRequestError('Token is invalid', 'InvalidToken') diff --git a/packages/pds/src/api/com/atproto/server/util.ts b/packages/pds/src/api/com/atproto/server/util.ts index e6f52b8df59..a092c047b1e 100644 --- a/packages/pds/src/api/com/atproto/server/util.ts +++ b/packages/pds/src/api/com/atproto/server/util.ts @@ -22,12 +22,21 @@ export const genInvCodes = (cfg: ServerConfig, count: number): string[] => { return codes } -// Formatted xxxxx-xxxxx where digits are in base32 -export const getRandomToken = () => { - const token = crypto.randomStr(8, 'base32').slice(0, 10) +// Random token formatted XXXXX-XXXXX where digits are in base32 +export const getEmailToken = () => { + const token = crypto.randomStr(8, 'base32').slice(0, 10).toUpperCase() return token.slice(0, 5) + '-' + token.slice(5, 10) } +// Transforms a badly-formed email token to XXXXX-XXXXX +// (i.e from xxXxxxx-xxx or xxxxxxxxxx) +export const normalizeEmailToken = (input: string): string => { + let normalized = input.trim().toUpperCase() // trim & capitalize + normalized = input.replace('-', '') // remove the hyphen + normalized = normalized.slice(0, 5) + '-' + normalized.slice(5, 10) // replace the hyphen + return normalized +} + export const safeResolveDidDoc = async ( ctx: AppContext, did: string,