@douvery/auth 0.2.1 → 0.3.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.
@@ -626,6 +626,171 @@ var DouveryAuthClient = class {
626
626
  }
627
627
  return tokens.accessToken;
628
628
  }
629
+ // ============================================
630
+ // Navigation Methods
631
+ // ============================================
632
+ /** Redirect to select/switch account */
633
+ selectAccount(options = {}) {
634
+ const url = this.buildSelectAccountUrl(options);
635
+ this.navigate(url, options);
636
+ }
637
+ /** Build select-account URL without redirecting */
638
+ buildSelectAccountUrl(options = {}) {
639
+ const params = new URLSearchParams();
640
+ if (options.returnTo) params.set("continue", options.returnTo);
641
+ if (options.clientId) params.set("client_id", options.clientId);
642
+ if (options.loginHint) params.set("login_hint", options.loginHint);
643
+ return this.createAuthUrl("/select-account", params);
644
+ }
645
+ /** Redirect to add another account (multi-session) */
646
+ addAccount(options = {}) {
647
+ const url = this.buildAddAccountUrl(options);
648
+ this.navigate(url, options);
649
+ }
650
+ /** Build add-account URL without redirecting */
651
+ buildAddAccountUrl(options = {}) {
652
+ const params = new URLSearchParams({ add_account: "true" });
653
+ if (options.returnTo) params.set("continue", options.returnTo);
654
+ if (options.clientId) params.set("client_id", options.clientId);
655
+ if (options.loginHint) params.set("login_hint", options.loginHint);
656
+ return this.createAuthUrl("/login", params);
657
+ }
658
+ /** Redirect to register a new account */
659
+ register(options = {}) {
660
+ const url = this.buildRegisterUrl(options);
661
+ this.navigate(url, options);
662
+ }
663
+ /** Build register URL without redirecting */
664
+ buildRegisterUrl(options = {}) {
665
+ const params = new URLSearchParams();
666
+ if (options.returnTo) params.set("continue", options.returnTo);
667
+ if (options.clientId) params.set("client_id", options.clientId);
668
+ if (options.email) params.set("email", options.email);
669
+ if (options.firstName) params.set("first_name", options.firstName);
670
+ if (options.lastName) params.set("last_name", options.lastName);
671
+ if (options.uiLocales) params.set("ui_locales", options.uiLocales);
672
+ return this.createAuthUrl("/register", params);
673
+ }
674
+ /** Redirect to recover account (forgot password) */
675
+ recoverAccount(options = {}) {
676
+ const url = this.buildRecoverAccountUrl(options);
677
+ this.navigate(url, options);
678
+ }
679
+ /** Build recover-account URL without redirecting */
680
+ buildRecoverAccountUrl(options = {}) {
681
+ const params = new URLSearchParams();
682
+ if (options.returnTo) params.set("continue", options.returnTo);
683
+ if (options.clientId) params.set("client_id", options.clientId);
684
+ if (options.email) params.set("email", options.email);
685
+ return this.createAuthUrl("/recover-account", params);
686
+ }
687
+ /** Redirect to verify account (email verification) */
688
+ verifyAccount(options = {}) {
689
+ const url = this.buildVerifyAccountUrl(options);
690
+ this.navigate(url, options);
691
+ }
692
+ /** Build verify-account URL without redirecting */
693
+ buildVerifyAccountUrl(options = {}) {
694
+ const params = new URLSearchParams();
695
+ if (options.returnTo) params.set("continue", options.returnTo);
696
+ if (options.clientId) params.set("client_id", options.clientId);
697
+ if (options.email) params.set("email", options.email);
698
+ return this.createAuthUrl("/verify-account", params);
699
+ }
700
+ /** Redirect to upgrade account (guest → full account) */
701
+ upgradeAccount(options = {}) {
702
+ const url = this.buildUpgradeAccountUrl(options);
703
+ this.navigate(url, options);
704
+ }
705
+ /** Build upgrade-account URL without redirecting */
706
+ buildUpgradeAccountUrl(options = {}) {
707
+ const params = new URLSearchParams();
708
+ if (options.returnTo) params.set("continue", options.returnTo);
709
+ if (options.clientId) params.set("client_id", options.clientId);
710
+ if (options.scopes?.length) params.set("scope", options.scopes.join(" "));
711
+ return this.createAuthUrl("/upgrade-account", params);
712
+ }
713
+ /** Redirect to passkey setup */
714
+ setupPasskey(options = {}) {
715
+ const url = this.buildSetupPasskeyUrl(options);
716
+ this.navigate(url, options);
717
+ }
718
+ /** Build setup-passkey URL without redirecting */
719
+ buildSetupPasskeyUrl(options = {}) {
720
+ const params = new URLSearchParams();
721
+ if (options.returnTo) params.set("continue", options.returnTo);
722
+ if (options.clientId) params.set("client_id", options.clientId);
723
+ return this.createAuthUrl("/setup-passkey", params);
724
+ }
725
+ /** Redirect to address setup */
726
+ setupAddress(options = {}) {
727
+ const url = this.buildSetupAddressUrl(options);
728
+ this.navigate(url, options);
729
+ }
730
+ /** Build setup-address URL without redirecting */
731
+ buildSetupAddressUrl(options = {}) {
732
+ const params = new URLSearchParams();
733
+ if (options.returnTo) params.set("continue", options.returnTo);
734
+ if (options.clientId) params.set("client_id", options.clientId);
735
+ return this.createAuthUrl("/setup-address", params);
736
+ }
737
+ /** Build a login URL without redirecting (useful for links/buttons) */
738
+ buildLoginUrl(options = {}) {
739
+ const params = new URLSearchParams();
740
+ if (options.returnTo) params.set("continue", options.returnTo);
741
+ if (options.loginHint) params.set("email", options.loginHint);
742
+ if (options.prompt) params.set("prompt", options.prompt);
743
+ if (options.uiLocales) params.set("ui_locales", options.uiLocales);
744
+ return this.createAuthUrl("/login", params);
745
+ }
746
+ /** Build a logout URL without redirecting */
747
+ buildLogoutUrl(options = {}) {
748
+ const params = new URLSearchParams();
749
+ if (options.returnTo) params.set("continue", options.returnTo);
750
+ return this.createAuthUrl("/logout", params);
751
+ }
752
+ /** Revoke a token (access or refresh) */
753
+ async revokeToken(options = {}) {
754
+ this.log("Revoking token...");
755
+ const token = options.token ?? (await this.tokenManager.getTokens())?.accessToken;
756
+ if (!token) {
757
+ throw new AuthError("invalid_token", "No token to revoke");
758
+ }
759
+ const revokeUrl = `${this.config.issuer}/oauth/revoke`;
760
+ const body = new URLSearchParams({
761
+ token,
762
+ client_id: this.config.clientId
763
+ });
764
+ if (options.tokenTypeHint) {
765
+ body.set("token_type_hint", options.tokenTypeHint);
766
+ }
767
+ const response = await fetch(revokeUrl, {
768
+ method: "POST",
769
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
770
+ body
771
+ });
772
+ if (!response.ok) {
773
+ const error = await response.json().catch(() => ({}));
774
+ throw new AuthError(
775
+ error.error ?? "server_error",
776
+ error.error_description ?? "Token revocation failed"
777
+ );
778
+ }
779
+ this.log("Token revoked successfully");
780
+ }
781
+ /** Check if the user's session is expired */
782
+ isSessionExpired() {
783
+ if (!this.state.tokens) return true;
784
+ return isTokenExpired(this.state.tokens.accessToken);
785
+ }
786
+ /** Check if user needs email verification */
787
+ needsEmailVerification() {
788
+ return this.state.user?.emailVerified === false;
789
+ }
790
+ /** Check if user is a guest account */
791
+ isGuestAccount() {
792
+ return this.state.user?.accountType === "guest";
793
+ }
629
794
  tokenSetToInfo(tokenSet) {
630
795
  return {
631
796
  accessToken: tokenSet.access_token,
@@ -636,6 +801,28 @@ var DouveryAuthClient = class {
636
801
  scope: tokenSet.scope?.split(" ") ?? []
637
802
  };
638
803
  }
804
+ /** Build an AuthUrl object for a given path and params */
805
+ createAuthUrl(path, params) {
806
+ const query = params.toString();
807
+ const url = `${this.config.issuer}${path}${query ? `?${query}` : ""}`;
808
+ return {
809
+ url,
810
+ redirect: () => {
811
+ window.location.href = url;
812
+ },
813
+ open: () => {
814
+ return window.open(url, "_blank");
815
+ }
816
+ };
817
+ }
818
+ /** Navigate to an auth URL, respecting openInNewTab option */
819
+ navigate(authUrl, options) {
820
+ if (options.openInNewTab) {
821
+ authUrl.open();
822
+ } else {
823
+ authUrl.redirect();
824
+ }
825
+ }
639
826
  async fetchUser(accessToken) {
640
827
  const discovery = await this.getDiscovery();
641
828
  const response = await fetch(discovery.userinfo_endpoint, {
@@ -734,30 +921,38 @@ function createDouveryAuth(config) {
734
921
  return new DouveryAuthClient(config);
735
922
  }
736
923
  var DouveryAuthContext = createContextId("douvery-auth");
737
- var DouveryAuthProvider = component$(({ config }) => {
738
- const client = createDouveryAuth(config);
739
- const state = useSignal(client.getState());
740
- const isInitialized = useSignal(false);
741
- const isLoading = useSignal(false);
742
- const error = useSignal(null);
743
- useContextProvider(DouveryAuthContext, { state, isInitialized, isLoading, error, client });
744
- useVisibleTask$(() => {
745
- client.initialize().then(() => {
746
- isInitialized.value = true;
747
- state.value = client.getState();
748
- }).catch((err) => {
749
- error.value = err instanceof Error ? err : new Error(String(err));
924
+ var DouveryAuthProvider = component$(
925
+ ({ config }) => {
926
+ const client = createDouveryAuth(config);
927
+ const state = useSignal(client.getState());
928
+ const isInitialized = useSignal(false);
929
+ const isLoading = useSignal(false);
930
+ const error = useSignal(null);
931
+ useContextProvider(DouveryAuthContext, {
932
+ state,
933
+ isInitialized,
934
+ isLoading,
935
+ error,
936
+ client
750
937
  });
751
- const unsubscribe = client.subscribe((event) => {
752
- state.value = client.getState();
753
- if (event.type === "LOGIN_ERROR" || event.type === "LOGOUT_ERROR" || event.type === "TOKEN_REFRESH_ERROR") {
754
- error.value = event.error;
755
- }
938
+ useVisibleTask$(() => {
939
+ client.initialize().then(() => {
940
+ isInitialized.value = true;
941
+ state.value = client.getState();
942
+ }).catch((err) => {
943
+ error.value = err instanceof Error ? err : new Error(String(err));
944
+ });
945
+ const unsubscribe = client.subscribe((event) => {
946
+ state.value = client.getState();
947
+ if (event.type === "LOGIN_ERROR" || event.type === "LOGOUT_ERROR" || event.type === "TOKEN_REFRESH_ERROR") {
948
+ error.value = event.error;
949
+ }
950
+ });
951
+ return () => unsubscribe();
756
952
  });
757
- return () => unsubscribe();
758
- });
759
- return /* @__PURE__ */ jsx(Slot, {});
760
- });
953
+ return /* @__PURE__ */ jsx(Slot, {});
954
+ }
955
+ );
761
956
  function useDouveryAuth() {
762
957
  return useContext(DouveryAuthContext);
763
958
  }
@@ -805,9 +1000,86 @@ function useAuthActions() {
805
1000
  isLoading.value = false;
806
1001
  }
807
1002
  };
808
- return { login, logout, isLoading };
1003
+ const selectAccount = (options) => {
1004
+ client.selectAccount(options);
1005
+ };
1006
+ const addAccount = (options) => {
1007
+ client.addAccount(options);
1008
+ };
1009
+ const register = (options) => {
1010
+ client.register(options);
1011
+ };
1012
+ const recoverAccount = (options) => {
1013
+ client.recoverAccount(options);
1014
+ };
1015
+ const verifyAccount = (options) => {
1016
+ client.verifyAccount(options);
1017
+ };
1018
+ const upgradeAccount = (options) => {
1019
+ client.upgradeAccount(options);
1020
+ };
1021
+ const setupPasskey = (options) => {
1022
+ client.setupPasskey(options);
1023
+ };
1024
+ const setupAddress = (options) => {
1025
+ client.setupAddress(options);
1026
+ };
1027
+ const revokeToken = async (options) => {
1028
+ isLoading.value = true;
1029
+ error.value = null;
1030
+ try {
1031
+ await client.revokeToken(options);
1032
+ } catch (err) {
1033
+ error.value = err instanceof Error ? err : new Error(String(err));
1034
+ throw err;
1035
+ } finally {
1036
+ isLoading.value = false;
1037
+ }
1038
+ };
1039
+ return {
1040
+ login,
1041
+ logout,
1042
+ selectAccount,
1043
+ addAccount,
1044
+ register,
1045
+ recoverAccount,
1046
+ verifyAccount,
1047
+ upgradeAccount,
1048
+ setupPasskey,
1049
+ setupAddress,
1050
+ revokeToken,
1051
+ isLoading
1052
+ };
1053
+ }
1054
+ function useAuthUrls() {
1055
+ const { client } = useDouveryAuth();
1056
+ return {
1057
+ loginUrl: (options) => client.buildLoginUrl(options),
1058
+ logoutUrl: (options) => client.buildLogoutUrl(options),
1059
+ selectAccountUrl: (options) => client.buildSelectAccountUrl(options),
1060
+ addAccountUrl: (options) => client.buildAddAccountUrl(options),
1061
+ registerUrl: (options) => client.buildRegisterUrl(options),
1062
+ recoverAccountUrl: (options) => client.buildRecoverAccountUrl(options),
1063
+ verifyAccountUrl: (options) => client.buildVerifyAccountUrl(options),
1064
+ upgradeAccountUrl: (options) => client.buildUpgradeAccountUrl(options),
1065
+ setupPasskeyUrl: (options) => client.buildSetupPasskeyUrl(options),
1066
+ setupAddressUrl: (options) => client.buildSetupAddressUrl(options)
1067
+ };
1068
+ }
1069
+ function useSessionStatus() {
1070
+ const { client, state } = useDouveryAuth();
1071
+ const isExpired = useSignal(client.isSessionExpired());
1072
+ const needsVerification = useSignal(client.needsEmailVerification());
1073
+ const isGuest = useSignal(client.isGuestAccount());
1074
+ useTask$(({ track }) => {
1075
+ track(() => state.value);
1076
+ isExpired.value = client.isSessionExpired();
1077
+ needsVerification.value = client.needsEmailVerification();
1078
+ isGuest.value = client.isGuestAccount();
1079
+ });
1080
+ return { isExpired, needsVerification, isGuest };
809
1081
  }
810
1082
 
811
- export { DouveryAuthClient, DouveryAuthContext, DouveryAuthProvider, createDouveryAuth, useAuthActions, useDouveryAuth, useIsAuthenticated, useUser };
1083
+ export { DouveryAuthClient, DouveryAuthContext, DouveryAuthProvider, createDouveryAuth, useAuthActions, useAuthUrls, useDouveryAuth, useIsAuthenticated, useSessionStatus, useUser };
812
1084
  //# sourceMappingURL=index.js.map
813
1085
  //# sourceMappingURL=index.js.map