docpouch-client 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Socket } from "socket.io-client";
2
2
  /**
3
3
  * Client for interacting with docPouch API.
4
+ * Supports JWT and OIDC authentication with comprehensive logout capabilities.
5
+ * Provides event emitter functionality for logout events.
4
6
  */
5
7
  export default class docPouchClient {
6
8
  /**
@@ -49,6 +51,10 @@ export default class docPouchClient {
49
51
  * @type {boolean}
50
52
  */
51
53
  private connectionInProgress;
54
+ /**
55
+ * Event emitter for logout events
56
+ */
57
+ private events;
52
58
  /**
53
59
  * Creates an instance of docPouchClient.
54
60
  *
@@ -227,9 +233,43 @@ export default class docPouchClient {
227
233
  /**
228
234
  * Logs out the client, clearing all tokens and disconnecting WebSocket.
229
235
  *
236
+ * @param {LogoutOptions} [options] - Logout options for OIDC logout
230
237
  * @returns {Promise<void>}
231
238
  */
232
- logout(): Promise<void>;
239
+ logout(options?: LogoutOptions): Promise<void>;
240
+ /**
241
+ * Explicitly logout from OIDC provider
242
+ * Redirects to /end_session endpoint
243
+ *
244
+ * @param {LogoutOptions} [options] - Logout options
245
+ * @returns {Promise<void>}
246
+ */
247
+ logoutOidc(options?: LogoutOptions): Promise<void>;
248
+ /**
249
+ * Logout from JWT (client-side only)
250
+ *
251
+ * @returns {Promise<void>}
252
+ */
253
+ logoutJwt(): Promise<void>;
254
+ /**
255
+ * Listen for logout events
256
+ */
257
+ onLogout(callback: () => void): void;
258
+ /**
259
+ * Listen for OIDC logout specifically
260
+ */
261
+ onOidcLogout(callback: () => void): void;
262
+ /**
263
+ * Listen for JWT logout specifically
264
+ */
265
+ onJwtLogout(callback: () => void): void;
266
+ /**
267
+ * Check if user was just logged out (after redirect from /end_session)
268
+ * Checks URL for logout indicator or checks localStorage
269
+ *
270
+ * @returns {boolean} True if user was just logged out
271
+ */
272
+ wasJustLoggedOut(): boolean;
233
273
  /**
234
274
  * Registers a new OIDC client with the server (dynamic client registration).
235
275
  *
@@ -296,6 +336,16 @@ export default class docPouchClient {
296
336
  private generateCodeVerifier;
297
337
  private generateCodeChallenge;
298
338
  private generateState;
339
+ /**
340
+ * Get post_logout_redirect_uri after OIDC logout
341
+ *
342
+ * @returns {string | null} The post logout redirect URI or null if not set
343
+ */
344
+ getPostLogoutRedirectUri(): string | null;
345
+ /**
346
+ * Emit an event
347
+ */
348
+ private emit;
299
349
  }
300
350
  export interface I_UserEntry extends I_UserCreation {
301
351
  _id: string;
@@ -335,6 +385,10 @@ export interface I_LoginResponse {
335
385
  userName: string;
336
386
  expiresIn?: number;
337
387
  }
388
+ export interface LogoutOptions {
389
+ redirectUri?: string;
390
+ idTokenHint?: string;
391
+ }
338
392
  export interface I_OidcConfig {
339
393
  issuer: string;
340
394
  clientId: string;
@@ -342,6 +396,7 @@ export interface I_OidcConfig {
342
396
  scope?: string;
343
397
  scopes?: string[];
344
398
  clientSecret?: string;
399
+ postLogoutRedirectUri?: string;
345
400
  }
346
401
  export interface I_OidcTokenResponse {
347
402
  accessToken: string;
@@ -401,6 +456,7 @@ export interface I_DocumentCreation {
401
456
  shareWithGroup: boolean;
402
457
  shareWithDepartment: boolean;
403
458
  public: boolean;
459
+ anonymous?: boolean;
404
460
  }
405
461
  export interface I_DocumentCreationOwned extends I_DocumentCreation {
406
462
  owner: string;
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@ import { io } from "socket.io-client";
2
2
  import packetJson from '../package.json';
3
3
  /**
4
4
  * Client for interacting with docPouch API.
5
+ * Supports JWT and OIDC authentication with comprehensive logout capabilities.
6
+ * Provides event emitter functionality for logout events.
5
7
  */
6
8
  export default class docPouchClient {
7
9
  /**
@@ -40,6 +42,10 @@ export default class docPouchClient {
40
42
  * @type {boolean}
41
43
  */
42
44
  this.connectionInProgress = false;
45
+ /**
46
+ * Event emitter for logout events
47
+ */
48
+ this.events = {};
43
49
  this.baseUrl = host;
44
50
  const socketUrl = host.includes('://') ? host : `https://${host}`;
45
51
  const socketUrlWithPort = socketUrl.includes(':') && !socketUrl.endsWith(':')
@@ -411,11 +417,15 @@ export default class docPouchClient {
411
417
  return true;
412
418
  }
413
419
  restoreOidcSession() {
414
- if (typeof window === 'undefined' || !window.localStorage)
415
- return false;
416
420
  const stored = localStorage.getItem('docpouch_oidc_session');
417
- if (!stored)
421
+ if (!stored) {
422
+ // If no session, check if user was just logged out
423
+ if (this.wasJustLoggedOut()) {
424
+ // User was logged out, emit oidc-logout event
425
+ this.emit('oidc-logout');
426
+ }
418
427
  return false;
428
+ }
419
429
  try {
420
430
  const session = JSON.parse(stored);
421
431
  if (!session.accessToken || Date.now() >= session.expiry) {
@@ -474,10 +484,32 @@ export default class docPouchClient {
474
484
  /**
475
485
  * Logs out the client, clearing all tokens and disconnecting WebSocket.
476
486
  *
487
+ * @param {LogoutOptions} [options] - Logout options for OIDC logout
477
488
  * @returns {Promise<void>}
478
489
  */
479
- async logout() {
490
+ async logout(options) {
480
491
  const wasOidc = this.authMethod === 'oidc';
492
+ // For OIDC, redirect to /end_session endpoint
493
+ if (wasOidc && this.oidcConfig && typeof window !== 'undefined') {
494
+ const redirectUri = options?.redirectUri || this.oidcConfig.redirectUri || window.location.origin;
495
+ let url = `${this.oidcConfig.issuer}/end_session?post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`;
496
+ // Add id_token_hint if available
497
+ const idToken = options?.idTokenHint || this.oidcIdToken;
498
+ if (idToken) {
499
+ url += `&id_token_hint=${idToken}`;
500
+ }
501
+ // Clear tokens before redirect
502
+ this.authToken = null;
503
+ this.clearOidcTokens();
504
+ this.authMethod = 'none';
505
+ if (this.socket.connected)
506
+ this.socket.disconnect();
507
+ // Redirect to OIDC logout endpoint
508
+ window.location.href = url;
509
+ return;
510
+ }
511
+ // For JWT or no auth, just clear local state
512
+ const wasJwt = this.authMethod === 'jwt';
481
513
  this.authToken = null;
482
514
  this.clearOidcTokens();
483
515
  this.authMethod = 'none';
@@ -487,11 +519,113 @@ export default class docPouchClient {
487
519
  (window.location.search.includes('code=') || window.location.search.includes('state='))) {
488
520
  window.history.replaceState({}, '', window.location.pathname);
489
521
  }
490
- if (wasOidc && this.oidcConfig && typeof window !== 'undefined') {
491
- const logoutUrl = `${this.oidcConfig.issuer}/session/end?post_logout_redirect_uri=${encodeURIComponent(this.oidcConfig.redirectUri)}`;
492
- window.location.href = logoutUrl;
522
+ // Emit appropriate logout events
523
+ if (wasJwt) {
524
+ this.emit('jwt-logout');
525
+ this.emit('logout');
526
+ }
527
+ else if (!wasOidc) {
528
+ // No auth case
529
+ this.emit('logout');
493
530
  }
494
531
  }
532
+ /**
533
+ * Explicitly logout from OIDC provider
534
+ * Redirects to /end_session endpoint
535
+ *
536
+ * @param {LogoutOptions} [options] - Logout options
537
+ * @returns {Promise<void>}
538
+ */
539
+ async logoutOidc(options) {
540
+ if (this.authMethod !== 'oidc') {
541
+ throw new Error('Not logged in with OIDC');
542
+ }
543
+ if (!this.oidcConfig?.issuer) {
544
+ throw new Error('OIDC issuer not configured');
545
+ }
546
+ const { redirectUri = this.oidcConfig.redirectUri || window.location.origin, idTokenHint } = options || {};
547
+ let url = `${this.oidcConfig.issuer}/end_session?post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`;
548
+ if (idTokenHint) {
549
+ url += `&id_token_hint=${idTokenHint}`;
550
+ }
551
+ else if (this.oidcIdToken) {
552
+ url += `&id_token_hint=${this.oidcIdToken}`;
553
+ }
554
+ // Clear tokens before redirect
555
+ this.authToken = null;
556
+ this.clearOidcTokens();
557
+ this.authMethod = 'none';
558
+ if (this.socket.connected)
559
+ this.socket.disconnect();
560
+ // Emit OIDC logout event
561
+ this.emit('oidc-logout');
562
+ this.emit('logout');
563
+ window.location.href = url;
564
+ }
565
+ /**
566
+ * Logout from JWT (client-side only)
567
+ *
568
+ * @returns {Promise<void>}
569
+ */
570
+ async logoutJwt() {
571
+ if (this.authMethod !== 'jwt') {
572
+ throw new Error('Not logged in with JWT');
573
+ }
574
+ this.authToken = null;
575
+ this.authMethod = 'none';
576
+ if (this.socket.connected)
577
+ this.socket.disconnect();
578
+ if (typeof window !== 'undefined' && window.location &&
579
+ (window.location.search.includes('code=') || window.location.search.includes('state='))) {
580
+ window.history.replaceState({}, '', window.location.pathname);
581
+ }
582
+ // Emit JWT logout event
583
+ if (this.events['jwt-logout']) {
584
+ this.events['jwt-logout']();
585
+ }
586
+ if (this.events['logout']) {
587
+ this.events['logout']();
588
+ }
589
+ }
590
+ /**
591
+ * Listen for logout events
592
+ */
593
+ onLogout(callback) {
594
+ this.events['logout'] = callback;
595
+ }
596
+ /**
597
+ * Listen for OIDC logout specifically
598
+ */
599
+ onOidcLogout(callback) {
600
+ this.events['oidc-logout'] = callback;
601
+ }
602
+ /**
603
+ * Listen for JWT logout specifically
604
+ */
605
+ onJwtLogout(callback) {
606
+ this.events['jwt-logout'] = callback;
607
+ }
608
+ /**
609
+ * Check if user was just logged out (after redirect from /end_session)
610
+ * Checks URL for logout indicator or checks localStorage
611
+ *
612
+ * @returns {boolean} True if user was just logged out
613
+ */
614
+ wasJustLoggedOut() {
615
+ // Check URL query parameter
616
+ const urlParams = new URLSearchParams(window.location.search);
617
+ if (urlParams.get('logout') === 'true') {
618
+ return true;
619
+ }
620
+ // Check localStorage flag
621
+ const lastAuthMethod = localStorage.getItem('lastAuthMethod');
622
+ const currentAuthMethod = localStorage.getItem('authMethod');
623
+ // If last was OIDC/JWT but current is none, was logged out
624
+ if (lastAuthMethod && currentAuthMethod === null) {
625
+ return true;
626
+ }
627
+ return false;
628
+ }
495
629
  // OIDC Dynamic Client Registration Methods
496
630
  /**
497
631
  * Registers a new OIDC client with the server (dynamic client registration).
@@ -747,4 +881,20 @@ export default class docPouchClient {
747
881
  crypto.getRandomValues(array);
748
882
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
749
883
  }
884
+ /**
885
+ * Get post_logout_redirect_uri after OIDC logout
886
+ *
887
+ * @returns {string | null} The post logout redirect URI or null if not set
888
+ */
889
+ getPostLogoutRedirectUri() {
890
+ return localStorage.getItem('postLogoutRedirectUri');
891
+ }
892
+ /**
893
+ * Emit an event
894
+ */
895
+ emit(event) {
896
+ if (this.events[event]) {
897
+ this.events[event]();
898
+ }
899
+ }
750
900
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docpouch-client",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {