@saurbit/oauth2-jwt 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _JoseJwksAuthority_instances, _JoseJwksAuthority_store, _JoseJwksAuthority_ttl, _JoseJwksAuthority_generateAndStoreKeyPair, _JoseJwksAuthority_getPrivateKey;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.JoseJwksAuthority = void 0;
16
+ const jose_1 = require("jose");
17
+ function base64UrlToString(b64url) {
18
+ // Fast path for Node/Bun
19
+ if (typeof Buffer !== "undefined") {
20
+ return Buffer.from(b64url, "base64url").toString();
21
+ }
22
+ // Convert Base64URL → standard Base64
23
+ const base64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
24
+ // Add padding if missing
25
+ const padded = base64.padEnd(base64.length + (4 - (base64.length % 4)) % 4, "=");
26
+ // Decode
27
+ const binary = atob(padded);
28
+ const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
29
+ return new TextDecoder().decode(bytes);
30
+ }
31
+ /**
32
+ * JWT authority backed by [jose](https://github.com/panva/jose).
33
+ *
34
+ * Manages RS256 signing key pairs, signs and verifies JWTs, and returns the
35
+ * JWKS endpoint payload. Keys are stored in a {@link JwksKeyStore} and are
36
+ * generated automatically on first use.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { createInMemoryKeyStore, JoseJwksAuthority } from "@saurbit/oauth2-jwt";
41
+ *
42
+ * const store = createInMemoryKeyStore();
43
+ * const authority = new JoseJwksAuthority(store, 86400); // keys valid for 24 h
44
+ *
45
+ * const { token } = await authority.sign({ sub: "user-123", exp: Math.floor(Date.now() / 1000) + 3600 });
46
+ * const payload = await authority.verify(token);
47
+ * ```
48
+ */
49
+ class JoseJwksAuthority {
50
+ /**
51
+ * @param store The key store used to manage JWKS keys.
52
+ * @param ttl Time-to-live for the keys in seconds.
53
+ */
54
+ constructor(store, ttl = 36000) {
55
+ _JoseJwksAuthority_instances.add(this);
56
+ _JoseJwksAuthority_store.set(this, void 0);
57
+ /**
58
+ * seconds
59
+ */
60
+ _JoseJwksAuthority_ttl.set(this, void 0);
61
+ __classPrivateFieldSet(this, _JoseJwksAuthority_store, store, "f");
62
+ __classPrivateFieldSet(this, _JoseJwksAuthority_ttl, ttl, "f");
63
+ }
64
+ /**
65
+ * Retrieves the current set of public keys from the key store.
66
+ * @returns An object containing the array of public keys.
67
+ */
68
+ async getPublicKeys() {
69
+ const publicJwks = await __classPrivateFieldGet(this, _JoseJwksAuthority_store, "f").getPublicKeys();
70
+ const json = { keys: publicJwks };
71
+ if (json && "keys" in json && Array.isArray(json.keys)) {
72
+ json.keys = [...json.keys].reverse();
73
+ }
74
+ return json;
75
+ }
76
+ /**
77
+ * Get current kid for observability/debugging
78
+ */
79
+ async getCurrentKid() {
80
+ const key = await __classPrivateFieldGet(this, _JoseJwksAuthority_instances, "m", _JoseJwksAuthority_getPrivateKey).call(this);
81
+ return key?.kid;
82
+ }
83
+ /**
84
+ * Helps implement the JWKS endpoint by returning the current set of public keys in the expected format.
85
+ * @returns The current set of public keys in the JWKS format.
86
+ */
87
+ getJwksEndpointResponse() {
88
+ return this.getPublicKeys();
89
+ }
90
+ /**
91
+ * Retrieves the public key corresponding to the given "kid" from the JWKS.
92
+ * @param kid The key ID of the desired public key.
93
+ * @returns The RSA public key if found, otherwise undefined.
94
+ */
95
+ async getPublicKey(kid) {
96
+ const keyStore = await this.getPublicKeys();
97
+ const key = keyStore.keys.find((k) => k.kid === kid);
98
+ if (!key)
99
+ return undefined;
100
+ if (key.kty !== "RSA")
101
+ return undefined;
102
+ return key;
103
+ }
104
+ /**
105
+ * Generates a new RSA key pair, stores it in the key store with the appropriate metadata
106
+ * (kid, alg, use), and ensures that the public key is available for JWKS exposure.
107
+ */
108
+ async generateKeyPair() {
109
+ await __classPrivateFieldGet(this, _JoseJwksAuthority_instances, "m", _JoseJwksAuthority_generateAndStoreKeyPair).call(this);
110
+ }
111
+ /**
112
+ * Signs the given payload as a JWT using the current private key.
113
+ * The "kid" of the signing key is included in the JWT header to allow verifiers
114
+ * to select the correct public key from the JWKS for verification.
115
+ * This method ensures that the JWT is signed with a valid key and that
116
+ * the corresponding public key is available in the JWKS for future verification.
117
+ * @param payload The payload to be signed as a JWT.
118
+ * @returns An object containing the signed JWT and the key ID used for signing.
119
+ */
120
+ async sign(payload) {
121
+ const key = await __classPrivateFieldGet(this, _JoseJwksAuthority_instances, "m", _JoseJwksAuthority_getPrivateKey).call(this);
122
+ if (key.kty !== "RSA") {
123
+ throw new Error("Invalid key type, expected RSA");
124
+ }
125
+ if (!key.kid) {
126
+ throw new Error('Key is missing "kid" property');
127
+ }
128
+ const privateKey = await (0, jose_1.importJWK)(key, "RS256");
129
+ const token = await new jose_1.SignJWT(payload)
130
+ .setProtectedHeader({ typ: "jwt", alg: "RS256", kid: key.kid })
131
+ .sign(privateKey);
132
+ return { token, kid: key.kid };
133
+ }
134
+ /**
135
+ * Verifies the given JWT using the appropriate public key from the JWKS based on the "kid" in the JWT header.
136
+ * It performs additional checks to ensure the integrity and expected structure of the JWT,
137
+ * such as validating the algorithm and ensuring no unexpected JWK is present in the header.
138
+ * @param token
139
+ * @returns
140
+ */
141
+ async verify(token) {
142
+ const [header] = token.split(".");
143
+ if (!header)
144
+ throw new Error("Invalid JWT format");
145
+ const parsedHeader = JSON.parse(base64UrlToString(header));
146
+ const kid = parsedHeader.kid;
147
+ if (!kid || typeof kid !== "string")
148
+ throw new Error('Invalid or missing "kid" in JWT header');
149
+ const key = await this.getPublicKey(kid);
150
+ if (!key)
151
+ throw new Error(`Key with kid "${kid}" not found`);
152
+ const { payload, protectedHeader } = await (0, jose_1.jwtVerify)(token, key);
153
+ if (protectedHeader.alg !== "RS256") {
154
+ throw new Error(`Unexpected algorithm: ${protectedHeader.alg}`);
155
+ }
156
+ // additional checks hardening security
157
+ if ("jwk" in protectedHeader) {
158
+ throw new Error("Unexpected JWK in header - potential forgery attempt");
159
+ }
160
+ if (protectedHeader.typ && protectedHeader.typ.toLowerCase() !== "jwt") {
161
+ throw new Error(`Unexpected typ: ${protectedHeader.typ}`);
162
+ }
163
+ return payload;
164
+ }
165
+ /**
166
+ * Signs multiple payloads with the same private key, returning an array of tokens and their corresponding kids.
167
+ * This is useful for batch operations where multiple JWTs need to be issued at once,
168
+ * ensuring they all use the same key and can be verified against the same public key in the JWKS.
169
+ * @param payloads The array of payloads to be signed as JWTs.
170
+ * @returns An array of objects containing the signed JWTs and their corresponding key IDs.
171
+ */
172
+ async signMany(payloads) {
173
+ const key = await __classPrivateFieldGet(this, _JoseJwksAuthority_instances, "m", _JoseJwksAuthority_getPrivateKey).call(this);
174
+ if (key.kty !== "RSA") {
175
+ throw new Error("Invalid key type, expected RSA");
176
+ }
177
+ if (!key.kid) {
178
+ throw new Error('Key is missing "kid" property');
179
+ }
180
+ const privateKey = await (0, jose_1.importJWK)(key, "RS256");
181
+ const result = [];
182
+ for (const payload of payloads) {
183
+ const token = await new jose_1.SignJWT(payload)
184
+ .setProtectedHeader({ typ: "jwt", alg: "RS256", kid: key.kid })
185
+ .sign(privateKey);
186
+ result.push({ token, kid: key.kid });
187
+ }
188
+ return result;
189
+ }
190
+ }
191
+ exports.JoseJwksAuthority = JoseJwksAuthority;
192
+ _JoseJwksAuthority_store = new WeakMap(), _JoseJwksAuthority_ttl = new WeakMap(), _JoseJwksAuthority_instances = new WeakSet(), _JoseJwksAuthority_generateAndStoreKeyPair = async function _JoseJwksAuthority_generateAndStoreKeyPair() {
193
+ const { publicKey, privateKey } = await (0, jose_1.generateKeyPair)("RS256", {
194
+ modulusLength: 2048,
195
+ extractable: true,
196
+ });
197
+ // Convert to JWK format and add necessary properties
198
+ const privateJwk = await (0, jose_1.exportJWK)(privateKey);
199
+ const publicJwk = await (0, jose_1.exportJWK)(publicKey);
200
+ const kid = crypto.randomUUID();
201
+ privateJwk.kid = kid;
202
+ privateJwk.alg = "RS256";
203
+ privateJwk.use = "sig";
204
+ publicJwk.kid = kid;
205
+ publicJwk.alg = "RS256";
206
+ publicJwk.use = "sig";
207
+ await __classPrivateFieldGet(this, _JoseJwksAuthority_store, "f").storeKeyPair(kid, privateJwk, publicJwk, __classPrivateFieldGet(this, _JoseJwksAuthority_ttl, "f"));
208
+ return { privateJwk, publicJwk };
209
+ }, _JoseJwksAuthority_getPrivateKey = async function _JoseJwksAuthority_getPrivateKey() {
210
+ const storedPrivateJwk = await __classPrivateFieldGet(this, _JoseJwksAuthority_store, "f").getPrivateKey();
211
+ if (storedPrivateJwk) {
212
+ return storedPrivateJwk;
213
+ }
214
+ else {
215
+ const { privateJwk } = await __classPrivateFieldGet(this, _JoseJwksAuthority_instances, "m", _JoseJwksAuthority_generateAndStoreKeyPair).call(this);
216
+ return privateJwk;
217
+ }
218
+ };
@@ -0,0 +1,53 @@
1
+ import type { JwksKeyStore, JwksRotationTimestampStore } from "./types.js";
2
+ /**
3
+ * In-memory implementation of {@link JwksKeyStore} and {@link JwksRotationTimestampStore}.
4
+ *
5
+ * Suitable for single-process deployments and testing. State is not shared
6
+ * across processes and is lost on restart.
7
+ *
8
+ * Use {@link createInMemoryKeyStore} as a convenience factory.
9
+ */
10
+ export declare class InMemoryKeyStore implements JwksKeyStore, JwksRotationTimestampStore {
11
+ private privateKey?;
12
+ private publicKeys;
13
+ private lastRotation;
14
+ /**
15
+ * Stores the current signing key pair. The private key replaces the existing one;
16
+ * the public key is appended to the list with a computed expiry based on `ttl`.
17
+ * @param _kid - The key ID (unused by this implementation).
18
+ * @param privateKey - The private key object to store as the active signing key.
19
+ * @param publicKey - The public key object to expose in the JWKS.
20
+ * @param ttl - Time-to-live in seconds for the public key.
21
+ */
22
+ storeKeyPair(_kid: string, privateKey: object, publicKey: object, ttl: number): Promise<void>;
23
+ /**
24
+ * Retrieves the current private signing key.
25
+ * @returns The private key object, or `undefined` if no key has been stored yet.
26
+ */
27
+ getPrivateKey(): Promise<object | undefined>;
28
+ /**
29
+ * Retrieves all public keys that have not yet expired.
30
+ * Expired keys are automatically pruned from the in-memory list on each call.
31
+ * @returns An array of non-expired public key objects.
32
+ */
33
+ getPublicKeys(): Promise<object[]>;
34
+ /**
35
+ * Retrieves the Unix timestamp (in milliseconds) of the last key rotation.
36
+ * @returns The timestamp of the last rotation, or `0` if no rotation has occurred.
37
+ */
38
+ getLastRotationTimestamp(): Promise<number>;
39
+ /**
40
+ * Stores the Unix timestamp (in milliseconds) of the most recent key rotation.
41
+ * @param msDate - The timestamp to persist.
42
+ */
43
+ setLastRotationTimestamp(msDate: number): Promise<void>;
44
+ }
45
+ /**
46
+ * Creates a new {@link InMemoryKeyStore} instance.
47
+ *
48
+ * Convenience factory for use with {@link JoseJwksAuthority} and {@link JwksRotator}.
49
+ *
50
+ * @returns A fresh in-memory key store with no keys stored.
51
+ */
52
+ export declare function createInMemoryKeyStore(): InMemoryKeyStore;
53
+ //# sourceMappingURL=jwks_key_store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks_key_store.d.ts","sourceRoot":"","sources":["../src/jwks_key_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE3E;;;;;;;GAOG;AACH,qBAAa,gBAAiB,YAAW,YAAY,EAAE,0BAA0B;IAC/E,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,YAAY,CAAa;IAEjC;;;;;;;OAOG;IACG,YAAY,CAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAIlD;;;;OAIG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMxC;;;OAGG;IACG,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjD;;;OAGG;IACG,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAI9D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemoryKeyStore = void 0;
4
+ exports.createInMemoryKeyStore = createInMemoryKeyStore;
5
+ /**
6
+ * In-memory implementation of {@link JwksKeyStore} and {@link JwksRotationTimestampStore}.
7
+ *
8
+ * Suitable for single-process deployments and testing. State is not shared
9
+ * across processes and is lost on restart.
10
+ *
11
+ * Use {@link createInMemoryKeyStore} as a convenience factory.
12
+ */
13
+ class InMemoryKeyStore {
14
+ constructor() {
15
+ Object.defineProperty(this, "privateKey", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ Object.defineProperty(this, "publicKeys", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: []
26
+ });
27
+ Object.defineProperty(this, "lastRotation", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: 0
32
+ });
33
+ }
34
+ /**
35
+ * Stores the current signing key pair. The private key replaces the existing one;
36
+ * the public key is appended to the list with a computed expiry based on `ttl`.
37
+ * @param _kid - The key ID (unused by this implementation).
38
+ * @param privateKey - The private key object to store as the active signing key.
39
+ * @param publicKey - The public key object to expose in the JWKS.
40
+ * @param ttl - Time-to-live in seconds for the public key.
41
+ */
42
+ async storeKeyPair(_kid, privateKey, publicKey, ttl) {
43
+ this.privateKey = privateKey;
44
+ const exp = Date.now() + ttl * 1000;
45
+ this.publicKeys.push({ key: publicKey, exp });
46
+ return await Promise.resolve();
47
+ }
48
+ /**
49
+ * Retrieves the current private signing key.
50
+ * @returns The private key object, or `undefined` if no key has been stored yet.
51
+ */
52
+ async getPrivateKey() {
53
+ return await Promise.resolve(this.privateKey);
54
+ }
55
+ /**
56
+ * Retrieves all public keys that have not yet expired.
57
+ * Expired keys are automatically pruned from the in-memory list on each call.
58
+ * @returns An array of non-expired public key objects.
59
+ */
60
+ async getPublicKeys() {
61
+ const now = Date.now();
62
+ this.publicKeys = this.publicKeys.filter((k) => k.exp > now);
63
+ return await Promise.resolve(this.publicKeys.map((k) => k.key));
64
+ }
65
+ /**
66
+ * Retrieves the Unix timestamp (in milliseconds) of the last key rotation.
67
+ * @returns The timestamp of the last rotation, or `0` if no rotation has occurred.
68
+ */
69
+ async getLastRotationTimestamp() {
70
+ return await Promise.resolve(this.lastRotation);
71
+ }
72
+ /**
73
+ * Stores the Unix timestamp (in milliseconds) of the most recent key rotation.
74
+ * @param msDate - The timestamp to persist.
75
+ */
76
+ async setLastRotationTimestamp(msDate) {
77
+ this.lastRotation = msDate;
78
+ return await Promise.resolve();
79
+ }
80
+ }
81
+ exports.InMemoryKeyStore = InMemoryKeyStore;
82
+ /**
83
+ * Creates a new {@link InMemoryKeyStore} instance.
84
+ *
85
+ * Convenience factory for use with {@link JoseJwksAuthority} and {@link JwksRotator}.
86
+ *
87
+ * @returns A fresh in-memory key store with no keys stored.
88
+ */
89
+ function createInMemoryKeyStore() {
90
+ return new InMemoryKeyStore();
91
+ }
@@ -0,0 +1,36 @@
1
+ import { JwksRotationTimestampStore, KeyGenerator } from "./types.js";
2
+ /**
3
+ * Configuration options for {@link JwksRotator}.
4
+ */
5
+ export interface JwksRotatorOptions {
6
+ /** The key generator used to produce new signing key pairs during rotation. */
7
+ keyGenerator: KeyGenerator;
8
+ /** The store used to read and persist the last rotation timestamp. */
9
+ rotatorKeyStore: JwksRotationTimestampStore;
10
+ /** How often (in milliseconds) new keys should be generated. For example, `7.884e9` for 91 days. */
11
+ rotationIntervalMs: number;
12
+ }
13
+ /**
14
+ * Manages automatic JWKS key rotation based on a configurable interval.
15
+ *
16
+ * Call {@link JwksRotator.checkAndRotateKeys} at service startup and/or on a
17
+ * recurring schedule (e.g. every hour) to ensure that signing keys are rotated
18
+ * before they expire. During rotation the previous public key remains available
19
+ * in the JWKS until its TTL expires, so in-flight tokens continue to verify correctly.
20
+ */
21
+ export declare class JwksRotator {
22
+ private readonly keyGenerator;
23
+ private readonly rotatorKeyStore;
24
+ private readonly rotationIntervalMs;
25
+ /**
26
+ * @param options - Rotation configuration: key generator, timestamp store, and interval.
27
+ */
28
+ constructor({ keyGenerator, rotationIntervalMs, rotatorKeyStore }: JwksRotatorOptions);
29
+ /**
30
+ * Checks if rotation is due, and performs rotation if necessary.
31
+ * Should be called at service startup or on a schedule (e.g. every hour).
32
+ */
33
+ checkAndRotateKeys(): Promise<void>;
34
+ private rotateKeys;
35
+ }
36
+ //# sourceMappingURL=jwks_rotator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks_rotator.d.ts","sourceRoot":"","sources":["../src/jwks_rotator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,YAAY,EAAE,YAAY,CAAC;IAC3B,sEAAsE;IACtE,eAAe,EAAE,0BAA0B,CAAC;IAC5C,oGAAoG;IACpG,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAE5C;;OAEG;gBACS,EAAE,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,EAAE,kBAAkB;IAMrF;;;OAGG;IACU,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;YAYlC,UAAU;CAGzB"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JwksRotator = void 0;
4
+ /**
5
+ * Manages automatic JWKS key rotation based on a configurable interval.
6
+ *
7
+ * Call {@link JwksRotator.checkAndRotateKeys} at service startup and/or on a
8
+ * recurring schedule (e.g. every hour) to ensure that signing keys are rotated
9
+ * before they expire. During rotation the previous public key remains available
10
+ * in the JWKS until its TTL expires, so in-flight tokens continue to verify correctly.
11
+ */
12
+ class JwksRotator {
13
+ /**
14
+ * @param options - Rotation configuration: key generator, timestamp store, and interval.
15
+ */
16
+ constructor({ keyGenerator, rotationIntervalMs, rotatorKeyStore }) {
17
+ Object.defineProperty(this, "keyGenerator", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: void 0
22
+ });
23
+ Object.defineProperty(this, "rotatorKeyStore", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ Object.defineProperty(this, "rotationIntervalMs", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: void 0
34
+ });
35
+ this.keyGenerator = keyGenerator;
36
+ this.rotationIntervalMs = rotationIntervalMs;
37
+ this.rotatorKeyStore = rotatorKeyStore;
38
+ }
39
+ /**
40
+ * Checks if rotation is due, and performs rotation if necessary.
41
+ * Should be called at service startup or on a schedule (e.g. every hour).
42
+ */
43
+ async checkAndRotateKeys() {
44
+ const now = Date.now();
45
+ const lastRotation = await this.rotatorKeyStore.getLastRotationTimestamp();
46
+ if (isNaN(lastRotation) || now - lastRotation >= this.rotationIntervalMs) {
47
+ await this.rotateKeys();
48
+ await this.rotatorKeyStore.setLastRotationTimestamp(now);
49
+ }
50
+ else {
51
+ const _nextIn = this.rotationIntervalMs - (now - lastRotation);
52
+ }
53
+ }
54
+ async rotateKeys() {
55
+ await this.keyGenerator.generateKeyPair();
56
+ }
57
+ }
58
+ exports.JwksRotator = JwksRotator;
@@ -0,0 +1,39 @@
1
+ import type { JwkVerify, JwtDecode, JwtVerify } from "@saurbit/oauth2";
2
+ /**
3
+ * Verifies a JWT using the provided secret or key and returns the decoded payload.
4
+ * Wraps [jose](https://github.com/panva/jose)'s `jwtVerify`.
5
+ *
6
+ * Pass this as the `JwtVerify` argument to `ClientSecretJwt` or `PrivateKeyJwt`
7
+ * from `@saurbit/oauth2`.
8
+ *
9
+ * @param jwt - The compact serialized JWT to verify.
10
+ * @param secretOrKey - The secret (`string` / `Uint8Array`) or `CryptoKey` for verification.
11
+ * @param options - Optional jose verification options (algorithms, audience, issuer, etc.).
12
+ * @returns The verified JWT payload.
13
+ * @throws If the token is invalid, expired, or the signature does not match.
14
+ */
15
+ export declare const verifyJwt: JwtVerify;
16
+ /**
17
+ * Decodes a JWT payload **without** verifying its signature.
18
+ * Wraps [jose](https://github.com/panva/jose)'s `decodeJwt`.
19
+ *
20
+ * Pass this as the `JwtDecode` argument to `ClientSecretJwt` or `PrivateKeyJwt`
21
+ * from `@saurbit/oauth2`.
22
+ *
23
+ * @param jwt - The compact serialized JWT to decode.
24
+ * @returns The decoded JWT payload (unverified).
25
+ */
26
+ export declare const decodeJwt: JwtDecode;
27
+ /**
28
+ * Verifies a JWT whose header embeds the public JWK (`"jwk"` header parameter).
29
+ * The public key is extracted from the JWT header itself and used to verify the signature.
30
+ * Only `ES256` algorithm tokens are accepted.
31
+ *
32
+ * Pass this as the `JwkVerify` argument to `DPoPTokenType` from `@saurbit/oauth2`.
33
+ *
34
+ * @param token - The compact serialized JWT (DPoP proof) to verify.
35
+ * @returns The verified JWT payload.
36
+ * @throws If the token is invalid, the `jwk` header is missing, or signature verification fails.
37
+ */
38
+ export declare const verifyJwk: JwkVerify;
39
+ //# sourceMappingURL=methods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGvE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,SAAS,EAAE,SAGvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,EAAE,SAEvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,EAAE,SAYvB,CAAC"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyJwk = exports.decodeJwt = exports.verifyJwt = void 0;
4
+ const jose_1 = require("jose");
5
+ /**
6
+ * Verifies a JWT using the provided secret or key and returns the decoded payload.
7
+ * Wraps [jose](https://github.com/panva/jose)'s `jwtVerify`.
8
+ *
9
+ * Pass this as the `JwtVerify` argument to `ClientSecretJwt` or `PrivateKeyJwt`
10
+ * from `@saurbit/oauth2`.
11
+ *
12
+ * @param jwt - The compact serialized JWT to verify.
13
+ * @param secretOrKey - The secret (`string` / `Uint8Array`) or `CryptoKey` for verification.
14
+ * @param options - Optional jose verification options (algorithms, audience, issuer, etc.).
15
+ * @returns The verified JWT payload.
16
+ * @throws If the token is invalid, expired, or the signature does not match.
17
+ */
18
+ const verifyJwt = async (jwt, secretOrKey, options) => {
19
+ const { payload } = await (0, jose_1.jwtVerify)(jwt, secretOrKey, options);
20
+ return payload;
21
+ };
22
+ exports.verifyJwt = verifyJwt;
23
+ /**
24
+ * Decodes a JWT payload **without** verifying its signature.
25
+ * Wraps [jose](https://github.com/panva/jose)'s `decodeJwt`.
26
+ *
27
+ * Pass this as the `JwtDecode` argument to `ClientSecretJwt` or `PrivateKeyJwt`
28
+ * from `@saurbit/oauth2`.
29
+ *
30
+ * @param jwt - The compact serialized JWT to decode.
31
+ * @returns The decoded JWT payload (unverified).
32
+ */
33
+ const decodeJwt = (jwt) => {
34
+ return (0, jose_1.decodeJwt)(jwt);
35
+ };
36
+ exports.decodeJwt = decodeJwt;
37
+ /**
38
+ * Verifies a JWT whose header embeds the public JWK (`"jwk"` header parameter).
39
+ * The public key is extracted from the JWT header itself and used to verify the signature.
40
+ * Only `ES256` algorithm tokens are accepted.
41
+ *
42
+ * Pass this as the `JwkVerify` argument to `DPoPTokenType` from `@saurbit/oauth2`.
43
+ *
44
+ * @param token - The compact serialized JWT (DPoP proof) to verify.
45
+ * @returns The verified JWT payload.
46
+ * @throws If the token is invalid, the `jwk` header is missing, or signature verification fails.
47
+ */
48
+ const verifyJwk = async (token) => {
49
+ const { payload } = await (0, jose_1.jwtVerify)(token, (header) => {
50
+ if (!header.jwk)
51
+ throw new Error("Missing JWK");
52
+ return (0, jose_1.importJWK)(header.jwk, header.alg);
53
+ }, {
54
+ algorithms: ["ES256"],
55
+ });
56
+ return payload;
57
+ };
58
+ exports.verifyJwk = verifyJwk;
@@ -0,0 +1,6 @@
1
+ export { JoseJwksAuthority } from "./jose_jwks_authority.js";
2
+ export { createInMemoryKeyStore, InMemoryKeyStore } from "./jwks_key_store.js";
3
+ export { JwksRotator, type JwksRotatorOptions } from "./jwks_rotator.js";
4
+ export { decodeJwt, verifyJwk, verifyJwt } from "./methods.js";
5
+ export type { JwksKeyStore, JwksRotationTimestampStore, JwtAuthority, JwtSigner, JwtVerifier, KeyGenerator, RawKey, RSA, } from "./types.js";
6
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/D,YAAY,EACV,YAAY,EACZ,0BAA0B,EAC1B,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,MAAM,EACN,GAAG,GACJ,MAAM,YAAY,CAAC"}
package/script/mod.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyJwt = exports.verifyJwk = exports.decodeJwt = exports.JwksRotator = exports.InMemoryKeyStore = exports.createInMemoryKeyStore = exports.JoseJwksAuthority = void 0;
4
+ var jose_jwks_authority_js_1 = require("./jose_jwks_authority.js");
5
+ Object.defineProperty(exports, "JoseJwksAuthority", { enumerable: true, get: function () { return jose_jwks_authority_js_1.JoseJwksAuthority; } });
6
+ var jwks_key_store_js_1 = require("./jwks_key_store.js");
7
+ Object.defineProperty(exports, "createInMemoryKeyStore", { enumerable: true, get: function () { return jwks_key_store_js_1.createInMemoryKeyStore; } });
8
+ Object.defineProperty(exports, "InMemoryKeyStore", { enumerable: true, get: function () { return jwks_key_store_js_1.InMemoryKeyStore; } });
9
+ var jwks_rotator_js_1 = require("./jwks_rotator.js");
10
+ Object.defineProperty(exports, "JwksRotator", { enumerable: true, get: function () { return jwks_rotator_js_1.JwksRotator; } });
11
+ var methods_js_1 = require("./methods.js");
12
+ Object.defineProperty(exports, "decodeJwt", { enumerable: true, get: function () { return methods_js_1.decodeJwt; } });
13
+ Object.defineProperty(exports, "verifyJwk", { enumerable: true, get: function () { return methods_js_1.verifyJwk; } });
14
+ Object.defineProperty(exports, "verifyJwt", { enumerable: true, get: function () { return methods_js_1.verifyJwt; } });
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }