| @@ -121,7 +121,7 @@ describe("MCP AuthKit token verification", () => { · 120 unmodified lines |
| 121 | 121 | |
| 122 | 122 | describe("access token expiry and identity boundaries", () => { |
| 123 | 123 | for (const kind of ["mcp", "user-management"] as const) { |
| 124 | | − for (const invalidClaim of ["missing-exp", "missing-iat", "older-than-one-day"] as const) { |
| 124 | + for (const invalidClaim of ["missing-exp", "missing-iat"] as const) { |
| 125 | 125 | it.effect(`${kind} rejects ${invalidClaim}`, () => |
| 126 | 126 | Effect.gen(function* () { |
| 127 | 127 | const { publicKey, privateKey } = yield* Effect.promise(() => generateKeyPair("RS256")); |
| @@ -134,9 +134,7 @@ describe("access token expiry and identity boundaries", () => { · 6 unmodified lines |
| 134 | 134 | iss: issuer, |
| 135 | 135 | aud: resource, |
| 136 | 136 | ...(invalidClaim === "missing-exp" ? {} : { exp: now + 300 }), |
| 137 | | − ...(invalidClaim === "missing-iat" |
| 138 | | − ? {} |
| 139 | | − : { iat: invalidClaim === "older-than-one-day" ? now - 86401 : now }), |
| 137 | + ...(invalidClaim === "missing-iat" ? {} : { iat: now }), |
| 140 | 138 | }; |
| 141 | 139 | const token = yield* Effect.promise(() => |
| 142 | 140 | new SignJWT(claims) |
| @@ -153,6 +151,30 @@ describe("access token expiry and identity boundaries", () => { · 10 unmodified lines |
| 153 | 151 | }), |
| 154 | 152 | ); |
| 155 | 153 | } |
| 154 | + it.effect(`${kind} accepts a token issued more than a day ago that has not expired`, () => |
| 155 | + Effect.gen(function* () { |
| 156 | + const { publicKey, privateKey } = yield* Effect.promise(() => generateKeyPair("RS256")); |
| 157 | + const jwk = yield* Effect.promise(() => exportJWK(publicKey)); |
| 158 | + const jwks = createLocalJWKSet({ keys: [{ ...jwk, kid: "expiry-key" }] }); |
| 159 | + const now = Math.floor(Date.now() / 1000); |
| 160 | + const token = yield* Effect.promise(() => |
| 161 | + new SignJWT({ |
| 162 | + sub: "user_test", |
| 163 | + org_id: "org_test", |
| 164 | + iss: issuer, |
| 165 | + aud: resource, |
| 166 | + iat: now - 5 * 86400, |
| 167 | + exp: now + 2 * 86400, |
| 168 | + }) |
| 169 | + .setProtectedHeader({ alg: "RS256", kid: "expiry-key" }) |
| 170 | + .sign(privateKey), |
| 171 | + ); |
| 172 | + const verified = yield* kind === "mcp" |
| 173 | + ? verifyMcpAccessToken(token, jwks, { issuer, audience: resource }) |
| 174 | + : verifyWorkosUserManagementToken(token, jwks); |
| 175 | + expect(verified).toEqual({ accountId: "user_test", organizationId: "org_test" }); |
| 176 | + }), |
| 177 | + ); |
| 156 | 178 | it.effect(`${kind} rejects a non-string organization claim`, () => |
| 157 | 179 | Effect.gen(function* () { |
| 158 | 180 | const { jwks, sign } = yield* Effect.promise(() => makeVerifier()); |