@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, {
@@ -783,34 +970,40 @@ function DouveryAuthProvider({
783
970
  });
784
971
  return unsubscribe;
785
972
  }, [client, onAuthenticated, onLogout, onError]);
786
- const login = useCallback(async (options) => {
787
- setIsLoading(true);
788
- setError(null);
789
- try {
790
- await client.login(options);
791
- } catch (err) {
792
- const e = err instanceof Error ? err : new Error(String(err));
793
- setError(e);
794
- onError?.(e);
795
- throw e;
796
- } finally {
797
- setIsLoading(false);
798
- }
799
- }, [client, onError]);
800
- const logout = useCallback(async (options) => {
801
- setIsLoading(true);
802
- setError(null);
803
- try {
804
- await client.logout(options);
805
- } catch (err) {
806
- const e = err instanceof Error ? err : new Error(String(err));
807
- setError(e);
808
- onError?.(e);
809
- throw e;
810
- } finally {
811
- setIsLoading(false);
812
- }
813
- }, [client, onError]);
973
+ const login = useCallback(
974
+ async (options) => {
975
+ setIsLoading(true);
976
+ setError(null);
977
+ try {
978
+ await client.login(options);
979
+ } catch (err) {
980
+ const e = err instanceof Error ? err : new Error(String(err));
981
+ setError(e);
982
+ onError?.(e);
983
+ throw e;
984
+ } finally {
985
+ setIsLoading(false);
986
+ }
987
+ },
988
+ [client, onError]
989
+ );
990
+ const logout = useCallback(
991
+ async (options) => {
992
+ setIsLoading(true);
993
+ setError(null);
994
+ try {
995
+ await client.logout(options);
996
+ } catch (err) {
997
+ const e = err instanceof Error ? err : new Error(String(err));
998
+ setError(e);
999
+ onError?.(e);
1000
+ throw e;
1001
+ } finally {
1002
+ setIsLoading(false);
1003
+ }
1004
+ },
1005
+ [client, onError]
1006
+ );
814
1007
  const getAccessToken = useCallback(() => client.getAccessToken(), [client]);
815
1008
  const refreshTokens = useCallback(async () => {
816
1009
  setIsLoading(true);
@@ -825,20 +1018,100 @@ function DouveryAuthProvider({
825
1018
  setIsLoading(false);
826
1019
  }
827
1020
  }, [client, onError]);
828
- const value = useMemo(() => ({
829
- state,
830
- isInitialized,
831
- isLoading,
832
- user: state.user,
833
- isAuthenticated: state.status === "authenticated",
834
- accessToken: state.tokens?.accessToken ?? null,
835
- error,
836
- login,
837
- logout,
838
- getAccessToken,
839
- refreshTokens,
840
- client
841
- }), [state, isInitialized, isLoading, error, login, logout, getAccessToken, refreshTokens, client]);
1021
+ const selectAccount = useCallback(
1022
+ (options) => client.selectAccount(options),
1023
+ [client]
1024
+ );
1025
+ const addAccount = useCallback(
1026
+ (options) => client.addAccount(options),
1027
+ [client]
1028
+ );
1029
+ const register = useCallback(
1030
+ (options) => client.register(options),
1031
+ [client]
1032
+ );
1033
+ const recoverAccount = useCallback(
1034
+ (options) => client.recoverAccount(options),
1035
+ [client]
1036
+ );
1037
+ const verifyAccount = useCallback(
1038
+ (options) => client.verifyAccount(options),
1039
+ [client]
1040
+ );
1041
+ const upgradeAccount = useCallback(
1042
+ (options) => client.upgradeAccount(options),
1043
+ [client]
1044
+ );
1045
+ const setupPasskey = useCallback(
1046
+ (options) => client.setupPasskey(options),
1047
+ [client]
1048
+ );
1049
+ const setupAddress = useCallback(
1050
+ (options) => client.setupAddress(options),
1051
+ [client]
1052
+ );
1053
+ const revokeToken = useCallback(
1054
+ async (options) => {
1055
+ setIsLoading(true);
1056
+ setError(null);
1057
+ try {
1058
+ await client.revokeToken(options);
1059
+ } catch (err) {
1060
+ const e = err instanceof Error ? err : new Error(String(err));
1061
+ setError(e);
1062
+ onError?.(e);
1063
+ throw e;
1064
+ } finally {
1065
+ setIsLoading(false);
1066
+ }
1067
+ },
1068
+ [client, onError]
1069
+ );
1070
+ const value = useMemo(
1071
+ () => ({
1072
+ state,
1073
+ isInitialized,
1074
+ isLoading,
1075
+ user: state.user,
1076
+ isAuthenticated: state.status === "authenticated",
1077
+ accessToken: state.tokens?.accessToken ?? null,
1078
+ error,
1079
+ login,
1080
+ logout,
1081
+ selectAccount,
1082
+ addAccount,
1083
+ register,
1084
+ recoverAccount,
1085
+ verifyAccount,
1086
+ upgradeAccount,
1087
+ setupPasskey,
1088
+ setupAddress,
1089
+ revokeToken,
1090
+ getAccessToken,
1091
+ refreshTokens,
1092
+ client
1093
+ }),
1094
+ [
1095
+ state,
1096
+ isInitialized,
1097
+ isLoading,
1098
+ error,
1099
+ login,
1100
+ logout,
1101
+ selectAccount,
1102
+ addAccount,
1103
+ register,
1104
+ recoverAccount,
1105
+ verifyAccount,
1106
+ upgradeAccount,
1107
+ setupPasskey,
1108
+ setupAddress,
1109
+ revokeToken,
1110
+ getAccessToken,
1111
+ refreshTokens,
1112
+ client
1113
+ ]
1114
+ );
842
1115
  return /* @__PURE__ */ jsx(DouveryAuthContext.Provider, { value, children });
843
1116
  }
844
1117
  function useDouveryAuth() {
@@ -859,10 +1132,65 @@ function useAccessToken() {
859
1132
  return { accessToken, getAccessToken };
860
1133
  }
861
1134
  function useAuthActions() {
862
- const { login, logout, isLoading } = useDouveryAuth();
863
- return { login, logout, isLoading };
1135
+ const {
1136
+ login,
1137
+ logout,
1138
+ selectAccount,
1139
+ addAccount,
1140
+ register,
1141
+ recoverAccount,
1142
+ verifyAccount,
1143
+ upgradeAccount,
1144
+ setupPasskey,
1145
+ setupAddress,
1146
+ revokeToken,
1147
+ isLoading
1148
+ } = useDouveryAuth();
1149
+ return {
1150
+ login,
1151
+ logout,
1152
+ selectAccount,
1153
+ addAccount,
1154
+ register,
1155
+ recoverAccount,
1156
+ verifyAccount,
1157
+ upgradeAccount,
1158
+ setupPasskey,
1159
+ setupAddress,
1160
+ revokeToken,
1161
+ isLoading
1162
+ };
1163
+ }
1164
+ function useAuthUrls() {
1165
+ const { client } = useDouveryAuth();
1166
+ return useMemo(
1167
+ () => ({
1168
+ loginUrl: (options) => client.buildLoginUrl(options),
1169
+ logoutUrl: (options) => client.buildLogoutUrl(options),
1170
+ selectAccountUrl: (options) => client.buildSelectAccountUrl(options),
1171
+ addAccountUrl: (options) => client.buildAddAccountUrl(options),
1172
+ registerUrl: (options) => client.buildRegisterUrl(options),
1173
+ recoverAccountUrl: (options) => client.buildRecoverAccountUrl(options),
1174
+ verifyAccountUrl: (options) => client.buildVerifyAccountUrl(options),
1175
+ upgradeAccountUrl: (options) => client.buildUpgradeAccountUrl(options),
1176
+ setupPasskeyUrl: (options) => client.buildSetupPasskeyUrl(options),
1177
+ setupAddressUrl: (options) => client.buildSetupAddressUrl(options)
1178
+ }),
1179
+ [client]
1180
+ );
1181
+ }
1182
+ function useSessionStatus() {
1183
+ const { client, state } = useDouveryAuth();
1184
+ return useMemo(
1185
+ () => ({
1186
+ isExpired: client.isSessionExpired(),
1187
+ needsVerification: client.needsEmailVerification(),
1188
+ isGuest: client.isGuestAccount()
1189
+ }),
1190
+ [client, state]
1191
+ );
864
1192
  }
865
1193
 
866
- export { DouveryAuthClient, DouveryAuthProvider, createDouveryAuth, useAccessToken, useAuthActions, useDouveryAuth, useIsAuthenticated, useUser };
1194
+ export { DouveryAuthClient, DouveryAuthProvider, createDouveryAuth, useAccessToken, useAuthActions, useAuthUrls, useDouveryAuth, useIsAuthenticated, useSessionStatus, useUser };
867
1195
  //# sourceMappingURL=index.js.map
868
1196
  //# sourceMappingURL=index.js.map