Skip to content

Commit 8714f21

Browse files
fix(server): validate expiresAt token value for non existence (#446)
* fix(server): validate expiresAt token value for non existence --------- Co-authored-by: Olivier Chafik <[email protected]>
1 parent d681f14 commit 8714f21

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

src/server/auth/middleware/bearerAuth.test.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe("requireBearerAuth middleware", () => {
3737
token: "valid-token",
3838
clientId: "client-123",
3939
scopes: ["read", "write"],
40+
expiresAt: Math.floor(Date.now() / 1000) + 3600, // Token expires in an hour
4041
};
4142
mockVerifyAccessToken.mockResolvedValue(validAuthInfo);
4243

@@ -53,13 +54,17 @@ describe("requireBearerAuth middleware", () => {
5354
expect(mockResponse.status).not.toHaveBeenCalled();
5455
expect(mockResponse.json).not.toHaveBeenCalled();
5556
});
56-
57-
it("should reject expired tokens", async () => {
57+
58+
it.each([
59+
[100], // Token expired 100 seconds ago
60+
[0], // Token expires at the same time as now
61+
])("should reject expired tokens (expired %s seconds ago)", async (expiredSecondsAgo: number) => {
62+
const expiresAt = Math.floor(Date.now() / 1000) - expiredSecondsAgo;
5863
const expiredAuthInfo: AuthInfo = {
5964
token: "expired-token",
6065
clientId: "client-123",
6166
scopes: ["read", "write"],
62-
expiresAt: Math.floor(Date.now() / 1000) - 100, // Token expired 100 seconds ago
67+
expiresAt
6368
};
6469
mockVerifyAccessToken.mockResolvedValue(expiredAuthInfo);
6570

@@ -82,6 +87,37 @@ describe("requireBearerAuth middleware", () => {
8287
expect(nextFunction).not.toHaveBeenCalled();
8388
});
8489

90+
it.each([
91+
[undefined], // Token has no expiration time
92+
[NaN], // Token has no expiration time
93+
])("should reject tokens with no expiration time (expiresAt: %s)", async (expiresAt: number | undefined) => {
94+
const noExpirationAuthInfo: AuthInfo = {
95+
token: "no-expiration-token",
96+
clientId: "client-123",
97+
scopes: ["read", "write"],
98+
expiresAt
99+
};
100+
mockVerifyAccessToken.mockResolvedValue(noExpirationAuthInfo);
101+
102+
mockRequest.headers = {
103+
authorization: "Bearer expired-token",
104+
};
105+
106+
const middleware = requireBearerAuth({ verifier: mockVerifier });
107+
await middleware(mockRequest as Request, mockResponse as Response, nextFunction);
108+
109+
expect(mockVerifyAccessToken).toHaveBeenCalledWith("expired-token");
110+
expect(mockResponse.status).toHaveBeenCalledWith(401);
111+
expect(mockResponse.set).toHaveBeenCalledWith(
112+
"WWW-Authenticate",
113+
expect.stringContaining('Bearer error="invalid_token"')
114+
);
115+
expect(mockResponse.json).toHaveBeenCalledWith(
116+
expect.objectContaining({ error: "invalid_token", error_description: "Token has no expiration time" })
117+
);
118+
expect(nextFunction).not.toHaveBeenCalled();
119+
});
120+
85121
it("should accept non-expired tokens", async () => {
86122
const nonExpiredAuthInfo: AuthInfo = {
87123
token: "valid-token",
@@ -141,6 +177,7 @@ describe("requireBearerAuth middleware", () => {
141177
token: "valid-token",
142178
clientId: "client-123",
143179
scopes: ["read", "write", "admin"],
180+
expiresAt: Math.floor(Date.now() / 1000) + 3600, // Token expires in an hour
144181
};
145182
mockVerifyAccessToken.mockResolvedValue(authInfo);
146183

src/server/auth/middleware/bearerAuth.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ export function requireBearerAuth({ verifier, requiredScopes = [], resourceMetad
6363
}
6464
}
6565

66-
// Check if the token is expired
67-
if (!!authInfo.expiresAt && authInfo.expiresAt < Date.now() / 1000) {
66+
// Check if the token is set to expire or if it is expired
67+
if (typeof authInfo.expiresAt !== 'number' || isNaN(authInfo.expiresAt)) {
68+
throw new InvalidTokenError("Token has no expiration time");
69+
} else if (authInfo.expiresAt < Date.now() / 1000) {
6870
throw new InvalidTokenError("Token has expired");
6971
}
7072

0 commit comments

Comments
 (0)