@ondewo/sip-client-nodejs 5.4.0 → 5.4.2

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.
@@ -13,15 +13,6 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
  //
16
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
- return new (P || (P = Promise))(function (resolve, reject) {
19
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
20
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
- step((generator = generator.apply(thisArg, _arguments || [])).next());
23
- });
24
- };
25
16
  Object.defineProperty(exports, "__esModule", { value: true });
26
17
  exports.OfflineTokenProvider = exports.TokenError = void 0;
27
18
  exports.login = login;
@@ -76,54 +67,54 @@ function buildTokenEndpoint(keycloakUrl, realm) {
76
67
  * @returns A promise resolving to the parsed {@link KeycloakTokenResponse}.
77
68
  * @throws {TokenError} On a non-2xx response, an unparseable body, or a missing `access_token`.
78
69
  */
79
- function postTokenRequest(tokenEndpoint, params, fetchImpl) {
80
- return __awaiter(this, void 0, void 0, function* () {
81
- const body = new URLSearchParams(params).toString();
82
- const response = yield fetchImpl(tokenEndpoint, {
83
- method: 'POST',
84
- headers: {
85
- 'Content-Type': 'application/x-www-form-urlencoded',
86
- Accept: 'application/json'
87
- },
88
- body
89
- });
90
- const text = yield response.text();
91
- if (!response.ok) {
92
- throw new TokenError(`Keycloak token endpoint returned HTTP ${response.status}: ${text}`);
93
- }
94
- let parsed;
95
- try {
96
- parsed = JSON.parse(text);
97
- }
98
- catch (_a) {
99
- throw new TokenError(`Keycloak token endpoint returned a non-JSON body: ${text}`);
100
- }
101
- if (typeof parsed.access_token !== 'string' || parsed.access_token.length === 0) {
102
- throw new TokenError('Keycloak token response did not contain an access_token');
103
- }
104
- return parsed;
70
+ async function postTokenRequest(tokenEndpoint, params, fetchImpl) {
71
+ const body = new URLSearchParams(params).toString();
72
+ const response = await fetchImpl(tokenEndpoint, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/x-www-form-urlencoded',
76
+ Accept: 'application/json'
77
+ },
78
+ body
105
79
  });
80
+ const text = await response.text();
81
+ if (!response.ok) {
82
+ throw new TokenError(`Keycloak token endpoint returned HTTP ${response.status}: ${text}`);
83
+ }
84
+ let parsed;
85
+ try {
86
+ parsed = JSON.parse(text);
87
+ }
88
+ catch {
89
+ throw new TokenError(`Keycloak token endpoint returned a non-JSON body: ${text}`);
90
+ }
91
+ if (typeof parsed.access_token !== 'string' || parsed.access_token.length === 0) {
92
+ throw new TokenError('Keycloak token response did not contain an access_token');
93
+ }
94
+ return parsed;
106
95
  }
107
96
  /**
108
- * Build the default fetch layer: delegate to the global `fetch` (Node >= 18).
97
+ * Build the default {@link TokenFetch}: delegate to the global `fetch` (Node >= 18).
109
98
  *
110
99
  * When `verifySsl` is `false`, a cached undici `Agent` with `rejectUnauthorized: false` is attached
111
100
  * to every request as its `dispatcher`, so the Keycloak token call skips TLS certificate verification
112
101
  * (opt-in insecure; Node-only). The dispatcher is built once here and reused for all requests this
113
102
  * transport makes (login + refreshes); the secure default never loads undici.
114
103
  *
115
- * @param {boolean} verifySsl - Whether to verify the Keycloak server's TLS certificate.
116
- * @returns {Function} A fetch layer bound to the chosen TLS-verification behaviour.
104
+ * @param verifySsl - Whether to verify the Keycloak server's TLS certificate.
105
+ * @returns A fetch layer bound to the chosen TLS-verification behaviour.
117
106
  */
118
107
  function createDefaultFetch(verifySsl) {
119
108
  let dispatcher;
120
109
  if (!verifySsl) {
110
+ // Lazy require keeps undici out of the default (secure) code path.
111
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
121
112
  const { Agent } = require('undici');
122
113
  dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
123
114
  }
124
115
  return (url, init) => {
125
116
  const globalFetch = globalThis.fetch;
126
- return globalFetch(url, dispatcher === undefined ? init : Object.assign({}, init, { dispatcher }));
117
+ return globalFetch(url, dispatcher === undefined ? init : { ...init, dispatcher });
127
118
  };
128
119
  }
129
120
  /**
@@ -141,7 +132,8 @@ class OfflineTokenProvider {
141
132
  this.tokenEndpoint = buildTokenEndpoint(options.keycloakUrl, options.realm);
142
133
  this.clientId = options.clientId;
143
134
  this.tokenExpirationInS = options.tokenExpirationInS;
144
- this.fetchImpl = options.fetchImpl !== undefined ? options.fetchImpl : createDefaultFetch(options.keycloakVerifySsl !== false);
135
+ this.fetchImpl =
136
+ options.fetchImpl !== undefined ? options.fetchImpl : createDefaultFetch(options.keycloakVerifySsl ?? true);
145
137
  this.nowInMs = options.nowInMs !== undefined ? options.nowInMs : Date.now;
146
138
  this.accessToken = null;
147
139
  this.refreshToken = null;
@@ -159,27 +151,25 @@ class OfflineTokenProvider {
159
151
  * @throws {TokenError} If the token endpoint fails or the response carries no `refresh_token`
160
152
  * (the SDK client lacks `directAccessGrants` + the `offline_access` scope).
161
153
  */
162
- bootstrap(username, password) {
163
- return __awaiter(this, void 0, void 0, function* () {
164
- const tokenResponse = yield postTokenRequest(this.tokenEndpoint, {
165
- grant_type: 'password',
166
- client_id: this.clientId,
167
- username,
168
- password,
169
- scope: 'offline_access'
170
- }, this.fetchImpl);
171
- this.accessToken = tokenResponse.access_token;
172
- this.refreshToken = typeof tokenResponse.refresh_token === 'string' ? tokenResponse.refresh_token : null;
173
- if (this.refreshToken === null) {
174
- throw new TokenError('Keycloak token response did not contain a refresh_token; the SDK client must have ' +
175
- 'directAccessGrants + the offline_access scope (ondewo-sip-cai-sdk-public)');
176
- }
177
- if (this.tokenExpirationInS !== undefined) {
178
- const expirationInMs = this.tokenExpirationInS * 1000;
179
- this.deadlineInMs = this.nowInMs() + expirationInMs;
180
- }
181
- this.scheduleRefresh(tokenResponse.expires_in);
182
- });
154
+ async bootstrap(username, password) {
155
+ const tokenResponse = await postTokenRequest(this.tokenEndpoint, {
156
+ grant_type: 'password',
157
+ client_id: this.clientId,
158
+ username,
159
+ password,
160
+ scope: 'offline_access'
161
+ }, this.fetchImpl);
162
+ this.accessToken = tokenResponse.access_token;
163
+ this.refreshToken = typeof tokenResponse.refresh_token === 'string' ? tokenResponse.refresh_token : null;
164
+ if (this.refreshToken === null) {
165
+ throw new TokenError('Keycloak token response did not contain a refresh_token; the SDK client must have ' +
166
+ 'directAccessGrants + the offline_access scope (ondewo-sip-cai-sdk-public)');
167
+ }
168
+ if (this.tokenExpirationInS !== undefined) {
169
+ const expirationInMs = this.tokenExpirationInS * 1000;
170
+ this.deadlineInMs = this.nowInMs() + expirationInMs;
171
+ }
172
+ this.scheduleRefresh(tokenResponse.expires_in);
183
173
  }
184
174
  /**
185
175
  * Exchange the offline refresh token for a fresh access token and re-arm the next refresh.
@@ -188,30 +178,28 @@ class OfflineTokenProvider {
188
178
  * @returns A promise that resolves once the token is refreshed (or the loop has lapsed/stopped).
189
179
  * @throws {TokenError} If the refresh token-endpoint call fails or returns an invalid body.
190
180
  */
191
- refresh() {
192
- return __awaiter(this, void 0, void 0, function* () {
193
- /* c8 ignore next 3 -- unreachable: stop() always clears the only timer that calls refresh() */
194
- if (this.stopped) {
195
- return;
196
- }
197
- // Re-check the bounded deadline at fire time (not just at schedule time): once it has elapsed the
198
- // loop stops with no further renewal -> the access token lapses -> re-login is required.
199
- if (this.deadlineInMs !== null && this.nowInMs() >= this.deadlineInMs) {
200
- this.stop();
201
- return;
202
- }
203
- const tokenResponse = yield postTokenRequest(this.tokenEndpoint, {
204
- grant_type: 'refresh_token',
205
- client_id: this.clientId,
206
- refresh_token: this.refreshToken
207
- }, this.fetchImpl);
208
- this.accessToken = tokenResponse.access_token;
209
- // Keycloak may rotate the offline refresh token; keep the newest one when present.
210
- if (typeof tokenResponse.refresh_token === 'string' && tokenResponse.refresh_token.length > 0) {
211
- this.refreshToken = tokenResponse.refresh_token;
212
- }
213
- this.scheduleRefresh(tokenResponse.expires_in);
214
- });
181
+ async refresh() {
182
+ /* c8 ignore next 3 -- unreachable: stop() always clears the only timer that calls refresh() */
183
+ if (this.stopped) {
184
+ return;
185
+ }
186
+ // Re-check the bounded deadline at fire time (not just at schedule time): once it has elapsed the
187
+ // loop stops with no further renewal -> the access token lapses -> re-login is required.
188
+ if (this.deadlineInMs !== null && this.nowInMs() >= this.deadlineInMs) {
189
+ this.stop();
190
+ return;
191
+ }
192
+ const tokenResponse = await postTokenRequest(this.tokenEndpoint, {
193
+ grant_type: 'refresh_token',
194
+ client_id: this.clientId,
195
+ refresh_token: this.refreshToken
196
+ }, this.fetchImpl);
197
+ this.accessToken = tokenResponse.access_token;
198
+ // Keycloak may rotate the offline refresh token; keep the newest one when present.
199
+ if (typeof tokenResponse.refresh_token === 'string' && tokenResponse.refresh_token.length > 0) {
200
+ this.refreshToken = tokenResponse.refresh_token;
201
+ }
202
+ this.scheduleRefresh(tokenResponse.expires_in);
215
203
  }
216
204
  /**
217
205
  * Arm a single timer for the next refresh, clamped to the bounded deadline. Stops silently once
@@ -301,20 +289,18 @@ exports.OfflineTokenProvider = OfflineTokenProvider;
301
289
  * @throws {TokenError} If `options` is missing, a required string option is empty, or the token
302
290
  * endpoint / response is invalid.
303
291
  */
304
- function login(options) {
305
- return __awaiter(this, void 0, void 0, function* () {
306
- if (options === undefined || options === null) {
307
- throw new TokenError('login() requires an options object');
308
- }
309
- const requiredKeys = ['keycloakUrl', 'realm', 'clientId', 'username', 'password'];
310
- for (const key of requiredKeys) {
311
- const value = options[key];
312
- if (typeof value !== 'string' || value.length === 0) {
313
- throw new TokenError(`login() option "${key}" is required and must be a non-empty string`);
314
- }
292
+ async function login(options) {
293
+ if (options === undefined || options === null) {
294
+ throw new TokenError('login() requires an options object');
295
+ }
296
+ const requiredKeys = ['keycloakUrl', 'realm', 'clientId', 'username', 'password'];
297
+ for (const key of requiredKeys) {
298
+ const value = options[key];
299
+ if (typeof value !== 'string' || value.length === 0) {
300
+ throw new TokenError(`login() option "${key}" is required and must be a non-empty string`);
315
301
  }
316
- const provider = new OfflineTokenProvider(options);
317
- yield provider.bootstrap(options.username, options.password);
318
- return provider;
319
- });
302
+ }
303
+ const provider = new OfflineTokenProvider(options);
304
+ await provider.bootstrap(options.username, options.password);
305
+ return provider;
320
306
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ondewo/sip-client-nodejs",
3
- "version": "5.4.0",
3
+ "version": "5.4.2",
4
4
  "description": "ONDEWO Session Initiation Protocol (SIP) Client library for Nodejs",
5
5
  "author": "ONDEWO GmbH <office@ondewo.com>",
6
6
  "homepage": "https://ondewo.com",
package/public-api.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- export * from './api/ondewo/sip/sip_grpc_pb.d';
2
- export * from './api/ondewo/sip/sip_pb.d';
3
1
  export * from './api/google/protobuf/empty_pb.d';
4
2
  export * from './api/google/protobuf/timestamp_pb.d';
3
+ export * from './api/ondewo/sip/sip_grpc_pb.d';
4
+ export * from './api/ondewo/sip/sip_pb.d';
5
+ export * from './auth/grpcChannel';
6
+ export * from './auth/offlineTokenProvider';
package/public-api.js CHANGED
@@ -1,6 +1,16 @@
1
- export * from './api/ondewo/sip/sip_grpc_pb';
2
- export * from './api/ondewo/sip/sip_pb';
3
- export * from './api/google/protobuf/empty_grpc_pb';
4
- export * from './api/google/protobuf/timestamp_pb';
5
- export * from './api/google/protobuf/empty_pb';
6
- export * from './api/google/protobuf/timestamp_grpc_pb';
1
+ 'use strict';
2
+ function reexport(m) {
3
+ Object.keys(m).forEach(function (k) {
4
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) {
5
+ Object.defineProperty(exports, k, { enumerable: true, get: function () { return m[k]; } });
6
+ }
7
+ });
8
+ }
9
+ reexport(require('./api/google/protobuf/empty_grpc_pb'));
10
+ reexport(require('./api/google/protobuf/empty_pb'));
11
+ reexport(require('./api/google/protobuf/timestamp_grpc_pb'));
12
+ reexport(require('./api/google/protobuf/timestamp_pb'));
13
+ reexport(require('./api/ondewo/sip/sip_grpc_pb'));
14
+ reexport(require('./api/ondewo/sip/sip_pb'));
15
+ reexport(require('./auth/grpcChannel'));
16
+ reexport(require('./auth/offlineTokenProvider'));