Skip to content

Commit e5a0c64

Browse files
committed
fix: store only 10 share tokens in the cookies and clear the expired ones
1 parent 414bcec commit e5a0c64

File tree

2 files changed

+53
-11
lines changed

2 files changed

+53
-11
lines changed

backend/src/share/share.controller.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import {
1010
Res,
1111
UseGuards,
1212
} from "@nestjs/common";
13+
import { JwtService } from "@nestjs/jwt";
1314
import { Throttle } from "@nestjs/throttler";
1415
import { User } from "@prisma/client";
1516
import { Request, Response } from "express";
17+
import * as moment from "moment";
1618
import { GetUser } from "src/auth/decorator/getUser.decorator";
1719
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
1820
import { JwtGuard } from "src/auth/guard/jwt.guard";
@@ -29,7 +31,10 @@ import { ShareTokenSecurity } from "./guard/shareTokenSecurity.guard";
2931
import { ShareService } from "./share.service";
3032
@Controller("shares")
3133
export class ShareController {
32-
constructor(private shareService: ShareService) {}
34+
constructor(
35+
private shareService: ShareService,
36+
private jwtService: JwtService,
37+
) {}
3338

3439
@Get("all")
3540
@UseGuards(JwtGuard, AdministratorGuard)
@@ -121,15 +126,46 @@ export class ShareController {
121126
@Post(":id/token")
122127
async getShareToken(
123128
@Param("id") id: string,
129+
@Req() request: Request,
124130
@Res({ passthrough: true }) response: Response,
125131
@Body() body: SharePasswordDto,
126132
) {
127133
const token = await this.shareService.getShareToken(id, body.password);
134+
135+
this.clearShareTokenCookies(request, response);
128136
response.cookie(`share_${id}_token`, token, {
129137
path: "/",
130138
httpOnly: true,
131139
});
132140

133141
return { token };
134142
}
143+
144+
/**
145+
* Keeps the 10 most recent share token cookies and deletes the rest and all expired ones
146+
*/
147+
private clearShareTokenCookies(request: Request, response: Response) {
148+
const shareTokenCookies = Object.entries(request.cookies)
149+
.filter(([key]) => key.startsWith("share_") && key.endsWith("_token"))
150+
.map(([key, value]) => ({
151+
key,
152+
payload: this.jwtService.decode(value),
153+
}));
154+
155+
const expiredTokens = shareTokenCookies.filter(
156+
(cookie) => cookie.payload.exp < moment().unix(),
157+
);
158+
const validTokens = shareTokenCookies.filter(
159+
(cookie) => cookie.payload.exp >= moment().unix(),
160+
);
161+
162+
expiredTokens.forEach((cookie) => response.clearCookie(cookie.key));
163+
164+
if (validTokens.length > 10) {
165+
validTokens
166+
.sort((a, b) => a.payload.exp - b.payload.exp)
167+
.slice(0, -10)
168+
.forEach((cookie) => response.clearCookie(cookie.key));
169+
}
170+
}
135171
}

backend/src/share/share.service.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
Injectable,
55
NotFoundException,
66
} from "@nestjs/common";
7-
import { JwtService } from "@nestjs/jwt";
7+
import { JwtService, JwtSignOptions } from "@nestjs/jwt";
88
import { Share, User } from "@prisma/client";
99
import * as archiver from "archiver";
1010
import * as argon from "argon2";
@@ -328,15 +328,21 @@ export class ShareService {
328328
const { expiration } = await this.prisma.share.findUnique({
329329
where: { id: shareId },
330330
});
331-
return this.jwtService.sign(
332-
{
333-
shareId,
334-
},
335-
{
336-
expiresIn: moment(expiration).diff(new Date(), "seconds") + "s",
337-
secret: this.config.get("internal.jwtSecret"),
338-
},
339-
);
331+
332+
const tokenPayload = {
333+
shareId,
334+
iat: moment().unix(),
335+
};
336+
337+
const tokenOptions: JwtSignOptions = {
338+
secret: this.config.get("internal.jwtSecret"),
339+
};
340+
341+
if (!moment(expiration).isSame(0)) {
342+
tokenOptions.expiresIn = moment(expiration).diff(new Date(), "seconds");
343+
}
344+
345+
return this.jwtService.sign(tokenPayload, tokenOptions);
340346
}
341347

342348
async verifyShareToken(shareId: string, token: string) {

0 commit comments

Comments
 (0)