docpouch-client 0.9.1 → 1.0.3

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/index.js CHANGED
@@ -25,6 +25,14 @@ export default class docPouchClient {
25
25
  * @type {string | null}
26
26
  */
27
27
  this.authToken = null;
28
+ this.oidcConfig = null;
29
+ this.oidcAccessToken = null;
30
+ this.oidcRefreshToken = null;
31
+ this.oidcIdToken = null;
32
+ this.oidcTokenExpiry = 0;
33
+ this.codeVerifier = null;
34
+ this.oidcState = null;
35
+ this.authMethod = 'none';
28
36
  /**
29
37
  * Flag indicating whether a connection attempt is in progress.
30
38
  *
@@ -66,7 +74,7 @@ export default class docPouchClient {
66
74
  return;
67
75
  }
68
76
  this.realTimeSync = newRealTimeSync;
69
- if (newRealTimeSync && this.authToken) {
77
+ if (newRealTimeSync && (this.authToken || this.oidcAccessToken)) {
70
78
  console.log("Activating realtime updates");
71
79
  // Ensure we're not in the middle of another connection attempt
72
80
  if (this.connectionInProgress) {
@@ -96,6 +104,7 @@ export default class docPouchClient {
96
104
  const response = await this.request('/users/login', 'POST', credentials, false);
97
105
  if (response.token) {
98
106
  this.authToken = response.token;
107
+ this.authMethod = 'jwt';
99
108
  // Reconnect websocket with new token if realtime sync is enabled
100
109
  if (this.realTimeSync) {
101
110
  this.initWebSocket();
@@ -236,8 +245,11 @@ export default class docPouchClient {
236
245
  */
237
246
  setToken(token) {
238
247
  console.log("Setting token to:", token ? "***token***" : "null");
248
+ this.authMethod = token ? 'jwt' : 'none';
239
249
  const tokenChanged = this.authToken !== token;
240
250
  this.authToken = token;
251
+ if (!token)
252
+ this.clearOidcTokens();
241
253
  if (!tokenChanged) {
242
254
  console.log("Token unchanged, no need to reconnect");
243
255
  return;
@@ -279,17 +291,244 @@ export default class docPouchClient {
279
291
  console.log("Socket connection debug info:");
280
292
  console.log("- Connected:", this.socket.connected);
281
293
  console.log("- Socket ID:", this.socket.id);
282
- console.log("- Auth token present:", !!this.authToken);
294
+ console.log("- Auth token present:", !!this.authToken, "(method:", this.authMethod + ")");
295
+ console.log("- OIDC access token present:", !!this.oidcAccessToken);
283
296
  console.log("- Connection in progress:", this.connectionInProgress);
284
297
  console.log("- Realtime sync enabled:", this.realTimeSync);
285
298
  console.log("- Socket options:", this.socket.io.opts);
299
+ const activeToken = this.authMethod === 'oidc' ? this.oidcAccessToken : this.authToken;
286
300
  // Try to force reconnection
287
- if (!this.socket.connected && this.authToken && this.realTimeSync) {
301
+ if (!this.socket.connected && activeToken && this.realTimeSync) {
288
302
  console.log("Attempting to force reconnection...");
289
- this.socket.auth = { token: this.authToken };
303
+ this.socket.auth = { token: activeToken };
290
304
  this.socket.connect();
291
305
  }
292
306
  }
307
+ // OIDC Authentication Methods
308
+ /**
309
+ * Sets the OIDC configuration to use for the callback and token exchange.
310
+ * Call this before {@link handleOidcCallback} if the client was freshly
311
+ * instantiated after a page reload (the config is otherwise only set
312
+ * internally by {@link loginWithOidc} before the redirect).
313
+ *
314
+ * @param {I_OidcConfig} config - OIDC provider configuration.
315
+ */
316
+ setOidcConfig(config) {
317
+ this.oidcConfig = config;
318
+ }
319
+ /**
320
+ * Initiates the OIDC authentication flow by redirecting to the authorization endpoint.
321
+ *
322
+ * @param {I_OidcConfig} config - OIDC provider configuration.
323
+ * @returns {Promise<void>}
324
+ */
325
+ async loginWithOidc(config) {
326
+ this.oidcConfig = config;
327
+ const response = await fetch(`${config.issuer}/.well-known/openid-configuration`);
328
+ const discovery = await response.json();
329
+ this.codeVerifier = this.generateCodeVerifier();
330
+ this.oidcState = this.generateState();
331
+ const codeChallenge = await this.generateCodeChallenge(this.codeVerifier);
332
+ if (typeof window !== 'undefined' && window.sessionStorage) {
333
+ sessionStorage.setItem('docpouch_oidc_state', this.oidcState);
334
+ sessionStorage.setItem('docpouch_oidc_code_verifier', this.codeVerifier);
335
+ sessionStorage.setItem('docpouch_oidc_issuer', config.issuer);
336
+ sessionStorage.setItem('docpouch_oidc_client_id', config.clientId);
337
+ sessionStorage.setItem('docpouch_oidc_redirect_uri', config.redirectUri);
338
+ }
339
+ const scope = config.scope || config.scopes?.join(' ') || 'openid profile';
340
+ const params = new URLSearchParams({
341
+ response_type: 'code',
342
+ client_id: config.clientId,
343
+ redirect_uri: config.redirectUri,
344
+ scope,
345
+ state: this.oidcState,
346
+ code_challenge: codeChallenge,
347
+ code_challenge_method: 'S256'
348
+ });
349
+ window.location.href = `${discovery.authorization_endpoint}?${params.toString()}`;
350
+ }
351
+ /**
352
+ * Handles the OIDC callback by exchanging the authorization code for tokens.
353
+ *
354
+ * @returns {Promise<boolean>} True if the callback was handled successfully.
355
+ */
356
+ async handleOidcCallback() {
357
+ const params = new URLSearchParams(window.location.search);
358
+ const code = params.get('code');
359
+ const state = params.get('state');
360
+ const error = params.get('error');
361
+ if (error)
362
+ throw new Error(`OAuth error: ${error}`);
363
+ if (!code || !state)
364
+ return false;
365
+ if (typeof window !== 'undefined' && window.sessionStorage) {
366
+ this.oidcState = sessionStorage.getItem('docpouch_oidc_state') || this.oidcState;
367
+ this.codeVerifier = sessionStorage.getItem('docpouch_oidc_code_verifier') || this.codeVerifier;
368
+ sessionStorage.removeItem('docpouch_oidc_state');
369
+ sessionStorage.removeItem('docpouch_oidc_code_verifier');
370
+ if (!this.oidcConfig) {
371
+ const issuer = sessionStorage.getItem('docpouch_oidc_issuer');
372
+ const clientId = sessionStorage.getItem('docpouch_oidc_client_id');
373
+ const redirectUri = sessionStorage.getItem('docpouch_oidc_redirect_uri');
374
+ if (issuer && clientId && redirectUri) {
375
+ this.oidcConfig = { issuer, clientId, redirectUri };
376
+ sessionStorage.removeItem('docpouch_oidc_issuer');
377
+ sessionStorage.removeItem('docpouch_oidc_client_id');
378
+ sessionStorage.removeItem('docpouch_oidc_redirect_uri');
379
+ }
380
+ }
381
+ }
382
+ if (state !== this.oidcState)
383
+ throw new Error('State mismatch');
384
+ const discovery = await this.discoverOidc();
385
+ const body = new URLSearchParams({
386
+ grant_type: 'authorization_code',
387
+ code,
388
+ redirect_uri: this.oidcConfig.redirectUri,
389
+ client_id: this.oidcConfig.clientId,
390
+ code_verifier: this.codeVerifier || ''
391
+ });
392
+ const response = await fetch(discovery.token_endpoint, {
393
+ method: 'POST',
394
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
395
+ body: body.toString()
396
+ });
397
+ if (!response.ok)
398
+ throw new Error('Token exchange failed');
399
+ const raw = await response.json();
400
+ const tokens = {
401
+ accessToken: raw.access_token,
402
+ refreshToken: raw.refresh_token,
403
+ idToken: raw.id_token,
404
+ expiresIn: raw.expires_in,
405
+ tokenType: raw.token_type,
406
+ scope: raw.scope
407
+ };
408
+ this.setOidcTokens(tokens);
409
+ if (this.realTimeSync)
410
+ this.initWebSocket();
411
+ return true;
412
+ }
413
+ restoreOidcSession() {
414
+ if (typeof window === 'undefined' || !window.localStorage)
415
+ return false;
416
+ const stored = localStorage.getItem('docpouch_oidc_session');
417
+ if (!stored)
418
+ return false;
419
+ try {
420
+ const session = JSON.parse(stored);
421
+ if (!session.accessToken || Date.now() >= session.expiry) {
422
+ this.clearPersistedOidcSession();
423
+ return false;
424
+ }
425
+ this.authMethod = 'oidc';
426
+ this.oidcAccessToken = session.accessToken;
427
+ this.oidcRefreshToken = session.refreshToken || null;
428
+ this.oidcIdToken = session.idToken || null;
429
+ this.oidcTokenExpiry = session.expiry;
430
+ return true;
431
+ }
432
+ catch {
433
+ this.clearPersistedOidcSession();
434
+ return false;
435
+ }
436
+ }
437
+ /**
438
+ * Ensures the OIDC access token is valid, refreshing it if necessary.
439
+ */
440
+ async ensureValidOidcToken() {
441
+ if (this.authMethod !== 'oidc' || !this.oidcAccessToken)
442
+ throw new Error('Not authenticated via OIDC');
443
+ if (Date.now() >= this.oidcTokenExpiry - 60000) {
444
+ await this.refreshOidcToken();
445
+ }
446
+ return this.oidcAccessToken;
447
+ }
448
+ /**
449
+ * Returns the current authentication method.
450
+ *
451
+ * @returns {'jwt' | 'oidc' | 'none'} The active auth method.
452
+ */
453
+ getAuthMethod() {
454
+ return this.authMethod;
455
+ }
456
+ /**
457
+ * Checks whether the client is currently authenticated.
458
+ *
459
+ * @returns {boolean} True if authenticated.
460
+ */
461
+ isAuthenticated() {
462
+ return this.authMethod !== 'none' && (this.authMethod === 'jwt'
463
+ ? !!this.authToken
464
+ : (this.oidcAccessToken !== null && Date.now() < this.oidcTokenExpiry));
465
+ }
466
+ /**
467
+ * Returns the current active token, regardless of auth method.
468
+ *
469
+ * @returns {string | null} The active token or null.
470
+ */
471
+ getToken() {
472
+ return this.authMethod === 'oidc' ? this.oidcAccessToken : this.authToken;
473
+ }
474
+ /**
475
+ * Logs out the client, clearing all tokens and disconnecting WebSocket.
476
+ *
477
+ * @returns {Promise<void>}
478
+ */
479
+ async logout() {
480
+ this.authToken = null;
481
+ this.clearOidcTokens();
482
+ this.authMethod = 'none';
483
+ if (this.socket.connected)
484
+ this.socket.disconnect();
485
+ if (typeof window !== 'undefined' && window.location &&
486
+ (window.location.search.includes('code=') || window.location.search.includes('state='))) {
487
+ window.history.replaceState({}, '', window.location.pathname);
488
+ }
489
+ }
490
+ // OIDC Dynamic Client Registration Methods
491
+ /**
492
+ * Registers a new OIDC client with the server (dynamic client registration).
493
+ *
494
+ * @param {I_OidcClientRegistration} registration - Client metadata for registration.
495
+ * @param {string} [registrationToken] - Initial registration access token. If not provided, the current auth token is used.
496
+ * @returns {Promise<I_OidcClientResponse>} The registered client details.
497
+ */
498
+ async registerOidcClient(registration, registrationToken) {
499
+ return await this.request('/oidc/reg', 'POST', registration, true, registrationToken);
500
+ }
501
+ /**
502
+ * Retrieves the registration state of an existing OIDC client.
503
+ *
504
+ * @param {string} clientId - The client identifier.
505
+ * @param {string} [registrationToken] - The registration access token. If not provided, the current auth token is used.
506
+ * @returns {Promise<I_OidcClientResponse>} The client details.
507
+ */
508
+ async getOidcClient(clientId, registrationToken) {
509
+ return await this.request(`/oidc/reg/${clientId}`, 'GET', undefined, true, registrationToken);
510
+ }
511
+ /**
512
+ * Updates the registration of an existing OIDC client.
513
+ *
514
+ * @param {string} clientId - The client identifier.
515
+ * @param {I_OidcClientRegistration} registration - Updated client metadata.
516
+ * @param {string} [registrationToken] - The registration access token. If not provided, the current auth token is used.
517
+ * @returns {Promise<I_OidcClientResponse>} The updated client details.
518
+ */
519
+ async updateOidcClient(clientId, registration, registrationToken) {
520
+ return await this.request(`/oidc/reg/${clientId}`, 'PUT', registration, true, registrationToken);
521
+ }
522
+ /**
523
+ * Deletes an OIDC client registration.
524
+ *
525
+ * @param {string} clientId - The client identifier.
526
+ * @param {string} [registrationToken] - The registration access token. If not provided, the current auth token is used.
527
+ * @returns {Promise<void>}
528
+ */
529
+ async deleteOidcClient(clientId, registrationToken) {
530
+ await this.request(`/oidc/reg/${clientId}`, 'DELETE', undefined, true, registrationToken);
531
+ }
293
532
  /**
294
533
  * Sets up permanent socket listeners for the client.
295
534
  *
@@ -320,8 +559,9 @@ export default class docPouchClient {
320
559
  * @private
321
560
  */
322
561
  initWebSocket() {
323
- console.log("initWebSocket called. Auth token present:", !!this.authToken, "Connection in progress:", this.connectionInProgress, "Socket connected:", this.socket.connected);
324
- if (!this.authToken) {
562
+ const token = this.authMethod === 'oidc' ? this.oidcAccessToken : this.authToken;
563
+ console.log("initWebSocket called. Auth token present:", !!this.authToken, "Connection in progress:", this.connectionInProgress, "Socket connected:", this.socket.connected, "Auth method:", this.authMethod);
564
+ if (!token) {
325
565
  console.log("Skipping WebSocket initialization: No auth token");
326
566
  return;
327
567
  }
@@ -337,7 +577,7 @@ export default class docPouchClient {
337
577
  try {
338
578
  console.log("Setting up WebSocket connection with token");
339
579
  // Update the auth token
340
- this.socket.auth = { token: this.authToken };
580
+ this.socket.auth = { token };
341
581
  // Remove any dynamic event listeners that might have been added
342
582
  this.socket.offAny();
343
583
  // Set up event handler for application events
@@ -383,12 +623,15 @@ export default class docPouchClient {
383
623
  * @returns {Promise<T>} Parsed JSON response body.
384
624
  * @private
385
625
  */
386
- async request(endpoint, method, body, requiresAuth = true) {
626
+ async request(endpoint, method, body, requiresAuth = true, authTokenOverride) {
387
627
  const headers = {
388
628
  'Content-Type': 'application/json',
389
629
  };
390
- if (requiresAuth && this.authToken)
391
- headers['Authorization'] = `Bearer ${this.authToken}`;
630
+ const authToken = authTokenOverride ?? (this.authMethod === 'oidc'
631
+ ? await this.ensureValidOidcToken().catch(() => null)
632
+ : this.authToken);
633
+ if (requiresAuth && authToken)
634
+ headers['Authorization'] = `Bearer ${authToken}`;
392
635
  if (this.socket.id)
393
636
  headers['X-Socket-ID'] = this.socket.id;
394
637
  const options = {
@@ -406,6 +649,97 @@ export default class docPouchClient {
406
649
  }
407
650
  throw new Error(`API error: ${response.status} ${response.statusText}`);
408
651
  }
652
+ if (response.status === 204)
653
+ return undefined;
409
654
  return await response.json();
410
655
  }
656
+ // OIDC Private Helpers
657
+ async discoverOidc() {
658
+ const response = await fetch(`${this.oidcConfig.issuer}/.well-known/openid-configuration`);
659
+ return response.json();
660
+ }
661
+ setOidcTokens(tokens) {
662
+ this.authMethod = 'oidc';
663
+ this.oidcAccessToken = tokens.accessToken;
664
+ this.oidcRefreshToken = tokens.refreshToken || this.oidcRefreshToken;
665
+ this.oidcIdToken = tokens.idToken || null;
666
+ this.oidcTokenExpiry = Date.now() + (tokens.expiresIn * 1000);
667
+ this.persistOidcSession();
668
+ }
669
+ clearOidcTokens() {
670
+ this.oidcAccessToken = null;
671
+ this.oidcRefreshToken = null;
672
+ this.oidcIdToken = null;
673
+ this.oidcTokenExpiry = 0;
674
+ this.codeVerifier = null;
675
+ this.oidcState = null;
676
+ if (this.authMethod === 'oidc')
677
+ this.authMethod = 'none';
678
+ this.clearPersistedOidcSession();
679
+ }
680
+ persistOidcSession() {
681
+ if (typeof window !== 'undefined' && window.localStorage) {
682
+ const session = {
683
+ accessToken: this.oidcAccessToken,
684
+ refreshToken: this.oidcRefreshToken,
685
+ idToken: this.oidcIdToken,
686
+ expiry: this.oidcTokenExpiry
687
+ };
688
+ localStorage.setItem('docpouch_oidc_session', JSON.stringify(session));
689
+ }
690
+ }
691
+ clearPersistedOidcSession() {
692
+ if (typeof window !== 'undefined' && window.localStorage) {
693
+ localStorage.removeItem('docpouch_oidc_session');
694
+ }
695
+ }
696
+ async refreshOidcToken() {
697
+ if (!this.oidcRefreshToken)
698
+ throw new Error('No refresh token available');
699
+ const discovery = await this.discoverOidc();
700
+ const body = new URLSearchParams({
701
+ grant_type: 'refresh_token',
702
+ refresh_token: this.oidcRefreshToken,
703
+ client_id: this.oidcConfig.clientId
704
+ });
705
+ const response = await fetch(discovery.token_endpoint, {
706
+ method: 'POST',
707
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
708
+ body: body.toString()
709
+ });
710
+ if (!response.ok) {
711
+ this.clearOidcTokens();
712
+ throw new Error('Token refresh failed');
713
+ }
714
+ const raw = await response.json();
715
+ const tokens = {
716
+ accessToken: raw.access_token,
717
+ refreshToken: raw.refresh_token,
718
+ idToken: raw.id_token,
719
+ expiresIn: raw.expires_in,
720
+ tokenType: raw.token_type,
721
+ scope: raw.scope
722
+ };
723
+ this.setOidcTokens(tokens);
724
+ }
725
+ generateCodeVerifier() {
726
+ const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
727
+ const array = new Uint8Array(64);
728
+ crypto.getRandomValues(array);
729
+ return Array.from(array, byte => charset[byte % charset.length]).join('');
730
+ }
731
+ async generateCodeChallenge(verifier) {
732
+ const encoder = new TextEncoder();
733
+ const data = encoder.encode(verifier);
734
+ const digest = await crypto.subtle.digest('SHA-256', data);
735
+ return btoa(String.fromCharCode(...new Uint8Array(digest)))
736
+ .replace(/\+/g, '-')
737
+ .replace(/\//g, '_')
738
+ .replace(/=+$/, '');
739
+ }
740
+ generateState() {
741
+ const array = new Uint8Array(32);
742
+ crypto.getRandomValues(array);
743
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
744
+ }
411
745
  }
package/hff ADDED
@@ -0,0 +1,114 @@
1
+ warning: in the working copy of 'README.md', LF will be replaced by CRLF the next time Git touches it
2
+ warning: in the working copy of 'src/index.ts', LF will be replaced by CRLF the next time Git touches it
3
+ warning: in the working copy of 'tsconfig.json', LF will be replaced by CRLF the next time Git touches it
4
+ diff --git a/package-lock.json b/package-lock.json
5
+ index dacae59..99c15e5 100644
6
+ --- a/package-lock.json
7
+ +++ b/package-lock.json
8
+ @@ -1,12 +1,12 @@
9
+ {
10
+ "name": "docpouch-client",
11
+ - "version": "1.0.0",
12
+ + "version": "1.0.1",
13
+ "lockfileVersion": 3,
14
+ "requires": true,
15
+ "packages": {
16
+ "": {
17
+ "name": "docpouch-client",
18
+ - "version": "1.0.0",
19
+ + "version": "1.0.1",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "socket.io-client": "^4.8.3"
23
+ diff --git a/package.json b/package.json
24
+ index 47412b6..61174a1 100644
25
+ --- a/package.json
26
+ +++ b/package.json
27
+ @@ -1,6 +1,6 @@
28
+ {
29
+ "name": "docpouch-client",
30
+ - "version": "1.0.1",
31
+ + "version": "1.0.3",
32
+ "main": "dist/index.js",
33
+ "types": "dist/index.d.ts",
34
+ "exports": {
35
+ diff --git a/src/index.ts b/src/index.ts
36
+ index b9368d4..545dade 100644
37
+ --- a/src/index.ts
38
+ +++ b/src/index.ts
39
+ @@ -358,6 +358,18 @@ export default class docPouchClient {
40
+ 
41
+ // OIDC Authentication Methods
42
+ 
43
+ + /**
44
+ + * Sets the OIDC configuration to use for the callback and token exchange.
45
+ + * Call this before {@link handleOidcCallback} if the client was freshly
46
+ + * instantiated after a page reload (the config is otherwise only set
47
+ + * internally by {@link loginWithOidc} before the redirect).
48
+ + *
49
+ + * @param {I_OidcConfig} config - OIDC provider configuration.
50
+ + */
51
+ + setOidcConfig(config: I_OidcConfig): void {
52
+ + this.oidcConfig = config;
53
+ + }
54
+ +
55
+ /**
56
+ * Initiates the OIDC authentication flow by redirecting to the authorization endpoint.
57
+ *
58
+ @@ -373,11 +385,20 @@ export default class docPouchClient {
59
+ this.oidcState = this.generateState();
60
+ const codeChallenge = await this.generateCodeChallenge(this.codeVerifier!);
61
+ 
62
+ + if (typeof window !== 'undefined' && window.sessionStorage) {
63
+ + sessionStorage.setItem('docpouch_oidc_state', this.oidcState!);
64
+ + sessionStorage.setItem('docpouch_oidc_code_verifier', this.codeVerifier!);
65
+ + sessionStorage.setItem('docpouch_oidc_issuer', config.issuer);
66
+ + sessionStorage.setItem('docpouch_oidc_client_id', config.clientId);
67
+ + sessionStorage.setItem('docpouch_oidc_redirect_uri', config.redirectUri);
68
+ + }
69
+ +
70
+ + const scope = config.scope || config.scopes?.join(' ') || 'openid profile';
71
+ const params = new URLSearchParams({
72
+ response_type: 'code',
73
+ client_id: config.clientId,
74
+ redirect_uri: config.redirectUri,
75
+ - scope: config.scopes?.join(' ') || 'openid profile',
76
+ + scope,
77
+ state: this.oidcState!,
78
+ code_challenge: codeChallenge,
79
+ code_challenge_method: 'S256'
80
+ @@ -399,6 +420,26 @@ export default class docPouchClient {
81
+ 
82
+ if (error) throw new Error(`OAuth error: ${error}`);
83
+ if (!code || !state) return false;
84
+ +
85
+ + if (typeof window !== 'undefined' && window.sessionStorage) {
86
+ + this.oidcState = sessionStorage.getItem('docpouch_oidc_state') || this.oidcState;
87
+ + this.codeVerifier = sessionStorage.getItem('docpouch_oidc_code_verifier') || this.codeVerifier;
88
+ + sessionStorage.removeItem('docpouch_oidc_state');
89
+ + sessionStorage.removeItem('docpouch_oidc_code_verifier');
90
+ +
91
+ + if (!this.oidcConfig) {
92
+ + const issuer = sessionStorage.getItem('docpouch_oidc_issuer');
93
+ + const clientId = sessionStorage.getItem('docpouch_oidc_client_id');
94
+ + const redirectUri = sessionStorage.getItem('docpouch_oidc_redirect_uri');
95
+ + if (issuer && clientId && redirectUri) {
96
+ + this.oidcConfig = { issuer, clientId, redirectUri };
97
+ + sessionStorage.removeItem('docpouch_oidc_issuer');
98
+ + sessionStorage.removeItem('docpouch_oidc_client_id');
99
+ + sessionStorage.removeItem('docpouch_oidc_redirect_uri');
100
+ + }
101
+ + }
102
+ + }
103
+ +
104
+ if (state !== this.oidcState) throw new Error('State mismatch');
105
+ 
106
+ const discovery = await this.discoverOidc();
107
+ @@ -861,6 +902,7 @@ export interface I_OidcConfig {
108
+ issuer: string;
109
+ clientId: string;
110
+ redirectUri: string;
111
+ + scope?: string;
112
+ scopes?: string[];
113
+ clientSecret?: string;
114
+ }
@@ -11,7 +11,7 @@ module.exports = {
11
11
  },
12
12
  extensionsToTreatAsEsm: ['.ts'],
13
13
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
14
- testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
14
+ testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)', 'tests/**/*.test.ts'],
15
15
  collectCoverage: true,
16
16
  coverageDirectory: 'coverage',
17
17
  coverageReporters: ['text', 'lcov'],
package/package.json CHANGED
@@ -1,38 +1,41 @@
1
- {
2
- "name": "docpouch-client",
3
- "version": "0.9.1",
4
- "main": "dist/index.js",
5
- "types": "dist/index.d.ts",
6
- "exports": {
7
- ".": {
8
- "import": "./dist/index.js",
9
- "require": "./dist/index.js"
10
- }
11
- },
12
- "type": "module",
13
- "scripts": {
14
- "build": "tsc",
15
- "test": "jest"
16
- },
17
- "keywords": [
18
- "docPouch",
19
- "API"
20
- ],
21
- "author": "Jan Frecè",
22
- "license": "MIT",
23
- "description": "A small class to more easily access the docPouch API",
24
- "repository": {
25
- "type": "git",
26
- "url": "https://github.com/BFH-JTF/docpouch-client"
27
- },
28
- "devDependencies": {
29
- "@types/jest": "^30.0.0",
30
- "@types/node": "^24.12.0",
31
- "jest": "^30.3.0",
32
- "ts-jest": "^29.4.9",
33
- "typescript": "^5.9.3"
34
- },
35
- "dependencies": {
36
- "socket.io-client": "^4.8.3"
37
- }
38
- }
1
+ {
2
+ "name": "docpouch-client",
3
+ "version": "1.0.3",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "require": "./dist/index.js"
10
+ }
11
+ },
12
+ "type": "module",
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.build.json",
15
+ "test": "node --experimental-vm-modules --no-warnings ./node_modules/jest/bin/jest.js --forceExit --silent"
16
+ },
17
+ "keywords": [
18
+ "docPouch",
19
+ "API"
20
+ ],
21
+ "author": "Jan Frecè",
22
+ "license": "MIT",
23
+ "description": "Client SDK for DocPouch API - supports JWT and OIDC authentication",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/BFH-JTF/docpouch-client"
27
+ },
28
+ "devDependencies": {
29
+ "@types/express": "^5.0.6",
30
+ "@types/jest": "^30.0.0",
31
+ "@types/node": "^25.7.0",
32
+ "express": "^5.2.1",
33
+ "jest": "^30.4.2",
34
+ "socket.io": "^4.8.3",
35
+ "ts-jest": "^29.4.9",
36
+ "typescript": "^6.0.3"
37
+ },
38
+ "dependencies": {
39
+ "socket.io-client": "^4.8.3"
40
+ }
41
+ }