Enforce 10-minute TTL for generated company invites

This commit is contained in:
Dotta
2026-03-06 10:10:23 -06:00
parent c3ac209e5f
commit 0f895a8cf9
6 changed files with 19 additions and 7 deletions

View File

@@ -0,0 +1,10 @@
import { describe, expect, it } from "vitest";
import { companyInviteExpiresAt } from "../routes/access.js";
describe("companyInviteExpiresAt", () => {
it("sets invite expiration to 10 minutes after invite creation time", () => {
const createdAtMs = Date.parse("2026-03-06T00:00:00.000Z");
const expiresAt = companyInviteExpiresAt(createdAtMs);
expect(expiresAt.toISOString()).toBe("2026-03-06T00:10:00.000Z");
});
});

View File

@@ -37,6 +37,7 @@ const INVITE_TOKEN_PREFIX = "pcp_invite_";
const INVITE_TOKEN_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
const INVITE_TOKEN_SUFFIX_LENGTH = 8;
const INVITE_TOKEN_MAX_RETRIES = 5;
const COMPANY_INVITE_TTL_MS = 10 * 60 * 1000;
function createInviteToken() {
const bytes = randomBytes(INVITE_TOKEN_SUFFIX_LENGTH);
@@ -51,6 +52,10 @@ function createClaimSecret() {
return `pcp_claim_${randomBytes(24).toString("hex")}`;
}
export function companyInviteExpiresAt(nowMs: number = Date.now()) {
return new Date(nowMs + COMPANY_INVITE_TTL_MS);
}
function tokenHashesMatch(left: string, right: string) {
const leftBytes = Buffer.from(left, "utf8");
const rightBytes = Buffer.from(right, "utf8");
@@ -1102,7 +1107,7 @@ export function accessRoutes(
inviteType: "company_join" as const,
allowedJoinTypes: req.body.allowedJoinTypes,
defaultsPayload: mergeInviteDefaults(req.body.defaultsPayload ?? null, normalizedAgentMessage),
expiresAt: new Date(Date.now() + req.body.expiresInHours * 60 * 60 * 1000),
expiresAt: companyInviteExpiresAt(),
invitedByUserId: req.actor.userId ?? null,
};