@bagelink/auth 1.4.182 → 1.4.184

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.cjs CHANGED
@@ -46,6 +46,17 @@ class EventEmitter {
46
46
  }
47
47
  }
48
48
  }
49
+ function queryParams() {
50
+ if (typeof window === "undefined" || !window.location?.search) {
51
+ return {};
52
+ }
53
+ const params = new URLSearchParams(window.location.search);
54
+ const result = {};
55
+ params.forEach((value, key) => {
56
+ result[key] = value;
57
+ });
58
+ return result;
59
+ }
49
60
 
50
61
  class AuthApi {
51
62
  api;
@@ -129,7 +140,8 @@ class AuthApi {
129
140
  */
130
141
  async linkSSOProvider(data) {
131
142
  return this.api.post(`/authentication/sso/${data.provider}/link`, {
132
- code: data.code
143
+ code: data.code,
144
+ state: data.state
133
145
  });
134
146
  }
135
147
  /**
@@ -463,11 +475,20 @@ function createSSOProvider(config) {
463
475
  state
464
476
  });
465
477
  },
466
- async link(code) {
478
+ async link(code, state) {
467
479
  const auth = getAuthApi();
480
+ if (typeof sessionStorage !== "undefined" && state) {
481
+ const storedState = sessionStorage.getItem(getStateKey());
482
+ sessionStorage.removeItem(getStateKey());
483
+ sessionStorage.removeItem(`oauth_provider:${state}`);
484
+ if (storedState && storedState !== state) {
485
+ throw new StateMismatchError();
486
+ }
487
+ }
468
488
  await auth.linkSSOProvider({
469
489
  provider: config.id,
470
- code
490
+ code,
491
+ state
471
492
  });
472
493
  },
473
494
  async unlink() {
@@ -490,7 +511,7 @@ function createSSOProvider(config) {
490
511
  // Default, can be overridden per provider
491
512
  };
492
513
  }
493
- const sso = {
514
+ const ssoProviders = {
494
515
  /**
495
516
  * Google OAuth Provider
496
517
  * https://developers.google.com/identity/protocols/oauth2
@@ -590,31 +611,59 @@ const sso = {
590
611
  }
591
612
  })
592
613
  };
593
- const ssoProviders = Object.values(sso);
614
+ const sso = {
615
+ // All provider instances
616
+ ...ssoProviders,
617
+ /**
618
+ * Handle OAuth callback from URL automatically
619
+ * Detects provider from state and completes login
620
+ *
621
+ * @example
622
+ * // On /auth/callback page
623
+ * const result = await sso.handleCallback()
624
+ */
625
+ handleCallback: handleOAuthCallback,
626
+ /**
627
+ * Handle OAuth link callback from URL automatically
628
+ * Detects provider from state and completes linking
629
+ *
630
+ * @example
631
+ * // On /settings/link-callback page
632
+ * await sso.handleLinkCallback()
633
+ */
634
+ handleLinkCallback: handleOAuthLinkCallback
635
+ };
636
+ const ssoProvidersList = Object.values(ssoProviders);
594
637
  function getSSOProvider(provider) {
595
- return sso[provider];
638
+ return ssoProviders[provider];
596
639
  }
597
640
  function getAllSSOProviders() {
598
- return ssoProviders;
641
+ return ssoProvidersList;
599
642
  }
600
643
  function isSupportedProvider(provider) {
601
- return provider in sso;
644
+ return provider in ssoProviders;
602
645
  }
603
- async function handleOAuthCallback() {
604
- if (typeof window === "undefined") {
605
- return null;
646
+ function handleOAuthCallback() {
647
+ const { code, state } = queryParams();
648
+ if (!code || !state) {
649
+ return Promise.resolve(null);
606
650
  }
607
- const urlParams = new URLSearchParams(window.location.search);
608
- const code = urlParams.get("code");
609
- const state = urlParams.get("state");
651
+ const provider = sessionStorage.getItem(`oauth_provider:${state}`);
652
+ if (!provider || !isSupportedProvider(provider)) {
653
+ throw new Error("Unable to determine OAuth provider. State may have expired.");
654
+ }
655
+ return ssoProviders[provider].callback(code, state);
656
+ }
657
+ function handleOAuthLinkCallback() {
658
+ const { code, state } = queryParams();
610
659
  if (!code || !state) {
611
- return null;
660
+ throw new Error("Missing code or state parameter");
612
661
  }
613
662
  const provider = sessionStorage.getItem(`oauth_provider:${state}`);
614
663
  if (!provider || !isSupportedProvider(provider)) {
615
664
  throw new Error("Unable to determine OAuth provider. State may have expired.");
616
665
  }
617
- return sso[provider].callback(code, state);
666
+ return ssoProviders[provider].link(code, state);
618
667
  }
619
668
 
620
669
  var AuthState = /* @__PURE__ */ ((AuthState2) => {
@@ -954,10 +1003,9 @@ exports.StateMismatchError = StateMismatchError;
954
1003
  exports.accountToUser = accountToUser;
955
1004
  exports.getAllSSOProviders = getAllSSOProviders;
956
1005
  exports.getSSOProvider = getSSOProvider;
957
- exports.handleOAuthCallback = handleOAuthCallback;
958
1006
  exports.initAuth = initAuth;
959
1007
  exports.isSupportedProvider = isSupportedProvider;
960
1008
  exports.setAuthContext = setAuthContext;
961
1009
  exports.sso = sso;
962
- exports.ssoProviders = ssoProviders;
1010
+ exports.ssoProvidersList = ssoProvidersList;
963
1011
  exports.useAuth = useAuth;
package/dist/index.d.cts CHANGED
@@ -197,6 +197,7 @@ interface SSOCallbackRequest {
197
197
  interface SSOLinkRequest {
198
198
  provider: SSOProvider;
199
199
  code: string;
200
+ state: string;
200
201
  }
201
202
  interface SSOUnlinkRequest {
202
203
  provider: SSOProvider;
@@ -459,8 +460,9 @@ interface SSOProviderInstance extends SSOProviderConfig {
459
460
  callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
460
461
  /**
461
462
  * Link this provider to the current logged-in user
463
+ * Call this after OAuth redirect completes on link callback page
462
464
  */
463
- link: (code: string) => Promise<void>;
465
+ link: (code: string, state?: string) => Promise<void>;
464
466
  /**
465
467
  * Unlink this provider from the current user
466
468
  */
@@ -476,86 +478,26 @@ interface SSOProviderInstance extends SSOProviderConfig {
476
478
  supportsPopup?: boolean;
477
479
  }
478
480
  /**
479
- * SSO Provider Implementations
481
+ * SSO object type with providers and helper methods
480
482
  */
481
- declare const sso: {
482
- /**
483
- * Google OAuth Provider
484
- * https://developers.google.com/identity/protocols/oauth2
485
- */
483
+ interface SSOObject {
486
484
  google: SSOProviderInstance;
487
- /**
488
- * Microsoft OAuth Provider (Azure AD / Microsoft Entra ID)
489
- * https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
490
- */
491
485
  microsoft: SSOProviderInstance;
492
- /**
493
- * GitHub OAuth Provider
494
- * https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
495
- */
496
486
  github: SSOProviderInstance;
497
- /**
498
- * Okta OAuth Provider
499
- * https://developer.okta.com/docs/guides/implement-grant-type/authcode/main/
500
- */
501
487
  okta: SSOProviderInstance;
502
- /**
503
- * Apple Sign In Provider
504
- * https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api
505
- * Note: Apple works best with redirect flow on web
506
- */
507
- apple: {
508
- supportsPopup: boolean;
509
- popup(options?: OAuthFlowOptions): Promise<any>;
510
- /**
511
- * Initiate OAuth flow with redirect (most common)
512
- * User is redirected to provider's authorization page
513
- */
514
- redirect: (options?: OAuthFlowOptions) => Promise<void>;
515
- /**
516
- * Complete OAuth flow after callback
517
- * Call this on your callback page
518
- */
519
- callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
520
- /**
521
- * Link this provider to the current logged-in user
522
- */
523
- link: (code: string) => Promise<void>;
524
- /**
525
- * Unlink this provider from the current user
526
- */
527
- unlink: () => Promise<void>;
528
- /**
529
- * Get authorization URL without redirecting
530
- */
531
- getAuthUrl: (options?: OAuthFlowOptions) => Promise<string>;
532
- /** Provider identifier */
533
- id: SSOProvider;
534
- /** Display name */
535
- name: string;
536
- /** Brand color (hex) */
537
- color: string;
538
- /** Icon identifier (for UI libraries) */
539
- icon: string;
540
- /** Default OAuth scopes */
541
- defaultScopes: string[];
542
- /** Provider-specific metadata */
543
- metadata?: {
544
- authDomain?: string;
545
- buttonText?: string;
546
- [key: string]: any;
547
- };
548
- };
549
- /**
550
- * Facebook OAuth Provider
551
- * https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
552
- */
488
+ apple: SSOProviderInstance;
553
489
  facebook: SSOProviderInstance;
554
- };
490
+ handleCallback: () => Promise<AuthenticationResponse | null>;
491
+ handleLinkCallback: () => Promise<void>;
492
+ }
493
+ /**
494
+ * SSO object with providers and global helper methods
495
+ */
496
+ declare const sso: SSOObject;
555
497
  /**
556
- * Array of all SSO providers
498
+ * Array of all SSO provider instances
557
499
  */
558
- declare const ssoProviders: readonly SSOProviderInstance[];
500
+ declare const ssoProvidersList: readonly SSOProviderInstance[];
559
501
  /**
560
502
  * Get SSO provider instance by ID
561
503
  */
@@ -568,11 +510,6 @@ declare function getAllSSOProviders(): readonly SSOProviderInstance[];
568
510
  * Check if a provider is supported
569
511
  */
570
512
  declare function isSupportedProvider(provider: string): provider is SSOProvider;
571
- /**
572
- * Handle OAuth callback from URL
573
- * Call this on your callback page to automatically detect and process the callback
574
- */
575
- declare function handleOAuthCallback(): Promise<AuthenticationResponse | null>;
576
513
 
577
514
  declare function initAuth({ baseURL, }: {
578
515
  baseURL: string;
@@ -643,32 +580,7 @@ declare function useAuth(): {
643
580
  metadata?: Record<string, any> | undefined;
644
581
  } | undefined;
645
582
  } | null>;
646
- sso: {
647
- google: SSOProviderInstance;
648
- microsoft: SSOProviderInstance;
649
- github: SSOProviderInstance;
650
- okta: SSOProviderInstance;
651
- apple: {
652
- supportsPopup: boolean;
653
- popup(options?: OAuthFlowOptions): Promise<any>;
654
- redirect: (options?: OAuthFlowOptions) => Promise<void>;
655
- callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
656
- link: (code: string) => Promise<void>;
657
- unlink: () => Promise<void>;
658
- getAuthUrl: (options?: OAuthFlowOptions) => Promise<string>;
659
- id: SSOProvider;
660
- name: string;
661
- color: string;
662
- icon: string;
663
- defaultScopes: string[];
664
- metadata?: {
665
- authDomain?: string;
666
- buttonText?: string;
667
- [key: string]: any;
668
- };
669
- };
670
- facebook: SSOProviderInstance;
671
- };
583
+ sso: SSOObject;
672
584
  getFullName: () => string;
673
585
  getIsLoggedIn: () => boolean;
674
586
  getEmail: () => string;
@@ -704,5 +616,5 @@ declare function useAuth(): {
704
616
  revokeAllSessions: (accountId?: string) => Promise<void>;
705
617
  };
706
618
 
707
- export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, handleOAuthCallback, initAuth, isSupportedProvider, setAuthContext, sso, ssoProviders, useAuth };
708
- export type { AccountInfo, ActivateAccountResponse, AuthEventHandler, AuthEventMap, AuthMethodInfo, AuthStatusResponse, AuthenticationAccount, AuthenticationAccountType, AuthenticationMethodType, AuthenticationResponse, AvailableMethodsResponse, ChangePasswordRequest, ChangePasswordResponse, CleanupSessionsResponse, DeactivateAccountResponse, DeleteAccountResponse, DeleteAllSessionsResponse, DeleteMeResponse, DeleteSessionResponse, EntityInfo, ForgotPasswordRequest, ForgotPasswordResponse, GetAccountResponse, GetMeResponse, GetMethodsResponse, GetSessionsResponse, LoginResponse, LogoutResponse, MessageResponse, NewUser, OAuthFlowOptions, OTPMetadata, PasswordLoginRequest, PersonInfo, PopupResult, RefreshSessionResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, SSOCallbackRequest, SSOCallbackResponse, SSOInitiateRequest, SSOInitiateResponse, SSOLinkRequest, SSOLinkResponse, SSOMetadata, SSOProvider, SSOProviderConfig, SSOProviderInstance, SSOUnlinkRequest, SSOUnlinkResponse, SendVerificationRequest, SendVerificationResponse, SessionInfo, SessionListResponse, UpdateAccountRequest, UpdateAccountResponse, UpdateMeResponse, UpdatePasswordForm, User, VerifyEmailRequest, VerifyEmailResponse, VerifyResetTokenResponse };
619
+ export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, initAuth, isSupportedProvider, setAuthContext, sso, ssoProvidersList, useAuth };
620
+ export type { AccountInfo, ActivateAccountResponse, AuthEventHandler, AuthEventMap, AuthMethodInfo, AuthStatusResponse, AuthenticationAccount, AuthenticationAccountType, AuthenticationMethodType, AuthenticationResponse, AvailableMethodsResponse, ChangePasswordRequest, ChangePasswordResponse, CleanupSessionsResponse, DeactivateAccountResponse, DeleteAccountResponse, DeleteAllSessionsResponse, DeleteMeResponse, DeleteSessionResponse, EntityInfo, ForgotPasswordRequest, ForgotPasswordResponse, GetAccountResponse, GetMeResponse, GetMethodsResponse, GetSessionsResponse, LoginResponse, LogoutResponse, MessageResponse, NewUser, OAuthFlowOptions, OTPMetadata, PasswordLoginRequest, PersonInfo, PopupResult, RefreshSessionResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, SSOCallbackRequest, SSOCallbackResponse, SSOInitiateRequest, SSOInitiateResponse, SSOLinkRequest, SSOLinkResponse, SSOMetadata, SSOObject, SSOProvider, SSOProviderConfig, SSOProviderInstance, SSOUnlinkRequest, SSOUnlinkResponse, SendVerificationRequest, SendVerificationResponse, SessionInfo, SessionListResponse, UpdateAccountRequest, UpdateAccountResponse, UpdateMeResponse, UpdatePasswordForm, User, VerifyEmailRequest, VerifyEmailResponse, VerifyResetTokenResponse };
package/dist/index.d.mts CHANGED
@@ -197,6 +197,7 @@ interface SSOCallbackRequest {
197
197
  interface SSOLinkRequest {
198
198
  provider: SSOProvider;
199
199
  code: string;
200
+ state: string;
200
201
  }
201
202
  interface SSOUnlinkRequest {
202
203
  provider: SSOProvider;
@@ -459,8 +460,9 @@ interface SSOProviderInstance extends SSOProviderConfig {
459
460
  callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
460
461
  /**
461
462
  * Link this provider to the current logged-in user
463
+ * Call this after OAuth redirect completes on link callback page
462
464
  */
463
- link: (code: string) => Promise<void>;
465
+ link: (code: string, state?: string) => Promise<void>;
464
466
  /**
465
467
  * Unlink this provider from the current user
466
468
  */
@@ -476,86 +478,26 @@ interface SSOProviderInstance extends SSOProviderConfig {
476
478
  supportsPopup?: boolean;
477
479
  }
478
480
  /**
479
- * SSO Provider Implementations
481
+ * SSO object type with providers and helper methods
480
482
  */
481
- declare const sso: {
482
- /**
483
- * Google OAuth Provider
484
- * https://developers.google.com/identity/protocols/oauth2
485
- */
483
+ interface SSOObject {
486
484
  google: SSOProviderInstance;
487
- /**
488
- * Microsoft OAuth Provider (Azure AD / Microsoft Entra ID)
489
- * https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
490
- */
491
485
  microsoft: SSOProviderInstance;
492
- /**
493
- * GitHub OAuth Provider
494
- * https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
495
- */
496
486
  github: SSOProviderInstance;
497
- /**
498
- * Okta OAuth Provider
499
- * https://developer.okta.com/docs/guides/implement-grant-type/authcode/main/
500
- */
501
487
  okta: SSOProviderInstance;
502
- /**
503
- * Apple Sign In Provider
504
- * https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api
505
- * Note: Apple works best with redirect flow on web
506
- */
507
- apple: {
508
- supportsPopup: boolean;
509
- popup(options?: OAuthFlowOptions): Promise<any>;
510
- /**
511
- * Initiate OAuth flow with redirect (most common)
512
- * User is redirected to provider's authorization page
513
- */
514
- redirect: (options?: OAuthFlowOptions) => Promise<void>;
515
- /**
516
- * Complete OAuth flow after callback
517
- * Call this on your callback page
518
- */
519
- callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
520
- /**
521
- * Link this provider to the current logged-in user
522
- */
523
- link: (code: string) => Promise<void>;
524
- /**
525
- * Unlink this provider from the current user
526
- */
527
- unlink: () => Promise<void>;
528
- /**
529
- * Get authorization URL without redirecting
530
- */
531
- getAuthUrl: (options?: OAuthFlowOptions) => Promise<string>;
532
- /** Provider identifier */
533
- id: SSOProvider;
534
- /** Display name */
535
- name: string;
536
- /** Brand color (hex) */
537
- color: string;
538
- /** Icon identifier (for UI libraries) */
539
- icon: string;
540
- /** Default OAuth scopes */
541
- defaultScopes: string[];
542
- /** Provider-specific metadata */
543
- metadata?: {
544
- authDomain?: string;
545
- buttonText?: string;
546
- [key: string]: any;
547
- };
548
- };
549
- /**
550
- * Facebook OAuth Provider
551
- * https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
552
- */
488
+ apple: SSOProviderInstance;
553
489
  facebook: SSOProviderInstance;
554
- };
490
+ handleCallback: () => Promise<AuthenticationResponse | null>;
491
+ handleLinkCallback: () => Promise<void>;
492
+ }
493
+ /**
494
+ * SSO object with providers and global helper methods
495
+ */
496
+ declare const sso: SSOObject;
555
497
  /**
556
- * Array of all SSO providers
498
+ * Array of all SSO provider instances
557
499
  */
558
- declare const ssoProviders: readonly SSOProviderInstance[];
500
+ declare const ssoProvidersList: readonly SSOProviderInstance[];
559
501
  /**
560
502
  * Get SSO provider instance by ID
561
503
  */
@@ -568,11 +510,6 @@ declare function getAllSSOProviders(): readonly SSOProviderInstance[];
568
510
  * Check if a provider is supported
569
511
  */
570
512
  declare function isSupportedProvider(provider: string): provider is SSOProvider;
571
- /**
572
- * Handle OAuth callback from URL
573
- * Call this on your callback page to automatically detect and process the callback
574
- */
575
- declare function handleOAuthCallback(): Promise<AuthenticationResponse | null>;
576
513
 
577
514
  declare function initAuth({ baseURL, }: {
578
515
  baseURL: string;
@@ -643,32 +580,7 @@ declare function useAuth(): {
643
580
  metadata?: Record<string, any> | undefined;
644
581
  } | undefined;
645
582
  } | null>;
646
- sso: {
647
- google: SSOProviderInstance;
648
- microsoft: SSOProviderInstance;
649
- github: SSOProviderInstance;
650
- okta: SSOProviderInstance;
651
- apple: {
652
- supportsPopup: boolean;
653
- popup(options?: OAuthFlowOptions): Promise<any>;
654
- redirect: (options?: OAuthFlowOptions) => Promise<void>;
655
- callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
656
- link: (code: string) => Promise<void>;
657
- unlink: () => Promise<void>;
658
- getAuthUrl: (options?: OAuthFlowOptions) => Promise<string>;
659
- id: SSOProvider;
660
- name: string;
661
- color: string;
662
- icon: string;
663
- defaultScopes: string[];
664
- metadata?: {
665
- authDomain?: string;
666
- buttonText?: string;
667
- [key: string]: any;
668
- };
669
- };
670
- facebook: SSOProviderInstance;
671
- };
583
+ sso: SSOObject;
672
584
  getFullName: () => string;
673
585
  getIsLoggedIn: () => boolean;
674
586
  getEmail: () => string;
@@ -704,5 +616,5 @@ declare function useAuth(): {
704
616
  revokeAllSessions: (accountId?: string) => Promise<void>;
705
617
  };
706
618
 
707
- export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, handleOAuthCallback, initAuth, isSupportedProvider, setAuthContext, sso, ssoProviders, useAuth };
708
- export type { AccountInfo, ActivateAccountResponse, AuthEventHandler, AuthEventMap, AuthMethodInfo, AuthStatusResponse, AuthenticationAccount, AuthenticationAccountType, AuthenticationMethodType, AuthenticationResponse, AvailableMethodsResponse, ChangePasswordRequest, ChangePasswordResponse, CleanupSessionsResponse, DeactivateAccountResponse, DeleteAccountResponse, DeleteAllSessionsResponse, DeleteMeResponse, DeleteSessionResponse, EntityInfo, ForgotPasswordRequest, ForgotPasswordResponse, GetAccountResponse, GetMeResponse, GetMethodsResponse, GetSessionsResponse, LoginResponse, LogoutResponse, MessageResponse, NewUser, OAuthFlowOptions, OTPMetadata, PasswordLoginRequest, PersonInfo, PopupResult, RefreshSessionResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, SSOCallbackRequest, SSOCallbackResponse, SSOInitiateRequest, SSOInitiateResponse, SSOLinkRequest, SSOLinkResponse, SSOMetadata, SSOProvider, SSOProviderConfig, SSOProviderInstance, SSOUnlinkRequest, SSOUnlinkResponse, SendVerificationRequest, SendVerificationResponse, SessionInfo, SessionListResponse, UpdateAccountRequest, UpdateAccountResponse, UpdateMeResponse, UpdatePasswordForm, User, VerifyEmailRequest, VerifyEmailResponse, VerifyResetTokenResponse };
619
+ export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, initAuth, isSupportedProvider, setAuthContext, sso, ssoProvidersList, useAuth };
620
+ export type { AccountInfo, ActivateAccountResponse, AuthEventHandler, AuthEventMap, AuthMethodInfo, AuthStatusResponse, AuthenticationAccount, AuthenticationAccountType, AuthenticationMethodType, AuthenticationResponse, AvailableMethodsResponse, ChangePasswordRequest, ChangePasswordResponse, CleanupSessionsResponse, DeactivateAccountResponse, DeleteAccountResponse, DeleteAllSessionsResponse, DeleteMeResponse, DeleteSessionResponse, EntityInfo, ForgotPasswordRequest, ForgotPasswordResponse, GetAccountResponse, GetMeResponse, GetMethodsResponse, GetSessionsResponse, LoginResponse, LogoutResponse, MessageResponse, NewUser, OAuthFlowOptions, OTPMetadata, PasswordLoginRequest, PersonInfo, PopupResult, RefreshSessionResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, SSOCallbackRequest, SSOCallbackResponse, SSOInitiateRequest, SSOInitiateResponse, SSOLinkRequest, SSOLinkResponse, SSOMetadata, SSOObject, SSOProvider, SSOProviderConfig, SSOProviderInstance, SSOUnlinkRequest, SSOUnlinkResponse, SendVerificationRequest, SendVerificationResponse, SessionInfo, SessionListResponse, UpdateAccountRequest, UpdateAccountResponse, UpdateMeResponse, UpdatePasswordForm, User, VerifyEmailRequest, VerifyEmailResponse, VerifyResetTokenResponse };
package/dist/index.d.ts CHANGED
@@ -197,6 +197,7 @@ interface SSOCallbackRequest {
197
197
  interface SSOLinkRequest {
198
198
  provider: SSOProvider;
199
199
  code: string;
200
+ state: string;
200
201
  }
201
202
  interface SSOUnlinkRequest {
202
203
  provider: SSOProvider;
@@ -459,8 +460,9 @@ interface SSOProviderInstance extends SSOProviderConfig {
459
460
  callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
460
461
  /**
461
462
  * Link this provider to the current logged-in user
463
+ * Call this after OAuth redirect completes on link callback page
462
464
  */
463
- link: (code: string) => Promise<void>;
465
+ link: (code: string, state?: string) => Promise<void>;
464
466
  /**
465
467
  * Unlink this provider from the current user
466
468
  */
@@ -476,86 +478,26 @@ interface SSOProviderInstance extends SSOProviderConfig {
476
478
  supportsPopup?: boolean;
477
479
  }
478
480
  /**
479
- * SSO Provider Implementations
481
+ * SSO object type with providers and helper methods
480
482
  */
481
- declare const sso: {
482
- /**
483
- * Google OAuth Provider
484
- * https://developers.google.com/identity/protocols/oauth2
485
- */
483
+ interface SSOObject {
486
484
  google: SSOProviderInstance;
487
- /**
488
- * Microsoft OAuth Provider (Azure AD / Microsoft Entra ID)
489
- * https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
490
- */
491
485
  microsoft: SSOProviderInstance;
492
- /**
493
- * GitHub OAuth Provider
494
- * https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
495
- */
496
486
  github: SSOProviderInstance;
497
- /**
498
- * Okta OAuth Provider
499
- * https://developer.okta.com/docs/guides/implement-grant-type/authcode/main/
500
- */
501
487
  okta: SSOProviderInstance;
502
- /**
503
- * Apple Sign In Provider
504
- * https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api
505
- * Note: Apple works best with redirect flow on web
506
- */
507
- apple: {
508
- supportsPopup: boolean;
509
- popup(options?: OAuthFlowOptions): Promise<any>;
510
- /**
511
- * Initiate OAuth flow with redirect (most common)
512
- * User is redirected to provider's authorization page
513
- */
514
- redirect: (options?: OAuthFlowOptions) => Promise<void>;
515
- /**
516
- * Complete OAuth flow after callback
517
- * Call this on your callback page
518
- */
519
- callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
520
- /**
521
- * Link this provider to the current logged-in user
522
- */
523
- link: (code: string) => Promise<void>;
524
- /**
525
- * Unlink this provider from the current user
526
- */
527
- unlink: () => Promise<void>;
528
- /**
529
- * Get authorization URL without redirecting
530
- */
531
- getAuthUrl: (options?: OAuthFlowOptions) => Promise<string>;
532
- /** Provider identifier */
533
- id: SSOProvider;
534
- /** Display name */
535
- name: string;
536
- /** Brand color (hex) */
537
- color: string;
538
- /** Icon identifier (for UI libraries) */
539
- icon: string;
540
- /** Default OAuth scopes */
541
- defaultScopes: string[];
542
- /** Provider-specific metadata */
543
- metadata?: {
544
- authDomain?: string;
545
- buttonText?: string;
546
- [key: string]: any;
547
- };
548
- };
549
- /**
550
- * Facebook OAuth Provider
551
- * https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
552
- */
488
+ apple: SSOProviderInstance;
553
489
  facebook: SSOProviderInstance;
554
- };
490
+ handleCallback: () => Promise<AuthenticationResponse | null>;
491
+ handleLinkCallback: () => Promise<void>;
492
+ }
493
+ /**
494
+ * SSO object with providers and global helper methods
495
+ */
496
+ declare const sso: SSOObject;
555
497
  /**
556
- * Array of all SSO providers
498
+ * Array of all SSO provider instances
557
499
  */
558
- declare const ssoProviders: readonly SSOProviderInstance[];
500
+ declare const ssoProvidersList: readonly SSOProviderInstance[];
559
501
  /**
560
502
  * Get SSO provider instance by ID
561
503
  */
@@ -568,11 +510,6 @@ declare function getAllSSOProviders(): readonly SSOProviderInstance[];
568
510
  * Check if a provider is supported
569
511
  */
570
512
  declare function isSupportedProvider(provider: string): provider is SSOProvider;
571
- /**
572
- * Handle OAuth callback from URL
573
- * Call this on your callback page to automatically detect and process the callback
574
- */
575
- declare function handleOAuthCallback(): Promise<AuthenticationResponse | null>;
576
513
 
577
514
  declare function initAuth({ baseURL, }: {
578
515
  baseURL: string;
@@ -643,32 +580,7 @@ declare function useAuth(): {
643
580
  metadata?: Record<string, any> | undefined;
644
581
  } | undefined;
645
582
  } | null>;
646
- sso: {
647
- google: SSOProviderInstance;
648
- microsoft: SSOProviderInstance;
649
- github: SSOProviderInstance;
650
- okta: SSOProviderInstance;
651
- apple: {
652
- supportsPopup: boolean;
653
- popup(options?: OAuthFlowOptions): Promise<any>;
654
- redirect: (options?: OAuthFlowOptions) => Promise<void>;
655
- callback: (code: string, state?: string) => Promise<AuthenticationResponse>;
656
- link: (code: string) => Promise<void>;
657
- unlink: () => Promise<void>;
658
- getAuthUrl: (options?: OAuthFlowOptions) => Promise<string>;
659
- id: SSOProvider;
660
- name: string;
661
- color: string;
662
- icon: string;
663
- defaultScopes: string[];
664
- metadata?: {
665
- authDomain?: string;
666
- buttonText?: string;
667
- [key: string]: any;
668
- };
669
- };
670
- facebook: SSOProviderInstance;
671
- };
583
+ sso: SSOObject;
672
584
  getFullName: () => string;
673
585
  getIsLoggedIn: () => boolean;
674
586
  getEmail: () => string;
@@ -704,5 +616,5 @@ declare function useAuth(): {
704
616
  revokeAllSessions: (accountId?: string) => Promise<void>;
705
617
  };
706
618
 
707
- export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, handleOAuthCallback, initAuth, isSupportedProvider, setAuthContext, sso, ssoProviders, useAuth };
708
- export type { AccountInfo, ActivateAccountResponse, AuthEventHandler, AuthEventMap, AuthMethodInfo, AuthStatusResponse, AuthenticationAccount, AuthenticationAccountType, AuthenticationMethodType, AuthenticationResponse, AvailableMethodsResponse, ChangePasswordRequest, ChangePasswordResponse, CleanupSessionsResponse, DeactivateAccountResponse, DeleteAccountResponse, DeleteAllSessionsResponse, DeleteMeResponse, DeleteSessionResponse, EntityInfo, ForgotPasswordRequest, ForgotPasswordResponse, GetAccountResponse, GetMeResponse, GetMethodsResponse, GetSessionsResponse, LoginResponse, LogoutResponse, MessageResponse, NewUser, OAuthFlowOptions, OTPMetadata, PasswordLoginRequest, PersonInfo, PopupResult, RefreshSessionResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, SSOCallbackRequest, SSOCallbackResponse, SSOInitiateRequest, SSOInitiateResponse, SSOLinkRequest, SSOLinkResponse, SSOMetadata, SSOProvider, SSOProviderConfig, SSOProviderInstance, SSOUnlinkRequest, SSOUnlinkResponse, SendVerificationRequest, SendVerificationResponse, SessionInfo, SessionListResponse, UpdateAccountRequest, UpdateAccountResponse, UpdateMeResponse, UpdatePasswordForm, User, VerifyEmailRequest, VerifyEmailResponse, VerifyResetTokenResponse };
619
+ export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, initAuth, isSupportedProvider, setAuthContext, sso, ssoProvidersList, useAuth };
620
+ export type { AccountInfo, ActivateAccountResponse, AuthEventHandler, AuthEventMap, AuthMethodInfo, AuthStatusResponse, AuthenticationAccount, AuthenticationAccountType, AuthenticationMethodType, AuthenticationResponse, AvailableMethodsResponse, ChangePasswordRequest, ChangePasswordResponse, CleanupSessionsResponse, DeactivateAccountResponse, DeleteAccountResponse, DeleteAllSessionsResponse, DeleteMeResponse, DeleteSessionResponse, EntityInfo, ForgotPasswordRequest, ForgotPasswordResponse, GetAccountResponse, GetMeResponse, GetMethodsResponse, GetSessionsResponse, LoginResponse, LogoutResponse, MessageResponse, NewUser, OAuthFlowOptions, OTPMetadata, PasswordLoginRequest, PersonInfo, PopupResult, RefreshSessionResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, SSOCallbackRequest, SSOCallbackResponse, SSOInitiateRequest, SSOInitiateResponse, SSOLinkRequest, SSOLinkResponse, SSOMetadata, SSOObject, SSOProvider, SSOProviderConfig, SSOProviderInstance, SSOUnlinkRequest, SSOUnlinkResponse, SendVerificationRequest, SendVerificationResponse, SessionInfo, SessionListResponse, UpdateAccountRequest, UpdateAccountResponse, UpdateMeResponse, UpdatePasswordForm, User, VerifyEmailRequest, VerifyEmailResponse, VerifyResetTokenResponse };
package/dist/index.mjs CHANGED
@@ -40,6 +40,17 @@ class EventEmitter {
40
40
  }
41
41
  }
42
42
  }
43
+ function queryParams() {
44
+ if (typeof window === "undefined" || !window.location?.search) {
45
+ return {};
46
+ }
47
+ const params = new URLSearchParams(window.location.search);
48
+ const result = {};
49
+ params.forEach((value, key) => {
50
+ result[key] = value;
51
+ });
52
+ return result;
53
+ }
43
54
 
44
55
  class AuthApi {
45
56
  api;
@@ -123,7 +134,8 @@ class AuthApi {
123
134
  */
124
135
  async linkSSOProvider(data) {
125
136
  return this.api.post(`/authentication/sso/${data.provider}/link`, {
126
- code: data.code
137
+ code: data.code,
138
+ state: data.state
127
139
  });
128
140
  }
129
141
  /**
@@ -457,11 +469,20 @@ function createSSOProvider(config) {
457
469
  state
458
470
  });
459
471
  },
460
- async link(code) {
472
+ async link(code, state) {
461
473
  const auth = getAuthApi();
474
+ if (typeof sessionStorage !== "undefined" && state) {
475
+ const storedState = sessionStorage.getItem(getStateKey());
476
+ sessionStorage.removeItem(getStateKey());
477
+ sessionStorage.removeItem(`oauth_provider:${state}`);
478
+ if (storedState && storedState !== state) {
479
+ throw new StateMismatchError();
480
+ }
481
+ }
462
482
  await auth.linkSSOProvider({
463
483
  provider: config.id,
464
- code
484
+ code,
485
+ state
465
486
  });
466
487
  },
467
488
  async unlink() {
@@ -484,7 +505,7 @@ function createSSOProvider(config) {
484
505
  // Default, can be overridden per provider
485
506
  };
486
507
  }
487
- const sso = {
508
+ const ssoProviders = {
488
509
  /**
489
510
  * Google OAuth Provider
490
511
  * https://developers.google.com/identity/protocols/oauth2
@@ -584,31 +605,59 @@ const sso = {
584
605
  }
585
606
  })
586
607
  };
587
- const ssoProviders = Object.values(sso);
608
+ const sso = {
609
+ // All provider instances
610
+ ...ssoProviders,
611
+ /**
612
+ * Handle OAuth callback from URL automatically
613
+ * Detects provider from state and completes login
614
+ *
615
+ * @example
616
+ * // On /auth/callback page
617
+ * const result = await sso.handleCallback()
618
+ */
619
+ handleCallback: handleOAuthCallback,
620
+ /**
621
+ * Handle OAuth link callback from URL automatically
622
+ * Detects provider from state and completes linking
623
+ *
624
+ * @example
625
+ * // On /settings/link-callback page
626
+ * await sso.handleLinkCallback()
627
+ */
628
+ handleLinkCallback: handleOAuthLinkCallback
629
+ };
630
+ const ssoProvidersList = Object.values(ssoProviders);
588
631
  function getSSOProvider(provider) {
589
- return sso[provider];
632
+ return ssoProviders[provider];
590
633
  }
591
634
  function getAllSSOProviders() {
592
- return ssoProviders;
635
+ return ssoProvidersList;
593
636
  }
594
637
  function isSupportedProvider(provider) {
595
- return provider in sso;
638
+ return provider in ssoProviders;
596
639
  }
597
- async function handleOAuthCallback() {
598
- if (typeof window === "undefined") {
599
- return null;
640
+ function handleOAuthCallback() {
641
+ const { code, state } = queryParams();
642
+ if (!code || !state) {
643
+ return Promise.resolve(null);
600
644
  }
601
- const urlParams = new URLSearchParams(window.location.search);
602
- const code = urlParams.get("code");
603
- const state = urlParams.get("state");
645
+ const provider = sessionStorage.getItem(`oauth_provider:${state}`);
646
+ if (!provider || !isSupportedProvider(provider)) {
647
+ throw new Error("Unable to determine OAuth provider. State may have expired.");
648
+ }
649
+ return ssoProviders[provider].callback(code, state);
650
+ }
651
+ function handleOAuthLinkCallback() {
652
+ const { code, state } = queryParams();
604
653
  if (!code || !state) {
605
- return null;
654
+ throw new Error("Missing code or state parameter");
606
655
  }
607
656
  const provider = sessionStorage.getItem(`oauth_provider:${state}`);
608
657
  if (!provider || !isSupportedProvider(provider)) {
609
658
  throw new Error("Unable to determine OAuth provider. State may have expired.");
610
659
  }
611
- return sso[provider].callback(code, state);
660
+ return ssoProviders[provider].link(code, state);
612
661
  }
613
662
 
614
663
  var AuthState = /* @__PURE__ */ ((AuthState2) => {
@@ -938,4 +987,4 @@ function useAuth() {
938
987
  };
939
988
  }
940
989
 
941
- export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, handleOAuthCallback, initAuth, isSupportedProvider, setAuthContext, sso, ssoProviders, useAuth };
990
+ export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, initAuth, isSupportedProvider, setAuthContext, sso, ssoProvidersList, useAuth };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/auth",
3
3
  "type": "module",
4
- "version": "1.4.182",
4
+ "version": "1.4.184",
5
5
  "description": "Bagelink auth package",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
package/src/api.ts CHANGED
@@ -136,6 +136,7 @@ export class AuthApi {
136
136
  async linkSSOProvider(data: SSOLinkRequest): Promise<SSOLinkResponse> {
137
137
  return this.api.post(`/authentication/sso/${data.provider}/link`, {
138
138
  code: data.code,
139
+ state: data.state,
139
140
  })
140
141
  }
141
142
 
package/src/sso.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { SSOProvider, AuthenticationResponse } from './types'
2
+ import { queryParams } from './utils'
2
3
 
3
4
  // Global reference to auth API - will be set by setAuthContext
4
5
  let authApiRef: any = null
@@ -135,8 +136,9 @@ export interface SSOProviderInstance extends SSOProviderConfig {
135
136
 
136
137
  /**
137
138
  * Link this provider to the current logged-in user
139
+ * Call this after OAuth redirect completes on link callback page
138
140
  */
139
- link: (code: string) => Promise<void>
141
+ link: (code: string, state?: string) => Promise<void>
140
142
 
141
143
  /**
142
144
  * Unlink this provider from the current user
@@ -155,6 +157,23 @@ export interface SSOProviderInstance extends SSOProviderConfig {
155
157
  supportsPopup?: boolean
156
158
  }
157
159
 
160
+ /**
161
+ * SSO object type with providers and helper methods
162
+ */
163
+ export interface SSOObject {
164
+ // Provider instances
165
+ google: SSOProviderInstance
166
+ microsoft: SSOProviderInstance
167
+ github: SSOProviderInstance
168
+ okta: SSOProviderInstance
169
+ apple: SSOProviderInstance
170
+ facebook: SSOProviderInstance
171
+
172
+ // Global helper methods
173
+ handleCallback: () => Promise<AuthenticationResponse | null>
174
+ handleLinkCallback: () => Promise<void>
175
+ }
176
+
158
177
  /**
159
178
  * Helper to generate random state for CSRF protection
160
179
  * Uses 32 bytes (64 hex chars) for enhanced security
@@ -372,11 +391,25 @@ function createSSOProvider(config: SSOProviderConfig): SSOProviderInstance {
372
391
  })
373
392
  },
374
393
 
375
- async link(code: string) {
394
+ async link(code: string, state?: string) {
376
395
  const auth = getAuthApi()
396
+
397
+ // Verify state if it was stored (per-provider key)
398
+ if (typeof sessionStorage !== 'undefined' && state) {
399
+ const storedState = sessionStorage.getItem(getStateKey())
400
+ sessionStorage.removeItem(getStateKey())
401
+ // Clean up provider mapping
402
+ sessionStorage.removeItem(`oauth_provider:${state}`)
403
+
404
+ if (storedState && storedState !== state) {
405
+ throw new StateMismatchError()
406
+ }
407
+ }
408
+
377
409
  await auth.linkSSOProvider({
378
410
  provider: config.id,
379
411
  code,
412
+ state,
380
413
  })
381
414
  },
382
415
 
@@ -404,9 +437,9 @@ function createSSOProvider(config: SSOProviderConfig): SSOProviderInstance {
404
437
  }
405
438
 
406
439
  /**
407
- * SSO Provider Implementations
440
+ * SSO Provider Implementations with global helpers
408
441
  */
409
- export const sso = {
442
+ const ssoProviders = {
410
443
  /**
411
444
  * Google OAuth Provider
412
445
  * https://developers.google.com/identity/protocols/oauth2
@@ -512,54 +545,96 @@ export const sso = {
512
545
  }
513
546
 
514
547
  /**
515
- * Array of all SSO providers
548
+ * SSO object with providers and global helper methods
516
549
  */
517
- export const ssoProviders = Object.values(sso) as readonly SSOProviderInstance[]
550
+ export const sso: SSOObject = {
551
+ // All provider instances
552
+ ...ssoProviders,
553
+
554
+ /**
555
+ * Handle OAuth callback from URL automatically
556
+ * Detects provider from state and completes login
557
+ *
558
+ * @example
559
+ * // On /auth/callback page
560
+ * const result = await sso.handleCallback()
561
+ */
562
+ handleCallback: handleOAuthCallback,
563
+
564
+ /**
565
+ * Handle OAuth link callback from URL automatically
566
+ * Detects provider from state and completes linking
567
+ *
568
+ * @example
569
+ * // On /settings/link-callback page
570
+ * await sso.handleLinkCallback()
571
+ */
572
+ handleLinkCallback: handleOAuthLinkCallback,
573
+ }
574
+
575
+ /**
576
+ * Array of all SSO provider instances
577
+ */
578
+ export const ssoProvidersList = Object.values(ssoProviders) as readonly SSOProviderInstance[]
518
579
 
519
580
  /**
520
581
  * Get SSO provider instance by ID
521
582
  */
522
583
  export function getSSOProvider(provider: SSOProvider): SSOProviderInstance | undefined {
523
- return sso[provider]
584
+ return ssoProviders[provider]
524
585
  }
525
586
 
526
587
  /**
527
588
  * Get all available SSO providers
528
589
  */
529
590
  export function getAllSSOProviders(): readonly SSOProviderInstance[] {
530
- return ssoProviders
591
+ return ssoProvidersList
531
592
  }
532
593
 
533
594
  /**
534
595
  * Check if a provider is supported
535
596
  */
536
597
  export function isSupportedProvider(provider: string): provider is SSOProvider {
537
- return provider in sso
598
+ return provider in ssoProviders
538
599
  }
539
600
 
540
601
  /**
541
602
  * Handle OAuth callback from URL
542
- * Call this on your callback page to automatically detect and process the callback
603
+ * Internal helper - use sso.handleCallback() instead
543
604
  */
544
- export async function handleOAuthCallback(): Promise<AuthenticationResponse | null> {
545
- if (typeof window === 'undefined') {
546
- return null
605
+ function handleOAuthCallback(): Promise<AuthenticationResponse | null> {
606
+ const { code, state } = queryParams()
607
+ if (!code || !state) {
608
+ return Promise.resolve(null)
609
+ }
610
+
611
+ // Get the provider from sessionStorage (stored during redirect/popup)
612
+ const provider = sessionStorage.getItem(`oauth_provider:${state}`) as SSOProvider | null
613
+
614
+ if (!provider || !isSupportedProvider(provider)) {
615
+ throw new Error('Unable to determine OAuth provider. State may have expired.')
547
616
  }
548
617
 
549
- const urlParams = new URLSearchParams(window.location.search)
550
- const code = urlParams.get('code')
551
- const state = urlParams.get('state')
618
+ return ssoProviders[provider].callback(code, state)
619
+ }
620
+
621
+ /**
622
+ * Handle OAuth link callback from URL
623
+ * Internal helper - use sso.handleLinkCallback() instead
624
+ */
625
+ function handleOAuthLinkCallback(): Promise<void> {
626
+ const { code, state } = queryParams()
552
627
 
553
628
  if (!code || !state) {
554
- return null
629
+ throw new Error('Missing code or state parameter')
555
630
  }
556
631
 
557
- // Get the provider from sessionStorage (stored during redirect/popup)
632
+ // Get the provider from sessionStorage (stored during redirect)
558
633
  const provider = sessionStorage.getItem(`oauth_provider:${state}`) as SSOProvider | null
559
634
 
560
635
  if (!provider || !isSupportedProvider(provider)) {
561
636
  throw new Error('Unable to determine OAuth provider. State may have expired.')
562
637
  }
563
638
 
564
- return sso[provider].callback(code, state)
639
+ return ssoProviders[provider].link(code, state)
565
640
  }
package/src/types.ts CHANGED
@@ -244,6 +244,7 @@ export interface SSOCallbackRequest {
244
244
  export interface SSOLinkRequest {
245
245
  provider: SSOProvider
246
246
  code: string
247
+ state: string
247
248
  }
248
249
 
249
250
  export interface SSOUnlinkRequest {
package/src/utils.ts CHANGED
@@ -44,3 +44,15 @@ export class EventEmitter {
44
44
  }
45
45
  }
46
46
  }
47
+
48
+ export function queryParams(): Record<string, string> {
49
+ if (typeof window === 'undefined' || !window.location?.search) {
50
+ return {}
51
+ }
52
+ const params = new URLSearchParams(window.location.search)
53
+ const result: Record<string, string> = {}
54
+ params.forEach((value, key) => {
55
+ result[key] = value
56
+ })
57
+ return result
58
+ }