VYPR
Medium severityNVD Advisory· Published Apr 29, 2025· Updated Apr 15, 2026

CVE-2025-46344

CVE-2025-46344

Description

The Auth0 Next.js SDK is a library for implementing user authentication in Next.js applications. Versions starting from 4.0.1 and prior to 4.5.1, do not invoke .setExpirationTime when generating a JWE token for the session. As a result, the JWE does not contain an internal expiration claim. While the session cookie may expire or be cleared, the JWE remains valid. This issue has been patched in version 4.5.1.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@auth0/nextjs-auth0npm
>= 4.0.1, < 4.5.14.5.1

Patches

2
a4f061aed02f

fix: Ensure JWE expires as expected (#2040)

https://github.com/auth0/nextjs-auth0Frederik PrijckApr 29, 2025via ghsa
11 files changed · +195 116
  • src/server/auth-client.test.ts+85 46 modified
    @@ -496,7 +496,9 @@ ca/T0LLtgmbMmxSv/MmzIg==
                 createdAt: Math.floor(Date.now() / 1000)
               }
             };
    -        const sessionCookie = await encrypt(session, secret);
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +        const sessionCookie = await encrypt(session, secret, expiration);
             const headers = new Headers();
             headers.append("cookie", `__session=${sessionCookie}`);
             const request = new NextRequest(
    @@ -516,7 +518,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
               updatedSessionCookie!.value,
               secret
             );
    -        expect(updatedSessionCookieValue).toEqual({
    +        expect(updatedSessionCookieValue).toEqual(expect.objectContaining({
               user: {
                 sub: DEFAULT.sub
               },
    @@ -529,7 +531,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
                 sid: DEFAULT.sid,
                 createdAt: expect.any(Number)
               }
    -        });
    +        }));
     
             // assert that the session expiry has been extended by the inactivity duration
             expect(updatedSessionCookie?.maxAge).toEqual(1800);
    @@ -868,13 +870,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
           );
           expect(transactionCookie).toBeDefined();
           expect((await decrypt(transactionCookie!.value, secret)).payload).toEqual(
    -        {
    +        expect.objectContaining({
               nonce: authorizationUrl.searchParams.get("nonce"),
               codeVerifier: expect.any(String),
               responseType: "code",
               state: authorizationUrl.searchParams.get("state"),
               returnTo: "/"
    -        }
    +        })
           );
         });
     
    @@ -1023,13 +1025,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
             expect(transactionCookie).toBeDefined();
             expect(
               (await decrypt(transactionCookie!.value, secret)).payload
    -        ).toEqual({
    +        ).toEqual(expect.objectContaining({
               nonce: authorizationUrl.searchParams.get("nonce"),
               codeVerifier: expect.any(String),
               responseType: "code",
               state: authorizationUrl.searchParams.get("state"),
               returnTo: "/"
    -        });
    +        }));
           });
     
           it("should forward the configured authorization parameters to the authorization server", async () => {
    @@ -1356,14 +1358,14 @@ ca/T0LLtgmbMmxSv/MmzIg==
           );
           expect(transactionCookie).toBeDefined();
           expect((await decrypt(transactionCookie!.value, secret)).payload).toEqual(
    -        {
    +        expect.objectContaining({
               nonce: authorizationUrl.searchParams.get("nonce"),
               maxAge: 3600,
               codeVerifier: expect.any(String),
               responseType: "code",
               state: authorizationUrl.searchParams.get("state"),
               returnTo: "/"
    -        }
    +        })
           );
         });
     
    @@ -1403,13 +1405,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
           );
           expect(transactionCookie).toBeDefined();
           expect((await decrypt(transactionCookie!.value, secret)).payload).toEqual(
    -        {
    +        expect.objectContaining({
               nonce: authorizationUrl.searchParams.get("nonce"),
               codeVerifier: expect.any(String),
               responseType: "code",
               state: authorizationUrl.searchParams.get("state"),
               returnTo: "https://example.com/dashboard"
    -        }
    +        })
           );
         });
     
    @@ -1449,13 +1451,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
           );
           expect(transactionCookie).toBeDefined();
           expect((await decrypt(transactionCookie!.value, secret)).payload).toEqual(
    -        {
    +        expect.objectContaining({
               nonce: authorizationUrl.searchParams.get("nonce"),
               codeVerifier: expect.any(String),
               responseType: "code",
               state: authorizationUrl.searchParams.get("state"),
               returnTo: "/"
    -        }
    +        })
           );
         });
     
    @@ -1583,13 +1585,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
             expect(transactionCookie).toBeDefined();
             expect(
               (await decrypt(transactionCookie!.value, secret)).payload
    -        ).toEqual({
    +        ).toEqual(expect.objectContaining({
               nonce: expect.any(String),
               codeVerifier: expect.any(String),
               responseType: "code",
               state,
               returnTo: "/"
    -        });
    +        }));
           });
     
           describe("custom parameters to the authorization server", async () => {
    @@ -1662,13 +1664,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
               expect(transactionCookie).toBeDefined();
               expect(
                 (await decrypt(transactionCookie!.value, secret)).payload
    -          ).toEqual({
    +          ).toEqual(expect.objectContaining({
                 nonce: expect.any(String),
                 codeVerifier: expect.any(String),
                 responseType: "code",
                 state,
                 returnTo: "/"
    -          });
    +          }));
             });
     
             it("should forward custom parameters set in the configuration to the authorization server", async () => {
    @@ -1742,13 +1744,13 @@ ca/T0LLtgmbMmxSv/MmzIg==
               expect(transactionCookie).toBeDefined();
               expect(
                 (await decrypt(transactionCookie!.value, secret)).payload
    -          ).toEqual({
    +          ).toEqual(expect.objectContaining({
                 nonce: expect.any(String),
                 codeVerifier: expect.any(String),
                 responseType: "code",
                 state,
                 returnTo: "/"
    -          });
    +          }));
             });
           });
         });
    @@ -1838,7 +1840,9 @@ ca/T0LLtgmbMmxSv/MmzIg==
               createdAt: Math.floor(Date.now() / 1000)
             }
           };
    -      const sessionCookie = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const sessionCookie = await encrypt(session, secret, expiration);
           const headers = new Headers();
           headers.append("cookie", `__session=${sessionCookie}`);
           const request = new NextRequest(
    @@ -1911,7 +1915,9 @@ ca/T0LLtgmbMmxSv/MmzIg==
               createdAt: Math.floor(Date.now() / 1000)
             }
           };
    -      const sessionCookie = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const sessionCookie = await encrypt(session, secret, expiration);
           const headers = new Headers();
           headers.append("cookie", `__session=${sessionCookie}`);
     
    @@ -2173,7 +2179,9 @@ ca/T0LLtgmbMmxSv/MmzIg==
               createdAt: Math.floor(Date.now() / 1000)
             }
           };
    -      const sessionCookie = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const sessionCookie = await encrypt(session, secret, expiration);
           const headers = new Headers();
           headers.append("cookie", `__session=${sessionCookie}`);
           const request = new NextRequest(
    @@ -2268,9 +2276,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
             state: state,
             returnTo: "/dashboard"
           };
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           headers.set(
             "cookie",
    -        `__txn_${state}=${await encrypt(transactionState, secret)}`
    +        `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
           );
           const request = new NextRequest(url, {
             method: "GET",
    @@ -2288,7 +2298,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
           const sessionCookie = response.cookies.get("__session");
           expect(sessionCookie).toBeDefined();
           const { payload: session } = await decrypt(sessionCookie!.value, secret);
    -      expect(session).toEqual({
    +      expect(session).toEqual(expect.objectContaining({
             user: {
               sub: DEFAULT.sub
             },
    @@ -2302,7 +2312,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
               sid: expect.any(String),
               createdAt: expect.any(Number)
             }
    -      });
    +      }));
     
           // validate the transaction cookie has been removed
           const transactionCookie = response.cookies.get(`__txn_${state}`);
    @@ -2377,9 +2387,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
             state: state,
             returnTo: "/dashboard"
           };
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           headers.set(
             "cookie",
    -        `__txn_${state}=${await encrypt(transactionState, secret)}`
    +        `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
           );
           const request = new NextRequest(url, {
             method: "GET",
    @@ -2397,7 +2409,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
           const sessionCookie = response.cookies.get("__session");
           expect(sessionCookie).toBeDefined();
           const { payload: session } = await decrypt(sessionCookie!.value, secret);
    -      expect(session).toEqual({
    +      expect(session).toEqual(expect.objectContaining({
             user: {
               sub: DEFAULT.sub
             },
    @@ -2411,7 +2423,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
               sid: expect.any(String),
               createdAt: expect.any(Number)
             }
    -      });
    +      }));
     
           // validate the transaction cookie has been removed
           const transactionCookie = response.cookies.get(`__txn_${state}`);
    @@ -2496,9 +2508,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
             state: state,
             returnTo: "/dashboard"
           };
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           headers.set(
             "cookie",
    -        `__txn_does-not-exist=${await encrypt(transactionState, secret)}`
    +        `__txn_does-not-exist=${await encrypt(transactionState, secret, expiration)}`
           );
           const request = new NextRequest(url, {
             method: "GET",
    @@ -2548,9 +2562,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
             state: state,
             returnTo: "/dashboard"
           };
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           headers.set(
             "cookie",
    -        `__txn_${state}=${await encrypt(transactionState, secret)}`
    +        `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
           );
           const request = new NextRequest(url, {
             method: "GET",
    @@ -2607,9 +2623,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
             state: state,
             returnTo: "/dashboard"
           };
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           headers.set(
             "cookie",
    -        `__txn_${state}=${await encrypt(transactionState, secret)}`
    +        `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
           );
           const request = new NextRequest(url, {
             method: "GET",
    @@ -2663,9 +2681,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
             state: state,
             returnTo: "/dashboard"
           };
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           headers.set(
             "cookie",
    -        `__txn_${state}=${await encrypt(transactionState, secret)}`
    +        `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
           );
           const request = new NextRequest(url, {
             method: "GET",
    @@ -2726,9 +2746,12 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      
             headers.set(
               "cookie",
    -          `__txn_${state}=${await encrypt(transactionState, secret)}`
    +          `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -2773,7 +2796,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
               sessionCookie!.value,
               secret
             );
    -        expect(session).toEqual(expectedSession);
    +        expect(session).toEqual(expect.objectContaining(expectedSession));
           });
     
           it("should be called with an error if the state parameter is missing", async () => {
    @@ -2881,9 +2904,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             headers.set(
               "cookie",
    -          `__txn_non-existent-state=${await encrypt(transactionState, secret)}`
    +          `__txn_non-existent-state=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -2956,9 +2981,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             headers.set(
               "cookie",
    -          `__txn_${state}=${await encrypt(transactionState, secret)}`
    +          `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -3040,9 +3067,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             headers.set(
               "cookie",
    -          `__txn_${state}=${await encrypt(transactionState, secret)}`
    +          `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -3124,9 +3153,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             headers.set(
               "cookie",
    -          `__txn_${state}=${await encrypt(transactionState, secret)}`
    +          `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -3204,9 +3235,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             headers.set(
               "cookie",
    -          `__txn_${state}=${await encrypt(transactionState, secret)}`
    +          `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -3227,7 +3260,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
               sessionCookie!.value,
               secret
             );
    -        expect(session).toEqual({
    +        expect(session).toEqual(expect.objectContaining({
               user: {
                 sub: DEFAULT.sub,
                 name: "John Doe",
    @@ -3244,7 +3277,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
                 sid: expect.any(String),
                 createdAt: expect.any(Number)
               }
    -        });
    +        }));
           });
     
           it("should not call the hook if the session is not established", async () => {
    @@ -3334,9 +3367,11 @@ ca/T0LLtgmbMmxSv/MmzIg==
               state: state,
               returnTo: "/dashboard"
             };
    +        const maxAge = 60 * 60; // 1 hour
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             headers.set(
               "cookie",
    -          `__txn_${state}=${await encrypt(transactionState, secret)}`
    +          `__txn_${state}=${await encrypt(transactionState, secret, expiration)}`
             );
             const request = new NextRequest(url, {
               method: "GET",
    @@ -3357,7 +3392,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
               sessionCookie!.value,
               secret
             );
    -        expect(session).toEqual({
    +        expect(session).toEqual(expect.objectContaining({
               user: {
                 sub: DEFAULT.sub,
                 name: "John Doe",
    @@ -3374,7 +3409,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
                 sid: expect.any(String),
                 createdAt: expect.any(Number)
               }
    -        });
    +        }));
           });
         });
       });
    @@ -3432,7 +3467,9 @@ ca/T0LLtgmbMmxSv/MmzIg==
               createdAt: Math.floor(Date.now() / 1000)
             }
           };
    -      const sessionCookie = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const sessionCookie = await encrypt(session, secret, expiration);
           const headers = new Headers();
           headers.append("cookie", `__session=${sessionCookie}`);
           const request = new NextRequest(
    @@ -3543,7 +3580,9 @@ ca/T0LLtgmbMmxSv/MmzIg==
               createdAt: Math.floor(Date.now() / 1000)
             }
           };
    -      const sessionCookie = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const sessionCookie = await encrypt(session, secret, expiration);
           const headers = new Headers();
           headers.append("cookie", `__session=${sessionCookie}`);
           const request = new NextRequest(
    
  • src/server/cookies.test.ts+24 5 modified
    @@ -9,28 +9,47 @@ describe("encrypt/decrypt", async () => {
     
       it("should encrypt/decrypt a payload with the correct secret", async () => {
         const payload = { key: "value" };
    -    const encrypted = await encrypt(payload, secret);
    +    const maxAge = 60 * 60; // 1 hour in seconds
    +    const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +    const encrypted = await encrypt(payload, secret, expiration);
         const decrypted = await decrypt(encrypted, secret);
     
    -    expect(decrypted.payload).toEqual(payload);
    +    expect(decrypted.payload).toEqual(expect.objectContaining(payload));
       });
     
       it("should fail to decrypt a payload with the incorrect secret", async () => {
         const payload = { key: "value" };
    -    const encrypted = await encrypt(payload, secret);
    +    const maxAge = 60 * 60; // 1 hour in seconds
    +    const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +    const encrypted = await encrypt(payload, secret, expiration);
         await expect(() =>
           decrypt(encrypted, incorrectSecret)
         ).rejects.toThrowError();
       });
     
    +  it("should fail to decrypt when expired", async () => {
    +    const payload = { key: "value" };
    +    const expiration = Math.floor(Date.now() / 1000 - 60); // 60 seconds in the past
    +    const encrypted = await encrypt(payload, secret, expiration);
    +    await expect(() =>
    +      decrypt(encrypted, secret)
    +    ).rejects.toThrowError(`"exp" claim timestamp check failed`);
    +  });
    +
       it("should fail to encrypt if a secret is not provided", async () => {
         const payload = { key: "value" };
    -    await expect(() => encrypt(payload, "")).rejects.toThrowError();
    +    const maxAge = 60 * 60; // 1 hour in seconds
    +    const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +    
    +    await expect(() => encrypt(payload, "", expiration)).rejects.toThrowError();
       });
     
       it("should fail to decrypt if a secret is not provided", async () => {
         const payload = { key: "value" };
    -    const encrypted = await encrypt(payload, secret);
    +    const maxAge = 60 * 60; // 1 hour in seconds
    +    const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +    
    +    const encrypted = await encrypt(payload, secret, expiration);
         await expect(() => decrypt(encrypted, "")).rejects.toThrowError();
       });
     });
    
  • src/server/cookies.ts+2 0 modified
    @@ -15,6 +15,7 @@ const ENCRYPTION_INFO = "JWE CEK";
     export async function encrypt(
       payload: jose.JWTPayload,
       secret: string,
    +  expiration: number,
       additionalHeaders?: {
         iat: number;
         uat: number;
    @@ -31,6 +32,7 @@ export async function encrypt(
     
       const encryptedCookie = await new jose.EncryptJWT(payload)
         .setProtectedHeader({ enc: ENC, alg: ALG, ...additionalHeaders })
    +    .setExpirationTime(expiration)
         .encrypt(encryptionSecret);
     
       return encryptedCookie.toString();
    
  • src/server/session/stateful-session-store.test.ts+16 5 modified
    @@ -34,11 +34,14 @@ describe("Stateful Session Store", async () => {
             set: vi.fn(),
             delete: vi.fn()
           };
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           const encryptedCookieValue = await encrypt(
             {
               id: sessionId
             },
    -        secret
    +        secret,
    +        expiration,
           );
     
           const headers = new Headers();
    @@ -99,11 +102,14 @@ describe("Stateful Session Store", async () => {
             set: vi.fn(),
             delete: vi.fn()
           };
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           const encryptedCookieValue = await encrypt(
             {
               id: sessionId
             },
    -        secret
    +        secret,
    +        expiration,
           );
     
           const headers = new Headers();
    @@ -464,12 +470,14 @@ describe("Stateful Session Store", async () => {
               set: vi.fn(),
               delete: vi.fn()
             };
    -
    +        const maxAge = 60 * 60; // 1 hour in seconds
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             const encryptedCookieValue = await encrypt(
               {
                 id: sessionId
               },
    -          secret
    +          secret,
    +          expiration,
             );
             const headers = new Headers();
             headers.append("cookie", `__session=${encryptedCookieValue}`);
    @@ -750,11 +758,14 @@ describe("Stateful Session Store", async () => {
             set: vi.fn(),
             delete: vi.fn()
           };
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
           const encryptedCookieValue = await encrypt(
             {
               id: sessionId
             },
    -        secret
    +        secret,
    +        expiration,
           );
           const headers = new Headers();
           headers.append("cookie", `__session=${encryptedCookieValue}`);
    
  • src/server/session/stateful-session-store.ts+6 4 modified
    @@ -130,15 +130,17 @@ export class StatefulSessionStore extends AbstractSessionStore {
         if (!sessionId) {
           sessionId = generateId();
         }
    -
    +    
    +    const maxAge = this.calculateMaxAge(session.internal.createdAt);
    +    const expiration = Date.now() / 1000 + maxAge;
         const jwe = await cookies.encrypt(
           {
             id: sessionId
           },
    -      this.secret
    +      this.secret,
    +      expiration,
         );
    -    const maxAge = this.calculateMaxAge(session.internal.createdAt);
    -
    +    
         resCookies.set(this.sessionCookieName, jwe.toString(), {
           ...this.cookieConfig,
           maxAge
    
  • src/server/session/stateless-session-store.test.ts+27 16 modified
    @@ -22,7 +22,9 @@ describe("Stateless Session Store", async () => {
               createdAt: Math.floor(Date.now() / 1000)
             }
           };
    -      const encryptedCookieValue = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const encryptedCookieValue = await encrypt(session, secret, expiration);
     
           const headers = new Headers();
           headers.append("cookie", `__session=${encryptedCookieValue}`);
    @@ -32,7 +34,7 @@ describe("Stateless Session Store", async () => {
             secret
           });
     
    -      expect(await sessionStore.get(requestCookies)).toEqual(session);
    +      expect(await sessionStore.get(requestCookies)).toEqual(expect.objectContaining(session));
         });
     
         it("should return null if no session cookie exists", async () => {
    @@ -65,9 +67,12 @@ describe("Stateless Session Store", async () => {
               uat: Math.floor(Date.now() / 1000),
               exp: Math.floor(Date.now() / 1000)
             };
    +        const maxAge = 60 * 60; // 1 hour in seconds
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             const encryptedCookieValue = await encrypt(
               legacySession,
               secret,
    +          expiration,
               legacyHeader
             );
     
    @@ -106,9 +111,12 @@ describe("Stateless Session Store", async () => {
               uat: Math.floor(Date.now() / 1000),
               exp: Math.floor(Date.now() / 1000)
             };
    +        const maxAge = 60 * 60; // 1 hour in seconds
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             const encryptedCookieValue = await encrypt(
               legacySession,
               secret,
    +          expiration,
               legacyHeader
             );
     
    @@ -153,9 +161,12 @@ describe("Stateless Session Store", async () => {
               uat: Math.floor(Date.now() / 1000),
               exp: Math.floor(Date.now() / 1000)
             };
    +        const maxAge = 60 * 60; // 1 hour in seconds
    +        const expiration = Math.floor(Date.now() / 1000 + maxAge);
             const encryptedCookieValue = await encrypt(
               legacySession,
               secret,
    +          expiration,
               legacyHeader
             );
     
    @@ -206,7 +217,9 @@ describe("Stateless Session Store", async () => {
               }
             ]
           };
    -      const encryptedCookieValue = await encrypt(session, secret);
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const encryptedCookieValue = await encrypt(session, secret, expiration);
     
           const headers = new Headers();
           headers.append("cookie", `__session=${encryptedCookieValue}`);
    @@ -216,7 +229,7 @@ describe("Stateless Session Store", async () => {
             secret
           });
     
    -      expect(await sessionStore.get(requestCookies)).toEqual(session);
    +      expect(await sessionStore.get(requestCookies)).toEqual(expect.objectContaining(session));
         });
       });
     
    @@ -264,7 +277,7 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("__session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(session));
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
             expect(cookie?.sameSite).toEqual("lax");
    @@ -306,12 +319,10 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("__session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    -        expect(cookie?.path).toEqual("/");
    -        expect(cookie?.httpOnly).toEqual(true);
    -        expect(cookie?.sameSite).toEqual("lax");
    -        expect(cookie?.maxAge).toEqual(0); // cookie should expire immediately
    -        expect(cookie?.secure).toEqual(false);
    +
    +        await expect(
    +          decrypt(cookie!.value, secret)
    +        ).rejects.toThrow(`"exp" claim timestamp check failed`);
           });
     
           it("should delete the legacy cookie if it exists", async () => {
    @@ -413,7 +424,7 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("__session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(session));
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
             expect(cookie?.sameSite).toEqual("lax");
    @@ -453,7 +464,7 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("__session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(session));
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
             expect(cookie?.sameSite).toEqual("lax");
    @@ -492,7 +503,7 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("__session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(session));
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
             expect(cookie?.sameSite).toEqual("strict");
    @@ -528,7 +539,7 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("__session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(session));
             expect(cookie?.path).toEqual("/custom-path");
           });
     
    @@ -563,7 +574,7 @@ describe("Stateless Session Store", async () => {
             const cookie = responseCookies.get("custom-session");
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(session);
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(session));
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
             expect(cookie?.sameSite).toEqual("lax");
    
  • src/server/session/stateless-session-store.ts+4 2 modified
    @@ -87,8 +87,9 @@ export class StatelessSessionStore extends AbstractSessionStore {
         session: SessionData
       ) {
         const { connectionTokenSets, ...originalSession } = session;
    -    const jwe = await cookies.encrypt(originalSession, this.secret);
         const maxAge = this.calculateMaxAge(session.internal.createdAt);
    +    const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +    const jwe = await cookies.encrypt(originalSession, this.secret, expiration);
         const cookieValue = jwe.toString();
         const options: CookieOptions = {
           ...this.cookieConfig,
    @@ -141,7 +142,8 @@ export class StatelessSessionStore extends AbstractSessionStore {
         cookieName: string,
         maxAge: number
       ) {
    -    const jwe = await cookies.encrypt(session, this.secret);
    +    const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +    const jwe = await cookies.encrypt(session, this.secret, expiration);
     
         const cookieValue = jwe.toString();
     
    
  • src/server/transaction-store.test.ts+13 9 modified
    @@ -20,7 +20,9 @@ describe("Transaction Store", async () => {
             state,
             returnTo: "/dashboard"
           };
    -      const encryptedCookieValue = await encrypt(transactionState, secret);
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const encryptedCookieValue = await encrypt(transactionState, secret, expiration);
     
           const headers = new Headers();
           headers.append("cookie", `__txn_${state}=${encryptedCookieValue}`);
    @@ -32,7 +34,7 @@ describe("Transaction Store", async () => {
     
           expect(
             (await transactionStore.get(requestCookies, state))?.payload
    -      ).toEqual(transactionState);
    +      ).toEqual(expect.objectContaining(transactionState));
         });
     
         it("should return null if no transaction cookie with a matching state exists", async () => {
    @@ -48,7 +50,9 @@ describe("Transaction Store", async () => {
             state,
             returnTo: "/dashboard"
           };
    -      const encryptedCookieValue = await encrypt(transactionState, secret);
    +      const maxAge = 60 * 60; // 1 hour in seconds
    +      const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +      const encryptedCookieValue = await encrypt(transactionState, secret, expiration);
     
           const headers = new Headers();
           headers.append("cookie", `__txn_incorrect-state=${encryptedCookieValue}`);
    @@ -89,7 +93,7 @@ describe("Transaction Store", async () => {
     
           expect(cookie).toBeDefined();
           expect((await decrypt(cookie!.value, secret)).payload).toEqual(
    -        transactionState
    +        expect.objectContaining(transactionState)
           );
           expect(cookie?.path).toEqual("/");
           expect(cookie?.httpOnly).toEqual(true);
    @@ -152,7 +156,7 @@ describe("Transaction Store", async () => {
     
             expect(cookie).toBeDefined();
             expect((await decrypt(cookie!.value, secret)).payload).toEqual(
    -          transactionState
    +          expect.objectContaining(transactionState)
             );
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
    @@ -190,7 +194,7 @@ describe("Transaction Store", async () => {
     
             expect(cookie).toBeDefined();
             expect((await decrypt(cookie!.value, secret)).payload).toEqual(
    -          transactionState
    +          expect.objectContaining(transactionState)
             );
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
    @@ -228,7 +232,7 @@ describe("Transaction Store", async () => {
     
             expect(cookie).toBeDefined();
             expect((await decrypt(cookie!.value, secret)).payload).toEqual(
    -          transactionState
    +          expect.objectContaining(transactionState)
             );
             expect(cookie?.path).toEqual("/custom-path");
           });
    @@ -261,9 +265,9 @@ describe("Transaction Store", async () => {
             const cookie = responseCookies.get(cookieName);
     
             expect(cookie).toBeDefined();
    -        expect((await decrypt(cookie!.value, secret)).payload).toEqual(
    +        expect((await decrypt(cookie!.value, secret)).payload).toEqual(expect.objectContaining(
               transactionState
    -        );
    +        ));
             expect(cookie?.path).toEqual("/");
             expect(cookie?.httpOnly).toEqual(true);
             expect(cookie?.sameSite).toEqual("lax");
    
  • src/server/transaction-store.ts+3 1 modified
    @@ -79,7 +79,9 @@ export class TransactionStore {
         resCookies: cookies.ResponseCookies,
         transactionState: TransactionState
       ) {
    -    const jwe = await cookies.encrypt(transactionState, this.secret);
    +
    +    const expiration = Math.floor(Date.now() / 1000 + this.cookieConfig.maxAge!);
    +    const jwe = await cookies.encrypt(transactionState, this.secret, expiration);
     
         if (!transactionState.state) {
           throw new Error("Transaction state is required");
    
  • src/testing/generate-session-cookie.test.ts+11 27 modified
    @@ -29,7 +29,7 @@ describe("generateSessionCookie", async () => {
         };
         const sessionCookie = await generateSessionCookie(session, config);
         expect(sessionCookie).toEqual(expect.any(String));
    -    expect((await decrypt(sessionCookie, secret)).payload).toEqual({
    +    expect((await decrypt(sessionCookie, secret)).payload).toEqual(expect.objectContaining({
           user: {
             sub: "user_123"
           },
    @@ -42,7 +42,7 @@ describe("generateSessionCookie", async () => {
             sid: "auth0-sid",
             createdAt: createdAt
           }
    -    });
    +    }));
       });
     
       it("should populate the internal property if it was not provided", async () => {
    @@ -60,7 +60,7 @@ describe("generateSessionCookie", async () => {
         };
         const sessionCookie = await generateSessionCookie(session, config);
         expect(sessionCookie).toEqual(expect.any(String));
    -    expect((await decrypt(sessionCookie, secret)).payload).toEqual({
    +    expect((await decrypt(sessionCookie, secret)).payload).toEqual(expect.objectContaining({
           user: {
             sub: "user_123"
           },
    @@ -73,7 +73,7 @@ describe("generateSessionCookie", async () => {
             sid: "auth0-sid",
             createdAt: expect.any(Number)
           }
    -    });
    +    }));
       });
     
       it("should not populate the internal property if a null was provided", async () => {
    @@ -93,20 +93,12 @@ describe("generateSessionCookie", async () => {
         };
         const sessionCookie = await generateSessionCookie(session, config);
         expect(sessionCookie).toEqual(expect.any(String));
    -    expect((await decrypt(sessionCookie, secret)).payload).toEqual({
    -      user: {
    -        sub: "user_123"
    -      },
    -      tokenSet: {
    -        accessToken: "at_123",
    -        refreshToken: "rt_123",
    -        expiresAt: 123456
    -      },
    -      internal: null
    -    });
    +    expect((await decrypt(sessionCookie, secret)).payload).not.toEqual(expect.objectContaining({
    +      internal: expect.anything()
    +    }));
       });
     
    -  it("should not populate the internal property if a undefine was provided", async () => {
    +  it("should not populate the internal property if a undefined was provided", async () => {
         const session: Partial<SessionData> = {
           user: { sub: "user_123" },
           tokenSet: {
    @@ -122,16 +114,8 @@ describe("generateSessionCookie", async () => {
         };
         const sessionCookie = await generateSessionCookie(session, config);
         expect(sessionCookie).toEqual(expect.any(String));
    -    expect((await decrypt(sessionCookie, secret)).payload).toEqual({
    -      user: {
    -        sub: "user_123"
    -      },
    -      tokenSet: {
    -        accessToken: "at_123",
    -        refreshToken: "rt_123",
    -        expiresAt: 123456
    -      },
    -      internal: undefined
    -    });
    +    expect((await decrypt(sessionCookie, secret)).payload).not.toEqual(expect.objectContaining({
    +      internal: expect.anything()
    +    }));
       });
     });
    
  • src/testing/generate-session-cookie.ts+4 1 modified
    @@ -21,5 +21,8 @@ export const generateSessionCookie = async (
         };
       }
     
    -  return encrypt(session, config.secret);
    +  const maxAge = 60 * 60; // 1 hour in seconds
    +  const expiration = Math.floor(Date.now() / 1000 + maxAge);
    +
    +  return encrypt(session, config.secret, expiration);
     };
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.