chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// @pdsjs/spaces/single-use - spending a token identifier once.
//
// A delegation token, a client attestation and a DPoP proof each authorize one
// exchange. Nothing in their signature stops a second presentation, so the
// server remembers the identifier until the token expires and refuses it after
// the first use.
//
// Held in memory, which is sound for the same reason the write queue in
// writer.js is: one space is served by exactly one process, a Node PDS or a
// single Durable Object.
/** @type {Map<string, number>} scoped identifier -> the instant it may be forgotten */
const spent = new Map();
/**
* Spend an identifier, answering whether this is the first time it was seen.
*
* Expired entries are dropped on each call, so the map holds one token
* lifetime's worth of traffic rather than growing without bound.
*
* @param {string} scope - what the identifier is unique within, e.g. an issuer
* @param {string} id - the token's `jti`
* @param {number} expiresAt - epoch ms after which a replay fails on its own
* @returns {boolean} false when the identifier was already spent
*/
export function spend(scope, id, expiresAt) {
const now = Date.now();
for (const [key, forgetAt] of spent) {
if (forgetAt <= now) spent.delete(key);
}
const key = `${scope}:${id}`;
if (spent.has(key)) return false;
spent.set(key, expiresAt);
return true;
}