diff --git a/keyserver/src/deleters/siwe-nonce-deleters.js b/keyserver/src/deleters/siwe-nonce-deleters.js --- a/keyserver/src/deleters/siwe-nonce-deleters.js +++ b/keyserver/src/deleters/siwe-nonce-deleters.js @@ -9,9 +9,21 @@ const earliestValidCreationTime = Date.now() - nonceLifetime; const query = SQL` DELETE FROM siwe_nonces - WHERE creation_time < ${earliestValidCreationTime} + WHERE creation_time < ${earliestValidCreationTime} `; await dbQuery(query); } -export { deleteStaleSIWENonceEntries }; +async function checkAndInvalidateSIWENonceEntry( + nonce: string, +): Promise { + const earliestValidCreationTime = Date.now() - nonceLifetime; + const query = SQL` + DELETE FROM siwe_nonces + WHERE nonce = ${nonce} AND creation_time > ${earliestValidCreationTime} + `; + const [result] = await dbQuery(query); + return result.affectedRows && result.affectedRows > 0; +} + +export { deleteStaleSIWENonceEntries, checkAndInvalidateSIWENonceEntry }; diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js --- a/keyserver/src/responders/user-responders.js +++ b/keyserver/src/responders/user-responders.js @@ -47,6 +47,7 @@ import { dbQuery, SQL } from '../database/database'; import { deleteAccount } from '../deleters/account-deleters'; import { deleteCookie } from '../deleters/cookie-deleters'; +import { checkAndInvalidateSIWENonceEntry } from '../deleters/siwe-nonce-deleters.js'; import { fetchEntryInfos } from '../fetchers/entry-fetchers'; import { fetchMessageInfos } from '../fetchers/message-fetchers'; import { fetchThreadInfos } from '../fetchers/thread-fetchers'; @@ -316,6 +317,16 @@ throw new ServerError('invalid_parameters'); } + // 2. Ensure that the `nonce` exists in the `siwe_nonces` table + // AND hasn't expired. If those conditions are met, delete the entry to + // ensure that the same `nonce` can't be re-used in a future request. + const wasNonceCheckedAndInvalidated = await checkAndInvalidateSIWENonceEntry( + siweMessage.nonce, + ); + if (!wasNonceCheckedAndInvalidated) { + throw new ServerError('invalid_parameters'); + } + return false; }