@hawcx/oauth-client 6.0.0 → 6.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/CHANGELOG.md CHANGED
@@ -7,6 +7,135 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [6.2.0] - 2026-11-02
11
+
12
+ Additive, apart from these `exchangeCode` / token-endpoint behaviour changes
13
+ (details under Changed): a 3xx from the token endpoint is no longer followed
14
+ and throws `TokenExchangeError`; a 2xx token response that is not a JSON
15
+ object throws `TokenExchangeError` instead of a raw `SyntaxError`/`TypeError`
16
+ (or being returned as-is); a 2xx body cut off mid-read throws a network
17
+ `TokenExchangeError` carrying that 2xx `statusCode`.
18
+
19
+ ### Added
20
+
21
+ - **Profile service client.** `ProfileClient` (`oauth.profile(options?)` or
22
+ `new ProfileClient(oauth, options?)`), built from a discovery-mode
23
+ `HawcxOAuth` with a `private_key_jwt` signer. Machine methods (RFC 6749 §4.4
24
+ `client_credentials`): `getUser`, `listUsers`/`iterUsers`, `createUser`,
25
+ `updateUser`, `replaceUser`, `deleteUser`, `getConsents`, `setConsent`,
26
+ `getAudit`/`iterAudit`. `forUser(idToken)` returns a `UserProfileClient`
27
+ (RFC 8693 exchange of the ID token for a `profile:self` token): `getMe`,
28
+ `updateMe`, `getMyConsents`, `setMyConsent`, `getMyAudit`/`iterMyAudit`.
29
+ Tokens are cached with early refresh and single-flight minting; expiry is
30
+ counted from when the token request was sent. The early refresh runs in the
31
+ background, so every caller (the one that triggered it included) gets the
32
+ still-valid cached token; if it fails (any error, e.g. a signer error too)
33
+ that token stays in use and no other early refresh is tried for 30 s (or
34
+ until expiry, if sooner). `forUser` tokens are not refreshed early (the OP
35
+ caps their `exp` at the ID token's), only at expiry. A 401 is
36
+ replayed once with a fresh token; for `forUser`, if that re-exchange fails
37
+ with a non-transient token error (e.g. `invalid_grant`, the ID token has
38
+ expired), the original 401 `ProfileApiError` is thrown (token error as
39
+ `cause`); GET/PUT/DELETE retry on transient
40
+ failures (Retry-After honoured, capped at 30 s); a token-endpoint network
41
+ error, 408, 429, 502, 503 or 504 is retried for every method within the same
42
+ budget (no profile request was sent), with backoff only (its Retry-After is
43
+ ignored); POST/PATCH are otherwise never
44
+ retried; a DELETE answered 404 counts as success once any earlier attempt of
45
+ the call may have run (no HTTP response, or 504; not 502, which is
46
+ ambiguous but left out because a false "deleted" is worse). Errors that
47
+ mean nothing was sent do not count: `fetch()` rejecting with a connect/DNS
48
+ or TLS-handshake code (ECONNREFUSED, ENOTFOUND, EAI_*, connect timeouts,
49
+ CERT_*/ERR_TLS_*/ERR_SSL_*), or a routing code (EHOSTUNREACH, ENETUNREACH,
50
+ EHOSTDOWN, ENETDOWN, EADDRNOTAVAIL) with `syscall: 'connect'`; an injected
51
+ `fetch` whose errors carry none of these counts as may-have-run. A failure
52
+ reading a 2xx body always counts, and its error keeps the 2xx status
53
+ (title `Response body read failed`) rather than 0, since the call ran.
54
+ Redirects are not
55
+ followed; path parameters, and `createUser`'s `user_id`, must be one segment
56
+ (`/`, `\` and control characters refused). `deleteUser` counts any 2xx as
57
+ success, whatever its body (empty, whitespace, not JSON, not an object).
58
+ Iterators stop on a null, missing or empty-string `next_cursor`. An
59
+ explicitly empty `scopes` list throws `HawcxError` when the client is built
60
+ (omit `scopes` for the client's default). `timeout` (default 10000) bounds
61
+ each token-endpoint call too, not only profile requests, whatever the
62
+ `HawcxOAuth` client's own `timeout`. `ProfilePatch.status` is `string` (no
63
+ `null`: the server answers 422 to a null status).
64
+ - **`forUser` is for right after sign-in.** The OP caps the delegated token's
65
+ `exp` at the ID token's (`id_token_ttl_seconds`, 60 s by default), so pass
66
+ the ID token `exchangeCode` just returned; for later work on a user's
67
+ profile use the machine methods with their `user_id` (the `sub` claim).
68
+ - **`ProfileApiError`** (`HawcxError` subclass) mapped from RFC 9457
69
+ problem+json: `status`, `title`, `detail`, `type`, `instance`, `requestId`,
70
+ `retryAfter`, `missing`, `fields`, `body`, `rawBody` (the body text, capped
71
+ at 4096 UTF-16 code units, kept out of `message`; on a malformed 2xx only
72
+ when the body is a markup page starting with `<`, since any other body may
73
+ be a mangled profile, i.e. PII). A Kong-style `{"message"}` body
74
+ (no non-blank `title`/`detail`) uses `message` as `detail`; `requestId`
75
+ falls back to the body's `request_id`. A non-204 2xx profile body that is
76
+ not a JSON object (or lacks a required field), and a 204, to any method but
77
+ `deleteUser`, throw `ProfileApiError` "Unexpected response body"; an
78
+ unparseable body "Invalid JSON response".
79
+
80
+ ### Changed
81
+
82
+ - `exchangeCode` no longer follows redirects from the token endpoint: a 3xx is
83
+ now a `TokenExchangeError`, so the code, PKCE verifier and client assertion
84
+ are never re-posted to another host. A token-endpoint network error now
85
+ carries the underlying error as `cause` and sets the new
86
+ `TokenExchangeError.network` flag (`true` when no complete HTTP response
87
+ came back, including a 2xx body cut off mid-read, which keeps the 2xx in
88
+ `statusCode`). A 2xx token response
89
+ that was read in full but is not a JSON object now throws a non-network
90
+ `TokenExchangeError` ("Invalid JSON in token response") instead of a raw
91
+ `SyntaxError`/`TypeError`.
92
+
93
+ ## [6.1.0] - 2026-10-12
94
+
95
+ Additive; no behaviour change for existing callers.
96
+
97
+ ### Added
98
+
99
+ - **Custom domains.** Optional `customDomain` on
100
+ `HawcxOAuth.fromIssuer(...)` (e.g. `https://auth.example.com`), validated as
101
+ a raw string (same rules as the Python and Java SDKs): `scheme://host[:port]`
102
+ only, https (http only for loopback), ASCII/punycode hostname (no `*`, `_`,
103
+ trailing dot, numeric-looking last label) or canonical IP literal, port
104
+ 1-65535 without leading zeros, no userinfo, path, query, fragment,
105
+ whitespace, `\` or `%`; IPv6 without embedded IPv4. Canonicalised
106
+ (lower-cased, default port dropped, IPv6 in RFC 5952 form, loopback checked
107
+ on the canonical host).
108
+ `null`/`undefined` = unset. An invalid value throws `HawcxOAuthError`
109
+ (`error: 'invalid_custom_domain'`) from `fromIssuer` before any fetch. It
110
+ changes only the browser URLs below; discovery, token exchange, JWKS and the
111
+ `iss` check stay on the issuer.
112
+ - **`buildAuthorizeUrl(options)`** builds the `/authorize` URL from the
113
+ discovered `authorization_endpoint`: origin (custom domain if set, else the
114
+ endpoint's raw scheme and authority) + the
115
+ endpoint's raw path and query, copied verbatim, then `response_type=code`,
116
+ `client_id`, `redirect_uri`, `scope` (blank → `openid`), `state`,
117
+ `code_challenge`, `code_challenge_method=S256`, and optional `nonce`,
118
+ `prompt` (blank = empty or ASCII whitespace only → omitted) and `extraParams` (non-empty keys, string values,
119
+ insertion order kept). `extraParams` cannot set any SDK parameter, `request`
120
+ or `request_uri`, nor a name already in the endpoint query. Missing or blank `redirectUri` / `state` / `codeChallenge`
121
+ or a bad `extraParams` entry throws `HawcxOAuthError`
122
+ (`error: 'invalid_request'`).
123
+ - **`buildEndSessionUrl(options?)`** builds the logout URL from the discovered
124
+ `end_session_endpoint` the same way; always sends `client_id`, plus
125
+ `post_logout_redirect_uri`, `id_token_hint` and `state` when non-blank.
126
+ `null` options = `{}`.
127
+ - Both helpers are discovery-mode only (legacy mode throws `HawcxOAuthError`,
128
+ `error: 'issuer_mode_required'`). A discovered endpoint that is missing, not
129
+ https (loopback http ok), has userinfo, no host, any `#`, a character
130
+ outside printable ASCII or any of `` \ " < > ^ ` { | } ``, or a query that
131
+ already sets one of the helper's SDK parameters (authorize: also `request`,
132
+ `request_uri`) throws `DiscoveryError`
133
+ (`error: 'invalid_discovery_document'`) when the helper is called, never
134
+ from `fromIssuer`, so discovery documents without these fields keep working.
135
+ - URLs are semantically, not byte-for-byte, equal across the Node, Python and
136
+ Java SDKs (parameter encoding differs on e.g. `~`, `*`, space).
137
+ - New exported types `AuthorizeUrlOptions` and `EndSessionUrlOptions`.
138
+
10
139
  ## [6.0.0] - 2026-10-10
11
140
 
12
141
  First public release since 5.2.0. **Major version** because of the BREAKING
package/README.md CHANGED
@@ -108,6 +108,7 @@ is the discovered `token_endpoint`, and EdDSA is the only accepted algorithm.
108
108
  | Module | Purpose |
109
109
  |--------|---------|
110
110
  | **OAuth** | Token exchange and JWT verification |
111
+ | **Profile** | Hawcx profile service: users, consents, audit (machine or on behalf of a user) |
111
112
  | **Delegation** | Server-to-server user management (MFA, devices) |
112
113
  | **Step-Up** | Step-up authentication flows |
113
114
 
@@ -130,11 +131,56 @@ const oauth = await HawcxOAuth.fromIssuer({
130
131
  clientId: 'your-client-id', // Required: expected `aud` on id_tokens
131
132
  timeout: 10000, // Optional: per-request timeout in ms
132
133
  discoveryTimeout: 5000, // Optional: discovery fetch timeout in ms
134
+ customDomain: 'https://auth.example.com', // Optional: custom domain origin for browser URLs (a CNAME to Hawcx)
133
135
  });
134
136
 
135
137
  oauth.discoveryMetadata; // resolved OIDC discovery doc, for diagnostics
136
138
  ```
137
139
 
140
+ `customDomain` must be `scheme://host[:port]` only: https (http only for
141
+ loopback), an ASCII/punycode hostname (`xn--...` for IDN) or an IP literal,
142
+ port 1-65535 without leading zeros, no userinfo, path, query or fragment (one
143
+ trailing `/` allowed). IPv6 literals may not embed IPv4 (no `.`). It is
144
+ canonicalised (lower-cased, default port dropped, IPv6 in RFC 5952 form, so
145
+ `http://[0:0::1]` becomes `http://[::1]` and counts as loopback). `null`/`undefined` means unset. An invalid value (including `''`,
146
+ whitespace, `*.example.com` or a trailing dot) throws `HawcxOAuthError`
147
+ (`error: 'invalid_custom_domain'`) from `fromIssuer`, before any network call.
148
+ The SDK only checks the form: `customDomain` must also be the TXT-verified
149
+ domain registered for your client's own tenant, set up as a DNS CNAME to Hawcx
150
+ (Hawcx serves its certificate). Another tenant's verified domain is always refused; when the tenant has
151
+ custom-domain enforcement on, every host other than its own verified domains
152
+ (including IP literals and single-label hosts) is refused too.
153
+
154
+ ##### Custom domain
155
+
156
+ When you serve Hawcx sign-in on your own domain (`auth.example.com`, a DNS
157
+ CNAME to Hawcx), the browser must go to `/authorize` and end-session on that
158
+ domain, while token exchange, JWKS and the `iss` check stay on the Hawcx
159
+ issuer.
160
+ `customDomain` swaps only the scheme + host + port of the two browser URLs;
161
+ everything server-to-server is unchanged.
162
+
163
+ ```typescript
164
+ const oauth = await HawcxOAuth.fromIssuer({
165
+ issuer: 'https://api.hawcx.com',
166
+ configId: process.env.HAWCX_CONFIG_ID!,
167
+ clientId: process.env.HAWCX_CLIENT_ID!,
168
+ customDomain: 'https://auth.example.com',
169
+ });
170
+
171
+ // {issuer}/authorize → https://auth.example.com/authorize?response_type=code&client_id=...
172
+ const loginUrl = oauth.buildAuthorizeUrl({ redirectUri, state, codeChallenge, nonce });
173
+
174
+ // Token exchange still POSTs to https://api.hawcx.com/oauth2/token
175
+ const { claims } = await oauth.exchangeCode(code, codeVerifier, redirectUri, nonce);
176
+
177
+ // {issuer}/oauth2/end_session → https://auth.example.com/oauth2/end_session?client_id=...
178
+ const logoutUrl = oauth.buildEndSessionUrl({ postLogoutRedirectUri, idTokenHint: idToken });
179
+ ```
180
+
181
+ The `nonce` passed to `buildAuthorizeUrl` must be passed again to
182
+ `exchangeCode` (4th argument).
183
+
138
184
  Throws `DiscoveryError` if the discovery document is unreachable, returns
139
185
  non-2xx, isn't valid JSON, or is missing any of `token_endpoint`,
140
186
  `jwks_uri`, `id_token_signing_alg_values_supported`.
@@ -209,6 +255,61 @@ interface JwtClaims {
209
255
  }
210
256
  ```
211
257
 
258
+ #### `buildAuthorizeUrl(options: AuthorizeUrlOptions): string`
259
+
260
+ Build the browser `/authorize` URL (discovery mode only; no network I/O).
261
+ Base is the discovered `authorization_endpoint` (`{issuer}/authorize`), with
262
+ its origin replaced by `customDomain` when set (otherwise the endpoint's scheme
263
+ and authority are kept exactly as discovered). Its path and query are copied
264
+ verbatim; the SDK parameters are appended after them. The URLs are
265
+ semantically, not byte-for-byte, equal to the Python and Java SDKs' (parameter
266
+ encoding of e.g. `~`, `*` and space differs).
267
+
268
+ ```typescript
269
+ const url = oauth.buildAuthorizeUrl({
270
+ redirectUri: 'https://app.example.com/auth/callback', // Required
271
+ state, // Required
272
+ codeChallenge, // Required (S256)
273
+ nonce, // Optional, recommended; pass again to exchangeCode
274
+ scope: 'openid email', // Optional, default 'openid'
275
+ prompt: 'login', // Optional
276
+ extraParams: { login_hint: 'a@example.com' }, // Optional
277
+ });
278
+ ```
279
+
280
+ Always sets `response_type=code`, `client_id` and `code_challenge_method=S256`.
281
+ Blank (empty or ASCII-whitespace-only: space, `\t`, `\n`, `\v`, `\f`, `\r`;
282
+ NBSP is not blank) optional values are omitted; a blank `scope`
283
+ becomes `openid`. `extraParams` keys must be non-empty and values strings, and
284
+ cannot be any parameter the SDK sets, `request`, `request_uri`, or a name
285
+ already in the endpoint's query (names are case-sensitive). Throws
286
+ `HawcxOAuthError` (`error: 'invalid_request'`) for a missing or blank required
287
+ option or a bad `extraParams` entry, `HawcxOAuthError`
288
+ (`error: 'issuer_mode_required'`) in legacy mode, and `DiscoveryError`
289
+ (`error: 'invalid_discovery_document'`) if the discovered
290
+ `authorization_endpoint` is missing, not https, has userinfo, a `#`, any
291
+ character outside printable ASCII or any of `` \ " < > ^ ` { | } ``, or its query
292
+ already sets one of the SDK's parameters, `request` or `request_uri`.
293
+
294
+ #### `buildEndSessionUrl(options?: EndSessionUrlOptions): string`
295
+
296
+ Build the browser logout URL from the discovered `end_session_endpoint`
297
+ (`{issuer}/oauth2/end_session`, same `customDomain` origin swap). Always sends
298
+ `client_id`; blank options are omitted. `buildEndSessionUrl(null)` is the same
299
+ as `buildEndSessionUrl({})`.
300
+
301
+ ```typescript
302
+ const url = oauth.buildEndSessionUrl({
303
+ postLogoutRedirectUri: 'https://app.example.com/', // Optional
304
+ idTokenHint: idToken, // Optional
305
+ state, // Optional
306
+ });
307
+ ```
308
+
309
+ Same errors as `buildAuthorizeUrl` for legacy mode and the discovered
310
+ `end_session_endpoint` (whose query may not set `client_id`,
311
+ `post_logout_redirect_uri`, `id_token_hint` or `state`).
312
+
212
313
  #### `refreshJwks(): void`
213
314
 
214
315
  Reset the JWKS cache so the next verification refetches keys (useful for testing
@@ -251,6 +352,122 @@ try {
251
352
 
252
353
  ---
253
354
 
355
+ ## Profile Module
356
+
357
+ `ProfileClient` calls the Hawcx profile service (`{issuer}/profile/v1/...`).
358
+ Backend only: it is built from a discovery-mode `HawcxOAuth` with a
359
+ `private_key_jwt` signer (see [Confidential clients](#confidential-clients-private_key_jwt)).
360
+
361
+ Client configuration in the Hawcx admin console:
362
+
363
+ - **Machine** (`ProfileClient`): `client_credentials` with
364
+ `allowed_scopes` covering what you call (`users:read`, `users:write`,
365
+ `users:delete`, `consent:read`, `consent:write`, `audit:read`).
366
+ - **On behalf of a user** (`forUser`): `token_exchange_scopes: ["profile:self"]`,
367
+ and the user's ID token must have been issued to this same client.
368
+
369
+ ```typescript
370
+ import { HawcxOAuth, ClientAssertionSigner, ProfileApiError } from '@hawcx/oauth-client';
371
+
372
+ const oauth = (
373
+ await HawcxOAuth.fromIssuer({ issuer, configId, clientId })
374
+ ).withClientAssertion(ClientAssertionSigner.ed25519FromJwk(privateJwk));
375
+
376
+ const profiles = oauth.profile(); // or new ProfileClient(oauth, options)
377
+
378
+ // Machine: any user in the tenant
379
+ const user = await profiles.getUser('user-123');
380
+ await profiles.updateUser('user-123', { display_name: 'Ada', locale: null }); // null clears, omitted = unchanged
381
+ await profiles.setConsent('user-123', 'marketing', { status: 'granted' });
382
+ for await (const p of profiles.iterUsers({ status: 'active' })) console.log(p.user_id);
383
+
384
+ // On behalf of the user, right after sign-in with the ID token you just received
385
+ const me = profiles.forUser(idToken);
386
+ const mine = await me.getMe();
387
+ await me.updateMe({ given_name: 'Ada' });
388
+ ```
389
+
390
+ Machine methods: `getUser`, `listUsers`, `iterUsers`, `createUser`,
391
+ `updateUser`, `replaceUser`, `deleteUser`, `getConsents`, `setConsent`,
392
+ `getAudit`, `iterAudit`. User methods: `getMe`, `updateMe`, `getMyConsents`,
393
+ `setMyConsent`, `getMyAudit`, `iterMyAudit`.
394
+
395
+ `forUser` only works with a fresh ID token: the OP caps the delegated token's
396
+ lifetime at the ID token's `exp`, and ID tokens live 60 s by default
397
+ (`id_token_ttl_seconds`). Use it with the ID token you just received at
398
+ sign-in; for later work on a user's profile, use the machine methods with
399
+ their `user_id` (the ID token's `sub` claim).
400
+
401
+ List methods take
402
+ `{ cursor?, limit? }` and return `{ items, nextCursor }` (consents:
403
+ `{ current, history, nextCursor }`).
404
+
405
+ Options (all optional): `baseUrl` (default `{issuer}/profile`; https, http only
406
+ for loopback), `scopes` (machine; default: none sent, so the OP grants all the
407
+ client's allowed scopes; an empty list throws), `audience` (exchange, default
408
+ `hx-profile`), `timeout` (ms, default 10000; bounds token-endpoint calls as
409
+ well as profile requests), `maxRetries` (default 2), `fetch`.
410
+
411
+ Behaviour: access tokens are cached (expiry counted from when the token
412
+ request was sent) and refreshed before expiry in the background, so callers
413
+ keep the still-valid token meanwhile (one in-flight request shared by
414
+ concurrent callers); a failed early refresh keeps that token and is not
415
+ retried for 30 s (or until expiry); `forUser` tokens are re-exchanged only at
416
+ expiry; a 401 re-mints the token and replays the request once (for `forUser`,
417
+ if the re-exchange fails, e.g. `invalid_grant`, the original 401 is thrown);
418
+ `deleteUser` treats any 2xx as success, whatever the body; GET/PUT/DELETE are retried on network errors, 408, 429, 502-504
419
+ from the profile service (honouring its `Retry-After`, capped at 30 s); a
420
+ token-endpoint network error, 408, 429, 502, 503 or 504 is retried for every
421
+ method, POST/PATCH included, since no profile request was sent, using backoff
422
+ only (the token endpoint's `Retry-After` is ignored); all retries share the
423
+ same `maxRetries` budget; POST/PATCH are otherwise never retried; redirects are
424
+ not followed; a DELETE answered 404 counts as success if any earlier attempt of
425
+ the same call may have run (it got no HTTP response, or got 504), and that
426
+ stays true whatever later attempts answer; a 502 (ambiguous, but a false
427
+ "deleted" is worse than a false 404) or a failure before anything was sent
428
+ does not count: any error with `syscall` `connect`/`getaddrinfo`, or,
429
+ with no `syscall`, refused, DNS, connect timeout or TLS handshake codes
430
+ (unreachable-host/network codes need `syscall: 'connect'`, since they can also
431
+ hit an established keep-alive socket); an error from an injected `fetch` with
432
+ no recognised `code`/`errno` counts as may-have-run;
433
+ iterators stop on a null or empty `next_cursor`, or if the server repeats a
434
+ cursor.
435
+
436
+ Errors: profile-service failures throw `ProfileApiError` (a `HawcxError`) with
437
+ `status` (`0` = no response: network error/timeout; a 2xx whose body was cut
438
+ off mid-read keeps that status, title `Response body read failed`, detail the
439
+ error name, since the call ran), `title`, `detail`, `type`, `instance`,
440
+ `requestId`, `retryAfter`, `missing` (403 insufficient scope), `fields` (422)
441
+ the parsed JSON `body`, and `rawBody` (the body text, e.g. a gateway's HTML
442
+ error page, capped at 4096 characters, never in `message`; on a malformed 2xx
443
+ only when the body is a markup page starting with `<`, since any other body
444
+ may be a mangled profile, i.e. PII); a body
445
+ with no non-blank `title`/`detail` but a string
446
+ `message` (Kong) uses `message` as `detail`, and `requestId` falls back to the
447
+ body's `request_id`; a 2xx body that is not JSON (`Invalid JSON response`) or
448
+ is not a JSON object / lacks a required field, or a 204 to anything but
449
+ `deleteUser` (`Unexpected response body`), is a `ProfileApiError` with the 2xx
450
+ status. Token failures throw
451
+ `TokenExchangeError` (`statusCode`, `error`, `network`: `true` when no
452
+ complete HTTP response came back, i.e. no response at all, `statusCode`
453
+ undefined, or a 2xx whose body was cut off mid-read, `statusCode` set; for
454
+ `exchangeCode` the latter means the code is already spent). Invalid arguments (empty `userId`/`purpose`/`idToken`)
455
+ throw `HawcxError` before any request, as do `userId`/`purpose` values, and
456
+ `createUser`'s `user_id`, that are `.`/`..` or contain `/`, `\` or control
457
+ characters.
458
+
459
+ ```typescript
460
+ try {
461
+ await profiles.createUser({ user_id: 'user-123', given_name: 'Ada', family_name: 'Lovelace' });
462
+ } catch (err) {
463
+ if (err instanceof ProfileApiError && err.status === 409) {
464
+ /* profile already exists */
465
+ } else throw err;
466
+ }
467
+ ```
468
+
469
+ ---
470
+
254
471
  ## Delegation Module
255
472
 
256
473
  Server-to-server API for managing users, MFA, and devices. Requires a Hawcx secret key.
package/dist/errors.d.ts CHANGED
@@ -39,7 +39,17 @@ export declare class HawcxOAuthError extends HawcxError {
39
39
  }
40
40
  export declare class TokenExchangeError extends HawcxOAuthError {
41
41
  readonly statusCode?: number;
42
- constructor(message: string, statusCode?: number, fields?: HawcxOAuthErrorFields);
42
+ /**
43
+ * `true` when no complete HTTP response came back: no response at all (DNS /
44
+ * TLS / connection failure, timeout; `statusCode` undefined) or a 2xx whose
45
+ * body was cut off mid-read (`statusCode` is that 2xx; for `exchangeCode`
46
+ * the code is then already spent). The underlying error is on `cause`.
47
+ * `false` otherwise.
48
+ */
49
+ readonly network: boolean;
50
+ constructor(message: string, statusCode?: number, fields?: HawcxOAuthErrorFields & {
51
+ network?: boolean;
52
+ });
43
53
  }
44
54
  export declare class TokenVerificationError extends HawcxOAuthError {
45
55
  constructor(message: string, fields?: HawcxOAuthErrorFields);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,qBAAqB;CAM5D;AAED,qBAAa,kBAAmB,SAAQ,eAAe;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,qBAAqB;CAKjF;AAED,qBAAa,sBAAuB,SAAQ,eAAe;gBAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,qBAAqB;CAI5D;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,eAAe;gBACrC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,qBAAqB;CAOnF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,qBAAqB;CAM5D;AAED,qBAAa,kBAAmB,SAAQ,eAAe;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAGxB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,qBAAqB,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;CAOzD;AAED,qBAAa,sBAAuB,SAAQ,eAAe;gBAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,qBAAqB;CAI5D;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,eAAe;gBACrC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,qBAAqB;CAOnF"}
package/dist/errors.js CHANGED
@@ -40,6 +40,7 @@ class TokenExchangeError extends HawcxOAuthError {
40
40
  super(message, fields);
41
41
  this.name = 'TokenExchangeError';
42
42
  this.statusCode = statusCode;
43
+ this.network = fields?.network ?? false;
43
44
  }
44
45
  }
45
46
  exports.TokenExchangeError = TokenExchangeError;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAgBH;;;GAGG;AACH,MAAa,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AALD,gCAKC;AAED;;;;;;;;GAQG;AACH,MAAa,eAAgB,SAAQ,UAAU;IAM7C,YAAY,OAAe,EAAE,MAA8B;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3D,IAAI,MAAM,EAAE,gBAAgB,KAAK,SAAS;YAAE,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAC9F,CAAC;CACF;AAZD,0CAYC;AAED,MAAa,kBAAmB,SAAQ,eAAe;IAGrD,YAAY,OAAe,EAAE,UAAmB,EAAE,MAA8B;QAC9E,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AARD,gDAQC;AAED,MAAa,sBAAuB,SAAQ,eAAe;IACzD,YAAY,OAAe,EAAE,MAA8B;QACzD,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AALD,wDAKC;AAED;;;;;;;;;GASG;AACH,MAAa,cAAe,SAAQ,eAAe;IACjD,YAAY,OAAe,EAAE,OAAqD;QAChF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,OAAO,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,IAA4B,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACtD,CAAC;IACH,CAAC;CACF;AARD,wCAQC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAgBH;;;GAGG;AACH,MAAa,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AALD,gCAKC;AAED;;;;;;;;GAQG;AACH,MAAa,eAAgB,SAAQ,UAAU;IAM7C,YAAY,OAAe,EAAE,MAA8B;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3D,IAAI,MAAM,EAAE,gBAAgB,KAAK,SAAS;YAAE,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAC9F,CAAC;CACF;AAZD,0CAYC;AAED,MAAa,kBAAmB,SAAQ,eAAe;IAWrD,YACE,OAAe,EACf,UAAmB,EACnB,MAAsD;QAEtD,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC;IAC1C,CAAC;CACF;AArBD,gDAqBC;AAED,MAAa,sBAAuB,SAAQ,eAAe;IACzD,YAAY,OAAe,EAAE,MAA8B;QACzD,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AALD,wDAKC;AAED;;;;;;;;;GASG;AACH,MAAa,cAAe,SAAQ,eAAe;IACjD,YAAY,OAAe,EAAE,OAAqD;QAChF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,OAAO,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,IAA4B,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACtD,CAAC;IACH,CAAC;CACF;AARD,wCAQC"}
package/dist/index.d.ts CHANGED
@@ -13,11 +13,15 @@
13
13
  * ```
14
14
  */
15
15
  export { HawcxOAuth } from './oauth';
16
- export type { HawcxOAuthConfig, HawcxOAuthIssuerConfig, ExchangeResult, JwtClaims, DiscoveryMetadata, VerifyTokenOptions, } from './oauth';
16
+ export type { HawcxOAuthConfig, HawcxOAuthIssuerConfig, ExchangeResult, JwtClaims, DiscoveryMetadata, VerifyTokenOptions, AuthorizeUrlOptions, EndSessionUrlOptions, } from './oauth';
17
17
  export { HawcxError, HawcxOAuthError, TokenExchangeError, TokenVerificationError, DiscoveryError, } from './errors';
18
18
  export type { HawcxOAuthErrorFields } from './errors';
19
19
  export { ClientAssertionSigner, CLIENT_ASSERTION_TYPE } from './client-assertion';
20
+ export { ProfileClient, UserProfileClient } from './profile/client';
21
+ export type { ProfileClientOptions } from './profile/client';
22
+ export { ProfileApiError } from './profile/errors';
23
+ export type { Profile, Consent, AuditEvent, Page, ConsentsPage, MyProfilePatch, ProfilePatch, ReplaceUserInput, CreateUserInput, ConsentInput, PageOptions, ListUsersOptions, } from './profile/types';
20
24
  export * from './delegation';
21
25
  export * from './stepup';
22
- export declare const VERSION = "6.0.0";
26
+ export declare const VERSION = "6.2.0";
23
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGlF,cAAc,cAAc,CAAC;AAG7B,cAAc,UAAU,CAAC;AAEzB,eAAO,MAAM,OAAO,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGlF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EACV,OAAO,EACP,OAAO,EACP,UAAU,EACV,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB,cAAc,cAAc,CAAC;AAG7B,cAAc,UAAU,CAAC;AAEzB,eAAO,MAAM,OAAO,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
28
28
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
29
29
  };
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.VERSION = exports.CLIENT_ASSERTION_TYPE = exports.ClientAssertionSigner = exports.DiscoveryError = exports.TokenVerificationError = exports.TokenExchangeError = exports.HawcxOAuthError = exports.HawcxError = exports.HawcxOAuth = void 0;
31
+ exports.VERSION = exports.ProfileApiError = exports.UserProfileClient = exports.ProfileClient = exports.CLIENT_ASSERTION_TYPE = exports.ClientAssertionSigner = exports.DiscoveryError = exports.TokenVerificationError = exports.TokenExchangeError = exports.HawcxOAuthError = exports.HawcxError = exports.HawcxOAuth = void 0;
32
32
  var oauth_1 = require("./oauth");
33
33
  Object.defineProperty(exports, "HawcxOAuth", { enumerable: true, get: function () { return oauth_1.HawcxOAuth; } });
34
34
  var errors_1 = require("./errors");
@@ -40,9 +40,15 @@ Object.defineProperty(exports, "DiscoveryError", { enumerable: true, get: functi
40
40
  var client_assertion_1 = require("./client-assertion");
41
41
  Object.defineProperty(exports, "ClientAssertionSigner", { enumerable: true, get: function () { return client_assertion_1.ClientAssertionSigner; } });
42
42
  Object.defineProperty(exports, "CLIENT_ASSERTION_TYPE", { enumerable: true, get: function () { return client_assertion_1.CLIENT_ASSERTION_TYPE; } });
43
+ // Profile service module
44
+ var client_1 = require("./profile/client");
45
+ Object.defineProperty(exports, "ProfileClient", { enumerable: true, get: function () { return client_1.ProfileClient; } });
46
+ Object.defineProperty(exports, "UserProfileClient", { enumerable: true, get: function () { return client_1.UserProfileClient; } });
47
+ var errors_2 = require("./profile/errors");
48
+ Object.defineProperty(exports, "ProfileApiError", { enumerable: true, get: function () { return errors_2.ProfileApiError; } });
43
49
  // Delegation module
44
50
  __exportStar(require("./delegation"), exports);
45
51
  // Step-up module
46
52
  __exportStar(require("./stepup"), exports);
47
- exports.VERSION = '6.0.0';
53
+ exports.VERSION = '6.2.0';
48
54
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;AAEH,iCAAqC;AAA5B,mGAAA,UAAU,OAAA;AASnB,mCAMkB;AALhB,oGAAA,UAAU,OAAA;AACV,yGAAA,eAAe,OAAA;AACf,4GAAA,kBAAkB,OAAA;AAClB,gHAAA,sBAAsB,OAAA;AACtB,wGAAA,cAAc,OAAA;AAIhB,uDAAkF;AAAzE,yHAAA,qBAAqB,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAErD,oBAAoB;AACpB,+CAA6B;AAE7B,iBAAiB;AACjB,2CAAyB;AAEZ,QAAA,OAAO,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;AAEH,iCAAqC;AAA5B,mGAAA,UAAU,OAAA;AAWnB,mCAMkB;AALhB,oGAAA,UAAU,OAAA;AACV,yGAAA,eAAe,OAAA;AACf,4GAAA,kBAAkB,OAAA;AAClB,gHAAA,sBAAsB,OAAA;AACtB,wGAAA,cAAc,OAAA;AAIhB,uDAAkF;AAAzE,yHAAA,qBAAqB,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAErD,yBAAyB;AACzB,2CAAoE;AAA3D,uGAAA,aAAa,OAAA;AAAE,2GAAA,iBAAiB,OAAA;AAEzC,2CAAmD;AAA1C,yGAAA,eAAe,OAAA;AAgBxB,oBAAoB;AACpB,+CAA6B;AAE7B,iBAAiB;AACjB,2CAAyB;AAEZ,QAAA,OAAO,GAAG,OAAO,CAAC"}
@@ -63,4 +63,11 @@ export declare function parseSecureBaseUrl(label: string, raw: string, makeError
63
63
  * base's exact origin and stays under its path prefix.
64
64
  */
65
65
  export declare function safeJoinUrl(base: SecureBaseUrl, path: string, label: string, makeError: UrlErrorFactory): string;
66
+ /**
67
+ * Validate a browser-facing custom domain as a raw string (no WHATWG parse,
68
+ * which would strip whitespace, decode `%2e`, accept `*` or a trailing dot)
69
+ * and return its canonical origin: `scheme://host[:port]`, lower-cased,
70
+ * default port dropped. Same rules as the Python and Java SDKs.
71
+ */
72
+ export declare function parseCustomDomain(raw: unknown, makeError: UrlErrorFactory): string;
66
73
  //# sourceMappingURL=url.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,KAAK,CAAC;AAiB1E,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,GAAG,CAsB3F;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,eAAe,GACzB,aAAa,CAYf;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,eAAe,GACzB,MAAM,CAyCR"}
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,KAAK,CAAC;AAiB1E,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,GAAG,CAsB3F;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,eAAe,GACzB,aAAa,CAYf;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,eAAe,GACzB,MAAM,CAyCR;AAOD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,eAAe,GAAG,MAAM,CAyClF"}
@@ -31,6 +31,8 @@ exports.isLoopbackHostname = isLoopbackHostname;
31
31
  exports.assertSecureUrl = assertSecureUrl;
32
32
  exports.parseSecureBaseUrl = parseSecureBaseUrl;
33
33
  exports.safeJoinUrl = safeJoinUrl;
34
+ exports.parseCustomDomain = parseCustomDomain;
35
+ const net_1 = require("net");
34
36
  /** Exact hostnames (as reported by `URL#hostname`) allowed to use plain http. */
35
37
  const LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);
36
38
  /** C0 controls, space and DEL — stripped or rewritten silently by the URL parser. */
@@ -145,6 +147,59 @@ function safeJoinUrl(base, path, label, makeError) {
145
147
  }
146
148
  return joined.href;
147
149
  }
150
+ /** Raw-string shape of a custom domain (cross-SDK contract §1). */
151
+ const CUSTOM_DOMAIN_SHAPE = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/(\[[0-9A-Fa-f:.]+\]|[^/:?#@[\]]+)(?::([^/]*))?\/?$/;
152
+ const DNS_LABEL = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
153
+ const IPV4_OCTET = /^(0|[1-9][0-9]{0,2})$/;
154
+ /**
155
+ * Validate a browser-facing custom domain as a raw string (no WHATWG parse,
156
+ * which would strip whitespace, decode `%2e`, accept `*` or a trailing dot)
157
+ * and return its canonical origin: `scheme://host[:port]`, lower-cased,
158
+ * default port dropped. Same rules as the Python and Java SDKs.
159
+ */
160
+ function parseCustomDomain(raw, makeError) {
161
+ const bad = (why) => makeError(`customDomain ${why}: ${JSON.stringify(raw)}`);
162
+ if (typeof raw !== 'string' || raw === '')
163
+ throw bad('must be a non-empty string');
164
+ // Printable ASCII only, checked before lower-casing (U+212A Kelvin sign lower-cases to 'k').
165
+ if (/[^\x21-\x7e]|[\\%]/.test(raw)) {
166
+ throw bad('must be printable ASCII (punycode for IDN) without whitespace, backslash or %');
167
+ }
168
+ const m = CUSTOM_DOMAIN_SHAPE.exec(raw);
169
+ if (m === null)
170
+ throw bad('must be scheme://host[:port] only (no userinfo, path, query or fragment)');
171
+ const scheme = m[1].toLowerCase();
172
+ let host = m[2].toLowerCase();
173
+ const port = m[3];
174
+ if (host.startsWith('[')) {
175
+ if (host.includes('.') || !(0, net_1.isIPv6)(host.slice(1, -1))) {
176
+ throw bad('has an invalid IPv6 literal (embedded IPv4 not allowed)');
177
+ }
178
+ // WHATWG serialises IPv6 in RFC 5952 form: `[0:0::1]` -> `[::1]` (contract R7).
179
+ host = new URL(`http://${host}`).hostname;
180
+ }
181
+ else if (/^[0-9.]+$/.test(host) && host.split('.').length === 4) {
182
+ if (!host.split('.').every((o) => IPV4_OCTET.test(o) && Number(o) <= 255)) {
183
+ throw bad('has a non-canonical IPv4 address');
184
+ }
185
+ }
186
+ else {
187
+ const labels = host.split('.');
188
+ if (host.length > 253 ||
189
+ !labels.every((l) => l.length <= 63 && DNS_LABEL.test(l)) ||
190
+ !/^[a-z]/.test(labels[labels.length - 1])) {
191
+ throw bad('has an invalid hostname (ASCII/punycode labels only, last label starts with a letter)');
192
+ }
193
+ }
194
+ if (port !== undefined && !(/^[1-9][0-9]{0,4}$/.test(port) && Number(port) <= 65535)) {
195
+ throw bad('has an invalid port');
196
+ }
197
+ if (scheme !== 'https' && !(scheme === 'http' && isLoopbackHostname(host))) {
198
+ throw bad('must use https (http only for loopback)');
199
+ }
200
+ const defaultPort = scheme === 'https' ? '443' : '80';
201
+ return `${scheme}://${host}${port === undefined || port === defaultPort ? '' : ':' + port}`;
202
+ }
148
203
  /** Render a URL for an error message with any credentials removed. */
149
204
  function redact(u) {
150
205
  const copy = new URL(u.href);
@@ -1 +1 @@
1
- {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"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"}