Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/middleware/idempotency.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NextFunction, Request, Response } from 'express';
import type { NextFunction, Request, Response, RequestHandler } from 'express';
import type { Pool } from 'pg';
import { createHash } from 'crypto';
import { config } from '../config/index.js';
Expand Down Expand Up @@ -342,3 +342,15 @@ export async function idempotencyMiddleware(
next(error);
}
}

/**
* Factory that returns a 3-parameter Express RequestHandler for idempotencyMiddleware.
* Express treats 4-parameter functions as error handlers, so this wrapper ensures
* Express dispatches the middleware during normal request processing.
*/
export function createIdempotencyMiddleware(opts?: IdempotencyConfig): RequestHandler {
return (req: Request, res: Response, next: NextFunction) => {
idempotencyMiddleware(req, res, next, opts);
};
}

13 changes: 12 additions & 1 deletion src/routes/refresh-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
* - Token hashes are never exposed in the response
*/

import { Router } from 'express';
import { Router, type Request } from 'express';
import { requireAuth } from '../middleware/requireAuth.js';
import { correlationMiddleware } from '../middleware/correlation.js';
import { idempotencyMiddleware } from '../middleware/idempotency.js';
import { getClientIp, DEFAULT_PROXY_HEADERS } from '../lib/clientIp.js';
import { encodeCursor, parseCursor } from '../lib/cursorPagination.js';
import {
Expand Down Expand Up @@ -45,6 +46,7 @@ export interface RefreshTokenRouterDeps {
refreshTokenRepository?: RefreshTokenRepository;
rateLimitMiddleware?: RequestHandler;
rateLimiter?: TokenBucketRateLimiter;
idempotencyMiddleware?: RequestHandler;
}

export function createRefreshTokenRouter(deps: RefreshTokenRouterDeps = {}): Router {
Expand All @@ -58,6 +60,15 @@ export function createRefreshTokenRouter(deps: RefreshTokenRouterDeps = {}): Rou
deps.rateLimiter,
);

const idempotencyHandler: RequestHandler =
deps.idempotencyMiddleware ??
((req, res, next) =>
idempotencyMiddleware(req, res, next, {
methods: ['POST', 'PATCH'],
}));

router.use(idempotencyHandler);

/**
* GET /api/refresh-token
*
Expand Down
Loading