@hawcx/oauth-client 4.0.1 → 5.2.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.
package/dist/oauth.js CHANGED
@@ -2,64 +2,322 @@
2
2
  /**
3
3
  * Hawcx OAuth Client
4
4
  *
5
- * Simple, clean implementation using the jose library for JWT verification.
5
+ * Two construction paths:
6
+ *
7
+ * 1. OIDC discovery (recommended) — `HawcxOAuth.fromIssuer(...)` fetches
8
+ * `<issuer>/.well-known/openid-configuration` once at startup and wires
9
+ * up a verifier that enforces signature + iss + aud + exp + nbf on every
10
+ * subsequent `verifyToken` call.
11
+ *
12
+ * 2. Legacy — `new HawcxOAuth({ configId, baseUrl })` hits the fixed
13
+ * endpoints `/oauth2/token` and `/keys` directly. Backward-compatible
14
+ * with v4.x callers, but does NOT verify `iss` or `aud` — the JWT's
15
+ * signature is the only check.
16
+ *
17
+ * Both modes are safe to share across requests.
6
18
  */
7
19
  Object.defineProperty(exports, "__esModule", { value: true });
8
20
  exports.HawcxOAuth = void 0;
9
21
  const jose_1 = require("jose");
22
+ const client_assertion_1 = require("./client-assertion");
10
23
  const errors_1 = require("./errors");
11
24
  const DEFAULT_BASE_URL = 'https://api.hawcx.com';
25
+ const DEFAULT_TIMEOUT_MS = 10000;
26
+ /** Bound the discovery fetch so an unreachable issuer fails fast at startup. */
27
+ const DEFAULT_DISCOVERY_TIMEOUT_MS = 5000;
28
+ /**
29
+ * Spec.md §3.3 recommends a default clock-tolerance of 10 seconds for
30
+ * `exp` / `nbf` checks during JWT verification. Without it jose defaults
31
+ * to 0 and tokens minted under normal NTP drift between issuer and verifier
32
+ * fail at the very first millisecond — the symptom that drove the
33
+ * `exchangeCodeTolerant` workaround in hawcx_admin_console pre-5.0.
34
+ */
35
+ const DEFAULT_CLOCK_TOLERANCE_SECONDS = 10;
36
+ /**
37
+ * Closed id_token signing-algorithm allowlist (spec.md §3.3). Algorithms
38
+ * outside this set — including HS256 and the rest of the symmetric family,
39
+ * RS512, PS*, none — are refused even if a Hawcx issuer advertises them.
40
+ * Mirrors {@code JwtVerifier.DEFAULT_ALGORITHMS} in the Java SDK.
41
+ */
42
+ const DEFAULT_ID_TOKEN_ALGS = Object.freeze([
43
+ 'RS256',
44
+ 'ES256',
45
+ 'ES384',
46
+ 'ES512',
47
+ 'EdDSA',
48
+ ]);
49
+ /**
50
+ * Require https on a discovery-related URL. Plaintext http is permitted only
51
+ * for loopback hosts (localhost / 127.0.0.1 / ::1) to support local dev and
52
+ * tests. OIDC Discovery §4 / OAuth 2.1 §7.4: an http issuer lets an on-path
53
+ * attacker swap the discovery doc and redirect token_endpoint / jwks_uri.
54
+ */
55
+ function requireSecureUrl(label, url) {
56
+ let u;
57
+ try {
58
+ u = new URL(url);
59
+ }
60
+ catch (err) {
61
+ throw new errors_1.DiscoveryError(`${label} is not a valid URL: ${url}`, { cause: err });
62
+ }
63
+ if (u.protocol === 'https:')
64
+ return;
65
+ const loopback = u.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '::1' || u.hostname === '[::1]';
66
+ if (u.protocol === 'http:' && loopback)
67
+ return;
68
+ throw new errors_1.DiscoveryError(`${label} must use https (got: ${url}). Plaintext http is permitted only for loopback hosts.`);
69
+ }
12
70
  /**
13
71
  * Hawcx OAuth Client
14
72
  *
15
- * Handles code exchange and JWT verification using the jose library.
73
+ * Discovery-mode usage (recommended):
16
74
  *
17
- * @example
18
75
  * ```typescript
19
- * const oauth = new HawcxOAuth({ configId: 'your-api-key' });
20
- *
21
- * // Exchange authorization code for tokens
76
+ * const oauth = await HawcxOAuth.fromIssuer({
77
+ * issuer: 'https://api.hawcx.com',
78
+ * configId: process.env.HAWCX_CONFIG_ID!,
79
+ * clientId: process.env.HAWCX_CLIENT_ID!,
80
+ * });
22
81
  * const { idToken, claims } = await oauth.exchangeCode(code, codeVerifier);
23
- * console.log(claims.sub); // user ID
82
+ * ```
83
+ *
84
+ * Legacy usage (no discovery; aud/iss are not checked):
24
85
  *
25
- * // Later, verify an existing token
26
- * const claims = await oauth.verifyToken(idToken);
86
+ * ```typescript
87
+ * const oauth = new HawcxOAuth({ configId: process.env.HAWCX_CONFIG_ID! });
88
+ * const { idToken, claims } = await oauth.exchangeCode(code, codeVerifier);
27
89
  * ```
28
90
  */
29
91
  class HawcxOAuth {
30
- constructor(config) {
92
+ constructor(config, discoveryInit) {
31
93
  this.jwks = null;
94
+ this.clearCacheWarned = false;
32
95
  if (!config.configId) {
33
96
  throw new Error('configId is required');
34
97
  }
35
98
  this.configId = config.configId;
36
- this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
37
- this.timeout = config.timeout ?? 10000;
99
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
100
+ this.clockToleranceSeconds =
101
+ config.clockToleranceSeconds ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
102
+ if (discoveryInit) {
103
+ this.tokenEndpoint = discoveryInit.metadata.token_endpoint;
104
+ this.jwksUrl = discoveryInit.metadata.jwks_uri;
105
+ this.expectedAudience = discoveryInit.clientId;
106
+ this.expectedIssuer = discoveryInit.metadata.issuer;
107
+ this.discoveryMetadata = discoveryInit.metadata;
108
+ this.allowedAlgorithms = discoveryInit.allowedAlgorithms;
109
+ this.clientAssertionSigner = discoveryInit.signer ?? null;
110
+ }
111
+ else {
112
+ const baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
113
+ this.tokenEndpoint = `${baseUrl}/oauth2/token`;
114
+ this.jwksUrl = `${baseUrl}/keys`;
115
+ this.expectedAudience = null;
116
+ this.expectedIssuer = null;
117
+ this.discoveryMetadata = null;
118
+ this.allowedAlgorithms = null;
119
+ this.clientAssertionSigner = null;
120
+ }
121
+ }
122
+ /**
123
+ * Construct a client via OIDC discovery.
124
+ *
125
+ * Fetches `<issuer>/.well-known/openid-configuration`, reads the token
126
+ * endpoint, JWKS URI, and supported id_token signing algorithm, and wires
127
+ * a verifier that enforces signature + `iss` + `aud` + `exp` + `nbf` on
128
+ * subsequent {@link verifyToken} calls.
129
+ *
130
+ * **Discovery is bounded** at `discoveryTimeout` ms (default 5000) so an
131
+ * unreachable issuer fails fast with {@link DiscoveryError} rather than
132
+ * hanging your bootstrap.
133
+ *
134
+ * @throws {DiscoveryError} if discovery fails to resolve or the document
135
+ * is missing required fields (`token_endpoint`, `jwks_uri`,
136
+ * `id_token_signing_alg_values_supported`).
137
+ */
138
+ static async fromIssuer(config) {
139
+ if (!config.issuer)
140
+ throw new Error('issuer is required');
141
+ if (!config.configId)
142
+ throw new Error('configId is required');
143
+ if (!config.clientId)
144
+ throw new Error('clientId is required');
145
+ if (config.clockToleranceSeconds !== undefined && config.clockToleranceSeconds < 0) {
146
+ throw new Error('clockToleranceSeconds must be >= 0');
147
+ }
148
+ const issuer = config.issuer.replace(/\/$/, '');
149
+ requireSecureUrl('issuer', issuer);
150
+ const discoveryUrl = `${issuer}/.well-known/openid-configuration`;
151
+ const timeout = config.discoveryTimeout ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
152
+ let response;
153
+ try {
154
+ response = await fetch(discoveryUrl, {
155
+ method: 'GET',
156
+ headers: { Accept: 'application/json' },
157
+ signal: AbortSignal.timeout(timeout),
158
+ });
159
+ }
160
+ catch (err) {
161
+ throw new errors_1.DiscoveryError(`OIDC discovery failed at ${discoveryUrl}: ${err}`, { cause: err });
162
+ }
163
+ if (!response.ok) {
164
+ throw new errors_1.DiscoveryError(`OIDC discovery returned HTTP ${response.status} at ${discoveryUrl}`);
165
+ }
166
+ let raw;
167
+ try {
168
+ raw = (await response.json());
169
+ }
170
+ catch (err) {
171
+ throw new errors_1.DiscoveryError(`OIDC discovery document at ${discoveryUrl} is not valid JSON`, { cause: err });
172
+ }
173
+ const required = (field) => {
174
+ const value = raw[field];
175
+ if (typeof value !== 'string' || value.length === 0) {
176
+ throw new errors_1.DiscoveryError(`Discovery document at ${discoveryUrl} is missing required field '${field}'`);
177
+ }
178
+ return value;
179
+ };
180
+ const tokenEndpoint = required('token_endpoint');
181
+ const jwksUri = required('jwks_uri');
182
+ // The discovered endpoints must also be https (loopback excepted) — a
183
+ // plaintext issuer could otherwise advertise plaintext sub-resources.
184
+ requireSecureUrl('token_endpoint', tokenEndpoint);
185
+ requireSecureUrl('jwks_uri', jwksUri);
186
+ const algsValue = raw['id_token_signing_alg_values_supported'];
187
+ if (!Array.isArray(algsValue) ||
188
+ algsValue.length === 0 ||
189
+ !algsValue.every((a) => typeof a === 'string')) {
190
+ throw new errors_1.DiscoveryError(`Discovery document at ${discoveryUrl} is missing required field ` +
191
+ `'id_token_signing_alg_values_supported' (must be a non-empty string array)`);
192
+ }
193
+ // spec.md §3.3: the algorithm allowlist MUST be a closed set
194
+ // ({RS256, ES256, ES384, ES512, EdDSA}). Intersect the issuer's advertised
195
+ // list with our allowlist — refuse to construct a client if no overlap.
196
+ // Without this, an issuer advertising only RS512 / PS256 / a symmetric alg
197
+ // would be silently trusted at verify time. Forecloses key-confusion /
198
+ // algorithm-substitution attacks where the JWKS happens to serve a key
199
+ // type matching whatever alg the JWT header advertises.
200
+ const allowedAlgorithms = Object.freeze(algsValue.filter((a) => DEFAULT_ID_TOKEN_ALGS.includes(a)));
201
+ if (allowedAlgorithms.length === 0) {
202
+ throw new errors_1.DiscoveryError(`Discovery document at ${discoveryUrl} advertises id_token signing algorithms ` +
203
+ `${JSON.stringify(algsValue)} — none are in the SDK allowlist ` +
204
+ `${JSON.stringify(DEFAULT_ID_TOKEN_ALGS)} (per spec.md §3.3). ` +
205
+ `Refusing to construct a verifier.`);
206
+ }
207
+ // RFC 8414 §3.3 / OIDC Discovery §4.3: the `issuer` value in the metadata
208
+ // document MUST be identical to the issuer URL the request was made to.
209
+ // Without this cross-check, a discovery document that advertises a different
210
+ // `issuer` (e.g., a shared base URL on a multi-tenant deployment) would be
211
+ // silently trusted, and `verifyToken` would later accept tokens issued by
212
+ // that *other* issuer — a cross-tenant token-acceptance bypass.
213
+ if (typeof raw['issuer'] !== 'string' || raw['issuer'].length === 0) {
214
+ throw new errors_1.DiscoveryError(`Discovery document at ${discoveryUrl} is missing required field 'issuer'`);
215
+ }
216
+ const issuerFromDoc = raw['issuer'].replace(/\/$/, '');
217
+ if (issuerFromDoc !== issuer) {
218
+ throw new errors_1.DiscoveryError(`Discovery document issuer '${issuerFromDoc}' does not match the issuer ` +
219
+ `requested ('${issuer}'). RFC 8414 §3.3 requires these to be identical.`);
220
+ }
221
+ const metadata = {
222
+ ...raw,
223
+ issuer: issuerFromDoc,
224
+ token_endpoint: tokenEndpoint,
225
+ jwks_uri: jwksUri,
226
+ id_token_signing_alg_values_supported: algsValue,
227
+ };
228
+ return new HawcxOAuth({
229
+ configId: config.configId,
230
+ timeout: config.timeout,
231
+ clockToleranceSeconds: config.clockToleranceSeconds,
232
+ }, { metadata, clientId: config.clientId, allowedAlgorithms });
233
+ }
234
+ /**
235
+ * Opt into `private_key_jwt` client authentication (discovery mode only).
236
+ * When set, the token exchange signs and attaches an RFC 7523
237
+ * `client_assertion` (in addition to PKCE). Without it, the exchange stays
238
+ * public-client + PKCE.
239
+ *
240
+ * Intended to be called once right after {@link fromIssuer}, before the
241
+ * client is shared. Returns `this` for chaining.
242
+ */
243
+ /**
244
+ * @beta Experimental `private_key_jwt` surface; may change before stable.
245
+ *
246
+ * Returns a **new** discovery-mode client configured with the signer (sharing
247
+ * this client's resolved discovery state); the original is unchanged, so a
248
+ * configured client is immutable. Chained usage
249
+ * (`(await fromIssuer(...)).withClientAssertion(...)`) is unaffected.
250
+ *
251
+ * Refuses if the OP's discovery doc advertises
252
+ * `token_endpoint_auth_methods_supported` without `private_key_jwt`
253
+ * (the assertion would be silently ignored).
254
+ */
255
+ withClientAssertion(signer) {
256
+ if (this.discoveryMetadata === null) {
257
+ throw new Error('withClientAssertion requires a discovery-mode client built via HawcxOAuth.fromIssuer(...)');
258
+ }
259
+ const methods = this.discoveryMetadata['token_endpoint_auth_methods_supported'];
260
+ if (Array.isArray(methods) && methods.length > 0 && !methods.includes('private_key_jwt')) {
261
+ throw new errors_1.DiscoveryError(`The OP does not advertise 'private_key_jwt' in token_endpoint_auth_methods_supported ` +
262
+ `(${JSON.stringify(methods)}); refusing to attach a client_assertion that would be ` +
263
+ `ignored. Register the client for private_key_jwt in the Hawcx admin console.`);
264
+ }
265
+ return new HawcxOAuth({
266
+ configId: this.configId,
267
+ timeout: this.timeout,
268
+ clockToleranceSeconds: this.clockToleranceSeconds,
269
+ }, {
270
+ metadata: this.discoveryMetadata,
271
+ clientId: this.expectedAudience,
272
+ allowedAlgorithms: this.allowedAlgorithms,
273
+ signer,
274
+ });
38
275
  }
39
276
  /**
40
277
  * Exchange authorization code for id_token and verified claims.
41
278
  *
42
279
  * @param code - Authorization code from auth flow
43
280
  * @param codeVerifier - PKCE code verifier
44
- * @returns id_token and verified claims
281
+ * @param redirectUri - Optional. If your original authorization request
282
+ * registered a `redirect_uri`, RFC 6749 §4.1.3 requires the token
283
+ * request to carry the same value. Omit if the auth request also omitted it.
284
+ * @param expectedNonce - Optional. If your authorization request bound a
285
+ * `nonce`, pass it here so the returned id_token's `nonce` is verified.
286
+ * Required whenever the token carries a nonce (verification refuses
287
+ * otherwise — OIDC Core §3.1.3.7).
45
288
  */
46
- async exchangeCode(code, codeVerifier) {
289
+ async exchangeCode(code, codeVerifier, redirectUri, expectedNonce) {
47
290
  if (!code)
48
291
  throw new errors_1.TokenExchangeError('code is required');
49
292
  if (!codeVerifier)
50
293
  throw new errors_1.TokenExchangeError('codeVerifier is required');
51
- // Exchange code for token
52
- const tokenUrl = `${this.baseUrl}/oauth2/token`;
53
294
  const body = new URLSearchParams();
295
+ body.append('grant_type', 'authorization_code');
54
296
  body.append('code', code);
55
297
  body.append('code_verifier', codeVerifier);
298
+ if (redirectUri) {
299
+ body.append('redirect_uri', redirectUri);
300
+ }
301
+ const signer = this.clientAssertionSigner;
302
+ if (signer !== null && this.discoveryMetadata !== null) {
303
+ // private_key_jwt: include client_id (parity with the demo RP) + the
304
+ // signed assertion. aud is the discovered token_endpoint string exactly,
305
+ // which the OP pins for the assertion. PKCE fields above are retained.
306
+ // In this guarded branch discoveryMetadata !== null, so expectedAudience
307
+ // is the clientId (non-null). Assert rather than mask with ''.
308
+ const clientId = this.expectedAudience;
309
+ body.append('client_id', clientId);
310
+ body.append('client_assertion_type', client_assertion_1.CLIENT_ASSERTION_TYPE);
311
+ body.append('client_assertion', await signer.sign(clientId, this.discoveryMetadata.token_endpoint));
312
+ }
56
313
  let response;
57
314
  try {
58
- response = await fetch(tokenUrl, {
315
+ response = await fetch(this.tokenEndpoint, {
59
316
  method: 'POST',
60
317
  headers: {
61
318
  'Content-Type': 'application/x-www-form-urlencoded',
62
319
  'X-Config-Id': this.configId,
320
+ Accept: 'application/json',
63
321
  },
64
322
  body: body.toString(),
65
323
  signal: AbortSignal.timeout(this.timeout),
@@ -70,81 +328,170 @@ class HawcxOAuth {
70
328
  }
71
329
  if (!response.ok) {
72
330
  let errorMsg = `HTTP ${response.status}`;
73
- try {
74
- const data = (await response.json());
75
- errorMsg = data.error_description || data.error || errorMsg;
76
- }
77
- catch {
78
- // ignore parse errors
331
+ // Read the body as text first so a non-JSON error (e.g. an HTML 502 from
332
+ // a gateway) is preserved in the message instead of being swallowed.
333
+ const rawBody = await response.text().catch(() => '');
334
+ let errCode;
335
+ let errDesc;
336
+ if (rawBody) {
337
+ try {
338
+ const data = JSON.parse(rawBody);
339
+ errCode = data.error;
340
+ errDesc = data.error_description;
341
+ errorMsg = data.error_description || data.error || `${errorMsg}: ${rawBody}`;
342
+ }
343
+ catch {
344
+ errorMsg = `${errorMsg}: ${rawBody}`;
345
+ }
79
346
  }
80
- throw new errors_1.TokenExchangeError(errorMsg, response.status);
347
+ throw new errors_1.TokenExchangeError(errorMsg, response.status, {
348
+ error: errCode,
349
+ errorDescription: errDesc,
350
+ });
81
351
  }
82
352
  const data = (await response.json());
83
353
  const idToken = data.id_token;
84
354
  if (!idToken) {
85
355
  throw new errors_1.TokenExchangeError('Response missing id_token');
86
356
  }
87
- // Verify the token
88
- const claims = await this.verifyToken(idToken);
357
+ const claims = await this.verifyToken(idToken, expectedNonce !== undefined ? { nonce: expectedNonce } : undefined);
89
358
  return { idToken, claims };
90
359
  }
91
360
  /**
92
361
  * Verify a JWT token and return its claims.
93
362
  *
94
- * Uses the jose library which handles all signature formats automatically,
95
- * including ECDSA signature conversion between P1363 and DER formats.
363
+ * In **discovery mode**, enforces signature + `iss` + `aud` + `exp` + `nbf`
364
+ * (and the optional `nonce` claim if `options.nonce` is provided).
96
365
  *
97
- * @param token - JWT token to verify
98
- * @returns Verified claims
366
+ * In **legacy mode**, only the signature is verified. Audience and issuer
367
+ * claims are not checked, matching the pre-5.x behavior.
99
368
  */
100
- async verifyToken(token) {
369
+ async verifyToken(token, options) {
101
370
  if (!token)
102
371
  throw new errors_1.TokenVerificationError('token is required');
103
372
  try {
104
- // Get or create the JWKS
105
373
  const jwks = this.getJwks();
106
- // Verify the token using jose - handles all the crypto automatically
107
- const { payload } = await (0, jose_1.jwtVerify)(token, jwks);
374
+ const { payload } = await (0, jose_1.jwtVerify)(token, jwks, {
375
+ ...(this.expectedIssuer !== null && { issuer: this.expectedIssuer }),
376
+ ...(this.expectedAudience !== null && { audience: this.expectedAudience }),
377
+ // Enforce the allowlist that fromIssuer pinned at construction time.
378
+ // Without this, jose accepts any algorithm whose key happens to be
379
+ // present in the JWKS — a key-confusion / alg-substitution path.
380
+ // `as string[]` because jose's type is mutable; ours is readonly.
381
+ ...(this.allowedAlgorithms !== null && {
382
+ algorithms: this.allowedAlgorithms,
383
+ }),
384
+ // Fail closed on a token with no `exp`/`iat` (jose only checks them
385
+ // when present). In discovery mode we require both; legacy mode keeps
386
+ // 4.x behavior. A missing exp would otherwise read as never-expiring.
387
+ ...(this.discoveryMetadata !== null && { requiredClaims: ['exp', 'iat'] }),
388
+ clockTolerance: this.clockToleranceSeconds,
389
+ });
390
+ if (options?.nonce !== undefined) {
391
+ if (payload.nonce !== options.nonce) {
392
+ throw new errors_1.TokenVerificationError(`Token nonce mismatch (expected '${options.nonce}', got '${payload.nonce ?? '<missing>'}')`);
393
+ }
394
+ }
395
+ else if (payload.nonce !== undefined && payload.nonce !== null) {
396
+ // OIDC Core §3.1.3.7 step 11: a present nonce MUST be verified. We have
397
+ // no expected value, so refuse rather than silently accept — pass the
398
+ // nonce you bound at /authorize via options.nonce (or exchangeCode's
399
+ // expectedNonce). Only RPs that actually use a nonce hit this.
400
+ throw new errors_1.TokenVerificationError('Token carries a nonce but none was provided to verify against. ' +
401
+ 'Pass the expected nonce (options.nonce / exchangeCode expectedNonce).');
402
+ }
108
403
  return payload;
109
404
  }
110
405
  catch (err) {
111
406
  if (err instanceof errors_1.TokenVerificationError)
112
407
  throw err;
113
408
  const message = err instanceof Error ? err.message : String(err);
409
+ // jose exposes machine-readable codes via `(err as any).code` for
410
+ // common JWT failures. Prefer that over string matching when present;
411
+ // fall back to substring sniffs for older versions / wrapped errors.
412
+ const code = err instanceof Error ? err.code : undefined;
114
413
  // Map jose errors to our error types
115
- if (message.includes('expired')) {
414
+ if (code === 'ERR_JWT_EXPIRED' || message.includes('expired')) {
116
415
  throw new errors_1.TokenVerificationError('Token expired');
117
416
  }
417
+ if (code === 'ERR_JWT_CLAIM_VALIDATION_FAILED') {
418
+ // The `claim` property tells us which claim mismatched
419
+ const claim = err.claim;
420
+ if (claim === 'iss')
421
+ throw new errors_1.TokenVerificationError(`Unexpected JWT issuer: ${message}`);
422
+ if (claim === 'aud')
423
+ throw new errors_1.TokenVerificationError(`Unexpected JWT audience: ${message}`);
424
+ if (claim === 'nbf')
425
+ throw new errors_1.TokenVerificationError('Token not yet valid');
426
+ }
118
427
  if (message.includes('not yet valid') || message.includes('nbf')) {
119
428
  throw new errors_1.TokenVerificationError('Token not yet valid');
120
429
  }
121
430
  if (message.includes('signature')) {
122
431
  throw new errors_1.TokenVerificationError('Invalid signature');
123
432
  }
433
+ if (message.includes('audience') || message.includes('"aud"')) {
434
+ throw new errors_1.TokenVerificationError(`Unexpected JWT audience: ${message}`);
435
+ }
436
+ if (message.includes('issuer') || message.includes('"iss"')) {
437
+ throw new errors_1.TokenVerificationError(`Unexpected JWT issuer: ${message}`);
438
+ }
124
439
  throw new errors_1.TokenVerificationError(`Token verification failed: ${message}`);
125
440
  }
126
441
  }
127
442
  /**
128
- * Get or create the remote JWKS.
129
- * The jose library handles caching internally.
443
+ * Get or create the remote JWKS. jose handles caching + concurrent-fetch
444
+ * coalescing internally.
445
+ *
446
+ * No single-flight guard is needed here: this method is fully synchronous
447
+ * (no `await` between the null-check and the assignment), so within Node's
448
+ * single-threaded event loop the first caller assigns `this.jwks` before any
449
+ * other code runs. Two concurrent `verifyToken` calls therefore cannot create
450
+ * duplicate `RemoteJWKSet` instances.
130
451
  */
131
452
  getJwks() {
132
453
  if (!this.jwks) {
133
- const jwksUrl = new URL(`${this.baseUrl}/keys`);
454
+ const jwksUrl = new URL(this.jwksUrl);
455
+ // Only set the X-Config-Id header for the legacy `/keys` endpoint; the
456
+ // OIDC-discovered `/.well-known/jwks.json` is public and tenant-agnostic.
457
+ const isLegacyKeysEndpoint = this.discoveryMetadata === null;
134
458
  this.jwks = (0, jose_1.createRemoteJWKSet)(jwksUrl, {
135
- headers: {
136
- 'X-Config-Id': this.configId,
137
- },
459
+ // Bound the JWKS fetch explicitly (jose's default is 5s; pin it to the
460
+ // configured request timeout so a stalled keys endpoint can't hang
461
+ // verification longer than intended).
462
+ timeoutDuration: this.timeout,
463
+ ...(isLegacyKeysEndpoint && {
464
+ headers: { 'X-Config-Id': this.configId },
465
+ }),
138
466
  });
139
467
  }
140
468
  return this.jwks;
141
469
  }
142
470
  /**
143
- * Clear the JWKS cache (useful for testing or key rotation).
471
+ * Drop the cached JWKS so the next `verifyToken` re-fetches it. Useful after
472
+ * an operator-triggered signing-key rotation, or in tests.
473
+ *
474
+ * Canonical name, aligned with the Java (`refreshJwks`) and Python
475
+ * (`refresh_jwks`) SDKs.
144
476
  */
145
- clearCache() {
477
+ refreshJwks() {
146
478
  this.jwks = null;
147
479
  }
480
+ /**
481
+ * @deprecated since 5.1.0 — renamed to {@link refreshJwks} for cross-SDK
482
+ * consistency. Will be removed in 6.0.0. Behaviour is identical.
483
+ */
484
+ clearCache() {
485
+ // One-time deprecation nudge per instance (per-instance so tests and
486
+ // multi-tenant setups each get warned, while a single instance doesn't spam).
487
+ if (!this.clearCacheWarned) {
488
+ this.clearCacheWarned = true;
489
+ // eslint-disable-next-line no-console
490
+ console.warn('[@hawcx/oauth-client] clearCache() is deprecated; use refreshJwks(). ' +
491
+ 'clearCache() will be removed in 6.0.0.');
492
+ }
493
+ this.refreshJwks();
494
+ }
148
495
  }
149
496
  exports.HawcxOAuth = HawcxOAuth;
150
497
  //# sourceMappingURL=oauth.js.map
package/dist/oauth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,15 @@
1
- import { DelegationTransport } from '../delegation/transport';
1
+ import { type JsonObject, type SecureRequestOptions } from '../delegation/transport';
2
2
  import { DelegationKeyMaterial } from '../delegation/factory';
3
+ import { type JwtBearerTransportOptions } from '../management/jwt_transport';
3
4
  import { StepUpClientBaseOptions, StepUpConsumeOptions, StepUpConsumeResponse, StepUpStartOptions, StepUpStartTokenResponse } from './types';
5
+ /**
6
+ * Structural interface satisfied by both `DelegationTransport` (ECIES) and
7
+ * `JwtBearerTransport` (private_key_jwt). Allows `StepUpClient` to accept
8
+ * either transport without a hard dependency on the concrete types.
9
+ */
10
+ interface StepUpTransport {
11
+ send<TReq extends JsonObject, TRes extends JsonObject>(options: Pick<SecureRequestOptions<TReq>, 'endpoint' | 'payload' | 'headers'>): Promise<TRes>;
12
+ }
4
13
  export interface StepUpClientSecretKeyOptions extends StepUpClientBaseOptions {
5
14
  secretKey: string;
6
15
  apiKey?: string;
@@ -8,19 +17,69 @@ export interface StepUpClientSecretKeyOptions extends StepUpClientBaseOptions {
8
17
  export type StepUpClientKeyOptions = StepUpClientBaseOptions & DelegationKeyMaterial & {
9
18
  apiKey?: string;
10
19
  };
20
+ /**
21
+ * Options for {@link StepUpClient.withPrivateKeyJwt}. Combines the
22
+ * JWT-bearer transport credentials with the standard step-up base options.
23
+ */
24
+ export interface StepUpClientPrivateKeyJwtOptions extends StepUpClientBaseOptions {
25
+ /** Ed25519 private signing key (PKCS#8 PEM, JWK object, or jose KeyLike). */
26
+ oidcSigningKey: JwtBearerTransportOptions['oidcSigningKey'];
27
+ /** Key ID embedded in the JWT `kid` header. */
28
+ kid: string;
29
+ /** OAuth client ID (`iss` / `sub` in the per-request JWT). */
30
+ clientId: string;
31
+ /** Tenant config identifier; forwarded as `X-Config-Id`. */
32
+ configId: string;
33
+ /** Optional API key forwarded as `apikey` header. */
34
+ apiKey?: string;
35
+ /** JWT time-to-live in seconds (default: 60). */
36
+ jwtTtlSeconds?: number;
37
+ }
11
38
  export declare class StepUpClient {
12
39
  private transport;
13
40
  private options;
14
41
  private apiKey?;
15
42
  constructor(options: {
16
- transport: DelegationTransport;
43
+ transport: StepUpTransport;
17
44
  clientOptions: StepUpClientBaseOptions;
18
45
  apiKey?: string;
19
46
  });
47
+ /**
48
+ * @deprecated since 5.2.0 — the ECIES secret-key credential format is
49
+ * proprietary and will be removed in a future major version. Migrate to
50
+ * {@link StepUpClient.withPrivateKeyJwt}, which reuses the standard OIDC
51
+ * Ed25519 `private_key_jwt` signing key already registered in the admin
52
+ * console (one key, standards-based).
53
+ */
20
54
  static fromSecretKey(options: StepUpClientSecretKeyOptions): StepUpClient;
55
+ /**
56
+ * @deprecated since 5.2.0 — the ECIES key-material format is proprietary
57
+ * and will be removed in a future major version. Migrate to
58
+ * {@link StepUpClient.withPrivateKeyJwt}.
59
+ */
21
60
  static fromKeys(options: StepUpClientKeyOptions): StepUpClient;
61
+ /**
62
+ * Build a `StepUpClient` authenticated via EdDSA `private_key_jwt` (Path A).
63
+ *
64
+ * Reuses the same Ed25519 signing key registered in the Hawcx admin console
65
+ * for OIDC `private_key_jwt` — one key, standards-based, no separate ECIES
66
+ * credential blob required.
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const stepUp = StepUpClient.withPrivateKeyJwt({
71
+ * baseUrl: 'https://api.hawcx.com',
72
+ * configId: process.env.HAWCX_CONFIG_ID!,
73
+ * clientId: process.env.HAWCX_CLIENT_ID!,
74
+ * oidcSigningKey: JSON.parse(process.env.HAWCX_PRIVATE_JWK!),
75
+ * kid: 'your-key-id',
76
+ * });
77
+ * ```
78
+ */
79
+ static withPrivateKeyJwt(options: StepUpClientPrivateKeyJwtOptions): StepUpClient;
22
80
  private buildExtraHeaders;
23
81
  startToken(options: StepUpStartOptions): Promise<StepUpStartTokenResponse>;
24
82
  consumeReceipt(options: StepUpConsumeOptions): Promise<StepUpConsumeResponse>;
25
83
  }
84
+ export {};
26
85
  //# sourceMappingURL=client.d.ts.map