@strands.gg/accui 1.6.3 → 1.6.5

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.
Files changed (184) hide show
  1. package/dist/accui.css +838 -0
  2. package/dist/nuxt/module.cjs.js +109 -0
  3. package/dist/nuxt/module.cjs.js.map +1 -0
  4. package/dist/nuxt/module.es.js +109 -0
  5. package/dist/nuxt/module.es.js.map +1 -0
  6. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +57 -0
  7. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js.map +1 -0
  8. package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +57 -0
  9. package/dist/nuxt/runtime/composables/useStrandsAuth.es.js.map +1 -0
  10. package/dist/nuxt/runtime/middleware/auth.global.cjs.js +42 -0
  11. package/dist/nuxt/runtime/middleware/auth.global.cjs.js.map +1 -0
  12. package/dist/nuxt/runtime/middleware/auth.global.es.js +43 -0
  13. package/dist/nuxt/runtime/middleware/auth.global.es.js.map +1 -0
  14. package/dist/nuxt/runtime/plugin.client.cjs.js +26 -0
  15. package/dist/nuxt/runtime/plugin.client.cjs.js.map +1 -0
  16. package/dist/nuxt/runtime/plugin.client.es.js +27 -0
  17. package/dist/nuxt/runtime/plugin.client.es.js.map +1 -0
  18. package/dist/nuxt/runtime/plugin.server.cjs.js +17 -0
  19. package/dist/nuxt/runtime/plugin.server.cjs.js.map +1 -0
  20. package/dist/nuxt/runtime/plugin.server.es.js +18 -0
  21. package/dist/nuxt/runtime/plugin.server.es.js.map +1 -0
  22. package/dist/nuxt-v4/module.cjs.js +119 -0
  23. package/dist/nuxt-v4/module.cjs.js.map +1 -0
  24. package/dist/nuxt-v4/module.es.js +119 -0
  25. package/dist/nuxt-v4/module.es.js.map +1 -0
  26. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.cjs.js +70 -0
  27. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.cjs.js.map +1 -0
  28. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.es.js +70 -0
  29. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.es.js.map +1 -0
  30. package/dist/nuxt-v4/runtime/middleware/auth.global.cjs.js +42 -0
  31. package/dist/nuxt-v4/runtime/middleware/auth.global.cjs.js.map +1 -0
  32. package/dist/nuxt-v4/runtime/middleware/auth.global.es.js +43 -0
  33. package/dist/nuxt-v4/runtime/middleware/auth.global.es.js.map +1 -0
  34. package/dist/nuxt-v4/runtime/plugin.client.cjs.js +26 -0
  35. package/dist/nuxt-v4/runtime/plugin.client.cjs.js.map +1 -0
  36. package/dist/nuxt-v4/runtime/plugin.client.es.js +27 -0
  37. package/dist/nuxt-v4/runtime/plugin.client.es.js.map +1 -0
  38. package/dist/nuxt-v4/runtime/plugin.server.cjs.js +22 -0
  39. package/dist/nuxt-v4/runtime/plugin.server.cjs.js.map +1 -0
  40. package/dist/nuxt-v4/runtime/plugin.server.es.js +23 -0
  41. package/dist/nuxt-v4/runtime/plugin.server.es.js.map +1 -0
  42. package/dist/strands-auth-ui.cjs.js +12788 -0
  43. package/dist/strands-auth-ui.cjs.js.map +1 -0
  44. package/dist/strands-auth-ui.es.js +12789 -0
  45. package/dist/strands-auth-ui.es.js.map +1 -0
  46. package/dist/useStrandsAuth-CO9JEdxM.js +693 -0
  47. package/dist/useStrandsAuth-CO9JEdxM.js.map +1 -0
  48. package/dist/useStrandsAuth-z4jAu9Uv.cjs +692 -0
  49. package/dist/useStrandsAuth-z4jAu9Uv.cjs.map +1 -0
  50. package/dist/useStrandsConfig-Bdk-g0jS.js +170 -0
  51. package/dist/useStrandsConfig-Bdk-g0jS.js.map +1 -0
  52. package/dist/useStrandsConfig-CtmQtE7Y.cjs +169 -0
  53. package/dist/useStrandsConfig-CtmQtE7Y.cjs.map +1 -0
  54. package/package.json +1 -1
  55. package/dist/index.d.ts +0 -7
  56. package/dist/index.d.ts.map +0 -1
  57. package/dist/nuxt/module.d.ts +0 -5
  58. package/dist/nuxt/module.d.ts.map +0 -1
  59. package/dist/nuxt/runtime/composables/useStrandsAuth.d.ts +0 -137
  60. package/dist/nuxt/runtime/composables/useStrandsAuth.d.ts.map +0 -1
  61. package/dist/nuxt/runtime/middleware/auth.global.d.ts +0 -3
  62. package/dist/nuxt/runtime/middleware/auth.global.d.ts.map +0 -1
  63. package/dist/nuxt/runtime/plugin.client.d.ts +0 -3
  64. package/dist/nuxt/runtime/plugin.client.d.ts.map +0 -1
  65. package/dist/nuxt/runtime/plugin.server.d.ts +0 -3
  66. package/dist/nuxt/runtime/plugin.server.d.ts.map +0 -1
  67. package/dist/nuxt/runtime/styles.css +0 -78
  68. package/dist/nuxt/types.d.ts +0 -46
  69. package/dist/nuxt/types.d.ts.map +0 -1
  70. package/dist/nuxt-v4/module.d.ts +0 -5
  71. package/dist/nuxt-v4/module.d.ts.map +0 -1
  72. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.d.ts +0 -27
  73. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.d.ts.map +0 -1
  74. package/dist/nuxt-v4/runtime/middleware/auth.global.d.ts +0 -3
  75. package/dist/nuxt-v4/runtime/middleware/auth.global.d.ts.map +0 -1
  76. package/dist/nuxt-v4/runtime/plugin.client.d.ts +0 -3
  77. package/dist/nuxt-v4/runtime/plugin.client.d.ts.map +0 -1
  78. package/dist/nuxt-v4/runtime/plugin.server.d.ts +0 -3
  79. package/dist/nuxt-v4/runtime/plugin.server.d.ts.map +0 -1
  80. package/dist/nuxt-v4/runtime/styles.css +0 -141
  81. package/dist/nuxt-v4/types.d.ts +0 -64
  82. package/dist/nuxt-v4/types.d.ts.map +0 -1
  83. package/dist/nuxt-v4.d.ts +0 -4
  84. package/dist/nuxt-v4.d.ts.map +0 -1
  85. package/dist/nuxt.d.ts +0 -3
  86. package/dist/nuxt.d.ts.map +0 -1
  87. package/dist/shared/defaults.d.ts +0 -3
  88. package/dist/shared/defaults.d.ts.map +0 -1
  89. package/dist/types/index.d.ts +0 -197
  90. package/dist/types/index.d.ts.map +0 -1
  91. package/dist/utils/index.d.ts +0 -2
  92. package/dist/utils/index.d.ts.map +0 -1
  93. package/dist/utils/slots.d.ts +0 -2
  94. package/dist/utils/slots.d.ts.map +0 -1
  95. package/dist/utils/validation.d.ts +0 -13
  96. package/dist/utils/validation.d.ts.map +0 -1
  97. package/dist/vue/components/SignedIn.vue.d.ts +0 -51
  98. package/dist/vue/components/SignedIn.vue.d.ts.map +0 -1
  99. package/dist/vue/components/SignedOut.vue.d.ts +0 -54
  100. package/dist/vue/components/SignedOut.vue.d.ts.map +0 -1
  101. package/dist/vue/components/StrandsAuth.vue.d.ts +0 -25
  102. package/dist/vue/components/StrandsAuth.vue.d.ts.map +0 -1
  103. package/dist/vue/components/StrandsBackupCodesModal.vue.d.ts +0 -12
  104. package/dist/vue/components/StrandsBackupCodesModal.vue.d.ts.map +0 -1
  105. package/dist/vue/components/StrandsCompleteSignUp.vue.d.ts +0 -21
  106. package/dist/vue/components/StrandsCompleteSignUp.vue.d.ts.map +0 -1
  107. package/dist/vue/components/StrandsConfigProvider.vue.d.ts +0 -22
  108. package/dist/vue/components/StrandsConfigProvider.vue.d.ts.map +0 -1
  109. package/dist/vue/components/StrandsConfirmModal.vue.d.ts +0 -22
  110. package/dist/vue/components/StrandsConfirmModal.vue.d.ts.map +0 -1
  111. package/dist/vue/components/StrandsEmailMfaSetupModal.vue.d.ts +0 -12
  112. package/dist/vue/components/StrandsEmailMfaSetupModal.vue.d.ts.map +0 -1
  113. package/dist/vue/components/StrandsHardwareKeySetupModal.vue.d.ts +0 -15
  114. package/dist/vue/components/StrandsHardwareKeySetupModal.vue.d.ts.map +0 -1
  115. package/dist/vue/components/StrandsLogo.vue.d.ts +0 -8
  116. package/dist/vue/components/StrandsLogo.vue.d.ts.map +0 -1
  117. package/dist/vue/components/StrandsMFASetup.vue.d.ts +0 -16
  118. package/dist/vue/components/StrandsMFASetup.vue.d.ts.map +0 -1
  119. package/dist/vue/components/StrandsMfaModal.vue.d.ts +0 -12
  120. package/dist/vue/components/StrandsMfaModal.vue.d.ts.map +0 -1
  121. package/dist/vue/components/StrandsMfaVerification.vue.d.ts +0 -17
  122. package/dist/vue/components/StrandsMfaVerification.vue.d.ts.map +0 -1
  123. package/dist/vue/components/StrandsPasswordReset.vue.d.ts +0 -15
  124. package/dist/vue/components/StrandsPasswordReset.vue.d.ts.map +0 -1
  125. package/dist/vue/components/StrandsSecuredFooter.vue.d.ts +0 -22
  126. package/dist/vue/components/StrandsSecuredFooter.vue.d.ts.map +0 -1
  127. package/dist/vue/components/StrandsSettingsModal.vue.d.ts +0 -18
  128. package/dist/vue/components/StrandsSettingsModal.vue.d.ts.map +0 -1
  129. package/dist/vue/components/StrandsSignIn.vue.d.ts +0 -21
  130. package/dist/vue/components/StrandsSignIn.vue.d.ts.map +0 -1
  131. package/dist/vue/components/StrandsSignUp.vue.d.ts +0 -19
  132. package/dist/vue/components/StrandsSignUp.vue.d.ts.map +0 -1
  133. package/dist/vue/components/StrandsTotpSetupModal.vue.d.ts +0 -12
  134. package/dist/vue/components/StrandsTotpSetupModal.vue.d.ts.map +0 -1
  135. package/dist/vue/components/StrandsUserButton.vue.d.ts +0 -23
  136. package/dist/vue/components/StrandsUserButton.vue.d.ts.map +0 -1
  137. package/dist/vue/components/StrandsUserProfile.vue.d.ts +0 -26
  138. package/dist/vue/components/StrandsUserProfile.vue.d.ts.map +0 -1
  139. package/dist/vue/components/SvgIcon.vue.d.ts +0 -17
  140. package/dist/vue/components/SvgIcon.vue.d.ts.map +0 -1
  141. package/dist/vue/components/index.d.ts +0 -23
  142. package/dist/vue/components/index.d.ts.map +0 -1
  143. package/dist/vue/composables/useOAuthProviders.d.ts +0 -74
  144. package/dist/vue/composables/useOAuthProviders.d.ts.map +0 -1
  145. package/dist/vue/composables/useStrandsAuth.d.ts +0 -124
  146. package/dist/vue/composables/useStrandsAuth.d.ts.map +0 -1
  147. package/dist/vue/composables/useStrandsConfig.d.ts +0 -11
  148. package/dist/vue/composables/useStrandsConfig.d.ts.map +0 -1
  149. package/dist/vue/composables/useStrandsMfa.d.ts +0 -37
  150. package/dist/vue/composables/useStrandsMfa.d.ts.map +0 -1
  151. package/dist/vue/index.d.ts +0 -7
  152. package/dist/vue/index.d.ts.map +0 -1
  153. package/dist/vue/plugins/StrandsUIPlugin.d.ts +0 -18
  154. package/dist/vue/plugins/StrandsUIPlugin.d.ts.map +0 -1
  155. package/dist/vue/ui/UiAlert.vue.d.ts +0 -31
  156. package/dist/vue/ui/UiAlert.vue.d.ts.map +0 -1
  157. package/dist/vue/ui/UiAvatarEditor.vue.d.ts +0 -24
  158. package/dist/vue/ui/UiAvatarEditor.vue.d.ts.map +0 -1
  159. package/dist/vue/ui/UiAvatarEditorSimple.vue.d.ts +0 -25
  160. package/dist/vue/ui/UiAvatarEditorSimple.vue.d.ts.map +0 -1
  161. package/dist/vue/ui/UiButton.vue.d.ts +0 -40
  162. package/dist/vue/ui/UiButton.vue.d.ts.map +0 -1
  163. package/dist/vue/ui/UiCard.vue.d.ts +0 -29
  164. package/dist/vue/ui/UiCard.vue.d.ts.map +0 -1
  165. package/dist/vue/ui/UiInput.vue.d.ts +0 -48
  166. package/dist/vue/ui/UiInput.vue.d.ts.map +0 -1
  167. package/dist/vue/ui/UiLevelProgress.vue.d.ts +0 -19
  168. package/dist/vue/ui/UiLevelProgress.vue.d.ts.map +0 -1
  169. package/dist/vue/ui/UiLink.vue.d.ts +0 -35
  170. package/dist/vue/ui/UiLink.vue.d.ts.map +0 -1
  171. package/dist/vue/ui/UiLoader.vue.d.ts +0 -15
  172. package/dist/vue/ui/UiLoader.vue.d.ts.map +0 -1
  173. package/dist/vue/ui/UiModal.vue.d.ts +0 -33
  174. package/dist/vue/ui/UiModal.vue.d.ts.map +0 -1
  175. package/dist/vue/ui/UiTabs.vue.d.ts +0 -17
  176. package/dist/vue/ui/UiTabs.vue.d.ts.map +0 -1
  177. package/dist/vue/ui/UiToggle.vue.d.ts +0 -15
  178. package/dist/vue/ui/UiToggle.vue.d.ts.map +0 -1
  179. package/dist/vue/ui/index.d.ts +0 -19
  180. package/dist/vue/ui/index.d.ts.map +0 -1
  181. package/dist/vue/utils/levels.d.ts +0 -28
  182. package/dist/vue/utils/levels.d.ts.map +0 -1
  183. package/dist/vue/utils/sounds.d.ts +0 -57
  184. package/dist/vue/utils/sounds.d.ts.map +0 -1
@@ -0,0 +1,693 @@
1
+ import { ref, computed } from "vue";
2
+ import { u as useStrandsConfig } from "./useStrandsConfig-Bdk-g0jS.js";
3
+ const { getUrl } = useStrandsConfig();
4
+ const currentUser = ref(null);
5
+ const currentSession = ref(null);
6
+ const isInitializing = ref(true);
7
+ const isSigningIn = ref(false);
8
+ const isSigningUp = ref(false);
9
+ const isSigningOut = ref(false);
10
+ const isRefreshingToken = ref(false);
11
+ const isSendingMfaEmail = ref(false);
12
+ const isVerifyingMfa = ref(false);
13
+ const isLoadingProfile = ref(false);
14
+ const loading = computed(() => isSigningIn.value || isSigningUp.value || isSigningOut.value || isRefreshingToken.value || isSendingMfaEmail.value || isVerifyingMfa.value || isLoadingProfile.value);
15
+ const isLoading = computed(() => isInitializing.value || loading.value);
16
+ const loadingMessage = computed(() => {
17
+ if (isInitializing.value) return "Checking authentication...";
18
+ if (isSigningIn.value) return "Signing you in...";
19
+ if (isSigningUp.value) return "Creating your account...";
20
+ if (isSigningOut.value) return "Signing you out...";
21
+ if (isRefreshingToken.value) return "Refreshing session...";
22
+ if (isSendingMfaEmail.value) return "Sending verification code...";
23
+ if (isVerifyingMfa.value) return "Verifying code...";
24
+ return "Loading...";
25
+ });
26
+ const isInitialized = ref(false);
27
+ const mfaRequired = ref(false);
28
+ const mfaSessionId = ref(null);
29
+ const availableMfaMethods = ref([]);
30
+ let refreshTimer = null;
31
+ function useStrandsAuth() {
32
+ const completeHardwareKeyRegistration = async (deviceId, credential, accessToken) => {
33
+ if (!currentSession.value?.accessToken) {
34
+ throw new Error("No access token available");
35
+ }
36
+ const response = await fetch(getUrl("mfaHardwareCompleteRegistration"), {
37
+ method: "POST",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
41
+ },
42
+ body: JSON.stringify({
43
+ device_id: deviceId,
44
+ credential
45
+ })
46
+ });
47
+ if (!response.ok) {
48
+ const errorText = await response.text();
49
+ let errorMessage = "Failed to complete hardware key registration";
50
+ try {
51
+ const errorData = JSON.parse(errorText);
52
+ errorMessage = errorData.message || errorData.error || errorText;
53
+ } catch {
54
+ errorMessage = errorText || "Failed to complete hardware key registration";
55
+ }
56
+ throw new Error(errorMessage);
57
+ }
58
+ return response.json();
59
+ };
60
+ const registerHardwareKey = async (deviceName, accessToken, deviceType = "hardware") => {
61
+ if (!currentSession.value?.accessToken) {
62
+ throw new Error("No access token available");
63
+ }
64
+ const response = await fetch(getUrl("mfaHardwareStartRegistration"), {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
69
+ },
70
+ body: JSON.stringify({
71
+ device_name: deviceName,
72
+ device_type: deviceType
73
+ })
74
+ });
75
+ if (!response.ok) {
76
+ const errorText = await response.text();
77
+ let errorMessage = "Failed to start hardware key registration";
78
+ try {
79
+ const errorData = JSON.parse(errorText);
80
+ errorMessage = errorData.message || errorData.error || errorText;
81
+ } catch {
82
+ errorMessage = errorText || "Failed to start hardware key registration";
83
+ }
84
+ throw new Error(errorMessage);
85
+ }
86
+ return response.json();
87
+ };
88
+ const isAuthenticated = computed(() => currentUser.value !== null);
89
+ const signIn = async (credentials) => {
90
+ isSigningIn.value = true;
91
+ try {
92
+ mfaRequired.value = false;
93
+ mfaSessionId.value = null;
94
+ availableMfaMethods.value = [];
95
+ const response = await fetch(getUrl("signIn"), {
96
+ method: "POST",
97
+ headers: {
98
+ "Content-Type": "application/json"
99
+ },
100
+ body: JSON.stringify(credentials)
101
+ });
102
+ if (!response.ok) {
103
+ if (response.status === 401) {
104
+ throw new Error("Invalid email or password");
105
+ } else if (response.status === 403) {
106
+ throw new Error("Please verify your email address before signing in");
107
+ } else {
108
+ throw new Error(`Sign in failed: ${response.status} ${response.statusText}`);
109
+ }
110
+ }
111
+ const authData = await response.json();
112
+ if (authData.mfa_required) {
113
+ mfaRequired.value = true;
114
+ mfaSessionId.value = authData.mfa_session_id || null;
115
+ const methods = (authData.available_mfa_methods || []).map((method) => {
116
+ let fallbackName = `${method.device_type.charAt(0).toUpperCase() + method.device_type.slice(1)} Authentication`;
117
+ if (method.device_type === "hardware") {
118
+ fallbackName = method.device_name || "Security Key";
119
+ } else if (method.device_type === "totp") {
120
+ fallbackName = method.device_name || "Authenticator App";
121
+ } else if (method.device_type === "email") {
122
+ fallbackName = method.device_name || "Email Verification";
123
+ }
124
+ return {
125
+ id: method.device_id,
126
+ device_type: method.device_type,
127
+ device_name: method.device_name || fallbackName,
128
+ is_active: true,
129
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
130
+ last_used_at: method.last_used_at,
131
+ // Pass through additional metadata if available
132
+ credential_id: method.credential_id,
133
+ device_info: method.device_info
134
+ };
135
+ });
136
+ availableMfaMethods.value = methods;
137
+ isSigningIn.value = false;
138
+ return authData;
139
+ }
140
+ setAuthData(authData);
141
+ return authData;
142
+ } catch (error) {
143
+ throw error;
144
+ } finally {
145
+ isSigningIn.value = false;
146
+ }
147
+ };
148
+ const signUp = async (userData) => {
149
+ isSigningUp.value = true;
150
+ try {
151
+ throw new Error("Sign up not implemented - please integrate with auth SDK");
152
+ } finally {
153
+ isSigningUp.value = false;
154
+ }
155
+ };
156
+ const signOut = async () => {
157
+ isSigningOut.value = true;
158
+ try {
159
+ stopTokenRefreshTimer();
160
+ currentUser.value = null;
161
+ currentSession.value = null;
162
+ mfaRequired.value = false;
163
+ mfaSessionId.value = null;
164
+ availableMfaMethods.value = [];
165
+ if (typeof window !== "undefined") {
166
+ localStorage.removeItem("strands_auth_session");
167
+ localStorage.removeItem("strands_auth_user");
168
+ }
169
+ } finally {
170
+ isSigningOut.value = false;
171
+ }
172
+ };
173
+ const refreshToken = async () => {
174
+ if (!currentSession.value?.refreshToken) {
175
+ return false;
176
+ }
177
+ isRefreshingToken.value = true;
178
+ try {
179
+ const response = await fetch(getUrl("refresh"), {
180
+ method: "POST",
181
+ headers: {
182
+ "Content-Type": "application/json"
183
+ },
184
+ body: JSON.stringify({
185
+ refresh_token: currentSession.value.refreshToken
186
+ })
187
+ });
188
+ if (!response.ok) {
189
+ if (response.status === 401) {
190
+ await signOut();
191
+ return false;
192
+ }
193
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
194
+ }
195
+ const authData = await response.json();
196
+ setAuthData(authData, true);
197
+ return true;
198
+ } catch (error) {
199
+ await signOut();
200
+ return false;
201
+ }
202
+ };
203
+ const fetchProfile = async () => {
204
+ if (!currentSession.value?.accessToken) {
205
+ throw new Error("No access token available");
206
+ }
207
+ isLoadingProfile.value = true;
208
+ try {
209
+ const response = await fetch(getUrl("profile"), {
210
+ method: "GET",
211
+ headers: {
212
+ "Content-Type": "application/json",
213
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
214
+ }
215
+ });
216
+ if (!response.ok) {
217
+ if (response.status === 401) {
218
+ throw new Error("Authentication expired. Please sign in again.");
219
+ } else {
220
+ throw new Error(`Failed to fetch profile: ${response.status} ${response.statusText}`);
221
+ }
222
+ }
223
+ const userData = await response.json();
224
+ currentUser.value = {
225
+ id: userData.id,
226
+ email: userData.email,
227
+ firstName: userData.first_name,
228
+ lastName: userData.last_name,
229
+ avatar: userData.avatar_url,
230
+ mfaEnabled: userData.mfaEnabled || false,
231
+ emailVerified: userData.email_verified,
232
+ passwordUpdatedAt: userData.password_updated_at,
233
+ settings: userData.settings || {},
234
+ xp: userData.xp || 0,
235
+ level: userData.level || 1,
236
+ next_level_xp: userData.next_level_xp || 4,
237
+ username: userData.username,
238
+ usernameLastChangedAt: userData.username_last_changed_at,
239
+ createdAt: userData.created_at,
240
+ updatedAt: userData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
241
+ };
242
+ if (typeof window !== "undefined") {
243
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
244
+ }
245
+ return currentUser.value;
246
+ } finally {
247
+ isLoadingProfile.value = false;
248
+ }
249
+ };
250
+ const updateProfile = async (profileData) => {
251
+ if (!currentSession.value?.accessToken) {
252
+ throw new Error("No access token available");
253
+ }
254
+ isLoadingProfile.value = true;
255
+ try {
256
+ const response = await fetch(getUrl("profile"), {
257
+ method: "POST",
258
+ headers: {
259
+ "Content-Type": "application/json",
260
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
261
+ },
262
+ body: JSON.stringify({
263
+ first_name: profileData.firstName,
264
+ last_name: profileData.lastName
265
+ })
266
+ });
267
+ if (!response.ok) {
268
+ if (response.status === 401) {
269
+ throw new Error("Authentication expired. Please sign in again.");
270
+ } else {
271
+ throw new Error(`Profile update failed: ${response.status} ${response.statusText}`);
272
+ }
273
+ }
274
+ const updatedUserData = await response.json();
275
+ if (currentUser.value) {
276
+ currentUser.value = {
277
+ ...currentUser.value,
278
+ id: updatedUserData.id,
279
+ firstName: updatedUserData.first_name,
280
+ lastName: updatedUserData.last_name,
281
+ // Keep existing properties not returned by API
282
+ avatar: updatedUserData.avatar_url || currentUser.value.avatar,
283
+ mfaEnabled: currentUser.value.mfaEnabled,
284
+ email: currentUser.value.email,
285
+ // Keep existing email since we don't update it here
286
+ emailVerified: currentUser.value.emailVerified,
287
+ createdAt: updatedUserData.created_at || currentUser.value.createdAt,
288
+ updatedAt: updatedUserData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
289
+ };
290
+ if (typeof window !== "undefined") {
291
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
292
+ }
293
+ }
294
+ return currentUser.value;
295
+ } finally {
296
+ isLoadingProfile.value = false;
297
+ }
298
+ };
299
+ const updateUserSettings = async (settings) => {
300
+ if (!currentSession.value?.accessToken) {
301
+ throw new Error("No access token available");
302
+ }
303
+ isLoadingProfile.value = true;
304
+ try {
305
+ const response = await fetch(getUrl("settings"), {
306
+ method: "POST",
307
+ headers: {
308
+ "Content-Type": "application/json",
309
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
310
+ },
311
+ body: JSON.stringify({
312
+ settings
313
+ })
314
+ });
315
+ if (!response.ok) {
316
+ if (response.status === 401) {
317
+ throw new Error("Authentication expired. Please sign in again.");
318
+ } else {
319
+ throw new Error(`Settings update failed: ${response.status} ${response.statusText}`);
320
+ }
321
+ }
322
+ const updatedUserData = await response.json();
323
+ if (currentUser.value) {
324
+ currentUser.value = {
325
+ ...currentUser.value,
326
+ settings: updatedUserData.settings,
327
+ updatedAt: updatedUserData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
328
+ };
329
+ if (typeof window !== "undefined") {
330
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
331
+ }
332
+ }
333
+ return currentUser.value;
334
+ } finally {
335
+ isLoadingProfile.value = false;
336
+ }
337
+ };
338
+ const changeEmail = async (newEmail, password) => {
339
+ if (!currentSession.value?.accessToken) {
340
+ throw new Error("No access token available");
341
+ }
342
+ isLoadingProfile.value = true;
343
+ try {
344
+ const response = await fetch(getUrl("changeEmail"), {
345
+ method: "POST",
346
+ headers: {
347
+ "Content-Type": "application/json",
348
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
349
+ },
350
+ body: JSON.stringify({
351
+ new_email: newEmail,
352
+ password
353
+ })
354
+ });
355
+ if (!response.ok) {
356
+ if (response.status === 401) {
357
+ throw new Error("Authentication expired. Please sign in again.");
358
+ } else {
359
+ const errorData = await response.json().catch(() => ({}));
360
+ throw new Error(errorData.message || `Email change failed: ${response.status} ${response.statusText}`);
361
+ }
362
+ }
363
+ const result = await response.json();
364
+ if (currentUser.value) {
365
+ currentUser.value = {
366
+ ...currentUser.value,
367
+ email: newEmail,
368
+ emailVerified: false,
369
+ // Email needs to be verified again
370
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
371
+ };
372
+ if (typeof window !== "undefined") {
373
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
374
+ }
375
+ }
376
+ return result;
377
+ } finally {
378
+ isLoadingProfile.value = false;
379
+ }
380
+ };
381
+ const verifyMfa = async (deviceId, code, isBackupCode = false) => {
382
+ if (!mfaSessionId.value) {
383
+ throw new Error("No MFA session available");
384
+ }
385
+ isVerifyingMfa.value = true;
386
+ try {
387
+ const endpoint = isBackupCode ? getUrl("mfaBackupCodeVerify") : getUrl("mfaSigninVerify");
388
+ const body = isBackupCode ? { mfa_session_id: mfaSessionId.value, backup_code: code } : { mfa_session_id: mfaSessionId.value, device_id: deviceId, code };
389
+ const response = await fetch(endpoint, {
390
+ method: "POST",
391
+ headers: { "Content-Type": "application/json" },
392
+ body: JSON.stringify(body)
393
+ });
394
+ if (!response.ok) {
395
+ const errorText = await response.text();
396
+ let errorMessage = "MFA verification failed";
397
+ try {
398
+ const errorData = JSON.parse(errorText);
399
+ errorMessage = errorData.message || errorData.error || errorText;
400
+ } catch {
401
+ errorMessage = errorText || "MFA verification failed";
402
+ }
403
+ throw new Error(errorMessage);
404
+ }
405
+ const authData = await response.json();
406
+ mfaRequired.value = false;
407
+ mfaSessionId.value = null;
408
+ availableMfaMethods.value = [];
409
+ setAuthData(authData);
410
+ return authData;
411
+ } finally {
412
+ isVerifyingMfa.value = false;
413
+ }
414
+ };
415
+ const sendMfaEmailCode = async (deviceId) => {
416
+ if (!mfaSessionId.value) {
417
+ throw new Error("No MFA session available");
418
+ }
419
+ isSendingMfaEmail.value = true;
420
+ try {
421
+ const response = await fetch(getUrl("mfaSigninSendEmail"), {
422
+ method: "POST",
423
+ headers: { "Content-Type": "application/json" },
424
+ body: JSON.stringify({
425
+ mfa_session_id: mfaSessionId.value,
426
+ device_id: deviceId
427
+ })
428
+ });
429
+ if (!response.ok) {
430
+ const errorText = await response.text();
431
+ let errorMessage = "Failed to send MFA email code";
432
+ try {
433
+ const errorData = JSON.parse(errorText);
434
+ errorMessage = errorData.message || errorData.error || errorText;
435
+ } catch {
436
+ errorMessage = errorText || "Failed to send MFA email code";
437
+ }
438
+ throw new Error(errorMessage);
439
+ }
440
+ const result = await response.json();
441
+ return result;
442
+ } finally {
443
+ isSendingMfaEmail.value = false;
444
+ }
445
+ };
446
+ const getMfaWebAuthnChallenge = async (deviceId) => {
447
+ if (!mfaSessionId.value) {
448
+ throw new Error("No MFA session available");
449
+ }
450
+ const response = await fetch(getUrl("mfaWebAuthnChallenge"), {
451
+ method: "POST",
452
+ headers: { "Content-Type": "application/json" },
453
+ body: JSON.stringify({
454
+ mfa_session_id: mfaSessionId.value,
455
+ device_id: deviceId
456
+ })
457
+ });
458
+ if (!response.ok) {
459
+ const errorText = await response.text();
460
+ let errorMessage = "Failed to get WebAuthn challenge";
461
+ try {
462
+ const errorData = JSON.parse(errorText);
463
+ errorMessage = errorData.message || errorData.error || errorText;
464
+ } catch {
465
+ errorMessage = errorText || errorMessage;
466
+ }
467
+ throw new Error(errorMessage);
468
+ }
469
+ return response.json();
470
+ };
471
+ const setAuthData = (authResponse, isTokenRefresh = false) => {
472
+ try {
473
+ if (authResponse.user) {
474
+ if (isTokenRefresh && currentUser.value) {
475
+ currentUser.value = {
476
+ ...currentUser.value,
477
+ ...authResponse.user,
478
+ // Preserve certain fields that might be missing in refresh response
479
+ avatar: authResponse.user.avatar || currentUser.value.avatar,
480
+ firstName: authResponse.user.firstName || currentUser.value.firstName,
481
+ lastName: authResponse.user.lastName || currentUser.value.lastName
482
+ };
483
+ } else {
484
+ currentUser.value = authResponse.user;
485
+ }
486
+ } else if (!isTokenRefresh) {
487
+ currentUser.value = null;
488
+ }
489
+ const session = {
490
+ accessToken: authResponse.access_token,
491
+ refreshToken: authResponse.refresh_token,
492
+ expiresAt: new Date(Date.now() + 5 * 60 * 1e3),
493
+ // 5 minutes from now (matching API token expiry)
494
+ userId: currentUser.value?.id || authResponse.user?.id
495
+ };
496
+ currentSession.value = session;
497
+ if (typeof window !== "undefined") {
498
+ localStorage.setItem("strands_auth_session", JSON.stringify(session));
499
+ if (currentUser.value) {
500
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
501
+ }
502
+ }
503
+ startTokenRefreshTimer();
504
+ } catch (error) {
505
+ console.error("Error setting auth data:", error);
506
+ }
507
+ };
508
+ const startTokenRefreshTimer = () => {
509
+ if (refreshTimer) {
510
+ clearTimeout(refreshTimer);
511
+ }
512
+ if (!currentSession.value) return;
513
+ const now = /* @__PURE__ */ new Date();
514
+ const expiresAt = currentSession.value.expiresAt;
515
+ const timeUntilRefresh = expiresAt.getTime() - now.getTime() - 1 * 60 * 1e3;
516
+ if (timeUntilRefresh <= 0) {
517
+ refreshToken();
518
+ return;
519
+ }
520
+ refreshTimer = setTimeout(async () => {
521
+ const success = await refreshToken();
522
+ if (success) {
523
+ startTokenRefreshTimer();
524
+ }
525
+ }, timeUntilRefresh);
526
+ };
527
+ const stopTokenRefreshTimer = () => {
528
+ if (refreshTimer) {
529
+ clearTimeout(refreshTimer);
530
+ refreshTimer = null;
531
+ }
532
+ };
533
+ const initialize = async () => {
534
+ if (isInitialized.value) return;
535
+ isInitializing.value = true;
536
+ try {
537
+ if (typeof window !== "undefined") {
538
+ const storedSession = localStorage.getItem("strands_auth_session");
539
+ const storedUser = localStorage.getItem("strands_auth_user");
540
+ if (storedSession && storedUser) {
541
+ try {
542
+ const session = JSON.parse(storedSession);
543
+ const user = JSON.parse(storedUser);
544
+ session.expiresAt = new Date(session.expiresAt);
545
+ if (session.expiresAt > /* @__PURE__ */ new Date()) {
546
+ currentSession.value = session;
547
+ currentUser.value = user;
548
+ startTokenRefreshTimer();
549
+ } else {
550
+ localStorage.removeItem("strands_auth_session");
551
+ localStorage.removeItem("strands_auth_user");
552
+ }
553
+ } catch (error) {
554
+ localStorage.removeItem("strands_auth_session");
555
+ localStorage.removeItem("strands_auth_user");
556
+ }
557
+ }
558
+ }
559
+ isInitialized.value = true;
560
+ await new Promise((resolve) => setTimeout(resolve, 50));
561
+ } catch (error) {
562
+ } finally {
563
+ isInitializing.value = false;
564
+ }
565
+ };
566
+ const changeUsername = async (newUsername) => {
567
+ if (!currentSession.value?.accessToken) {
568
+ throw new Error("No access token available");
569
+ }
570
+ isLoadingProfile.value = true;
571
+ try {
572
+ const response = await fetch(getUrl("changeUsername"), {
573
+ method: "POST",
574
+ headers: {
575
+ "Content-Type": "application/json",
576
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
577
+ },
578
+ body: JSON.stringify({
579
+ username: newUsername
580
+ })
581
+ });
582
+ if (!response.ok) {
583
+ const errorData = await response.json().catch(() => ({}));
584
+ if (response.status === 409) {
585
+ throw new Error("Username is already taken");
586
+ } else if (errorData.cooldown_end) {
587
+ throw new Error(`You can only change your username once every 30 days. You can change it again on ${new Date(errorData.cooldown_end).toLocaleDateString()}`);
588
+ }
589
+ throw new Error(errorData.message || `Username change failed: ${response.status} ${response.statusText}`);
590
+ }
591
+ const result = await response.json();
592
+ if (currentUser.value) {
593
+ currentUser.value = {
594
+ ...currentUser.value,
595
+ username: newUsername,
596
+ usernameLastChangedAt: (/* @__PURE__ */ new Date()).toISOString(),
597
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
598
+ };
599
+ if (typeof window !== "undefined") {
600
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
601
+ }
602
+ }
603
+ return result;
604
+ } finally {
605
+ isLoadingProfile.value = false;
606
+ }
607
+ };
608
+ const getUsernameCooldown = async () => {
609
+ if (!currentSession.value?.accessToken) {
610
+ throw new Error("No access token available");
611
+ }
612
+ const response = await fetch(getUrl("usernameCooldown"), {
613
+ method: "GET",
614
+ headers: {
615
+ "Authorization": `Bearer ${currentSession.value.accessToken}`
616
+ }
617
+ });
618
+ if (!response.ok) {
619
+ throw new Error(`Failed to get username cooldown: ${response.status} ${response.statusText}`);
620
+ }
621
+ return response.json();
622
+ };
623
+ const checkUsernameAvailability = async (username) => {
624
+ const url = getUrl("checkUsernameAvailability").replace("{username}", encodeURIComponent(username));
625
+ const response = await fetch(url, {
626
+ method: "GET",
627
+ headers: {
628
+ "Content-Type": "application/json"
629
+ }
630
+ });
631
+ if (!response.ok) {
632
+ throw new Error(`Failed to check username availability: ${response.status} ${response.statusText}`);
633
+ }
634
+ return response.json();
635
+ };
636
+ if (!isInitialized.value) {
637
+ initialize();
638
+ }
639
+ return {
640
+ // State
641
+ user: computed(() => currentUser.value),
642
+ currentUser: computed(() => currentUser.value),
643
+ currentSession: computed(() => currentSession.value),
644
+ isAuthenticated,
645
+ isLoading: computed(() => isLoading.value || !isInitialized.value),
646
+ loading: computed(() => loading.value),
647
+ loadingMessage,
648
+ // Specific loading states
649
+ isInitializing,
650
+ isSigningIn,
651
+ isSigningUp,
652
+ isSigningOut,
653
+ isRefreshingToken,
654
+ isSendingMfaEmail,
655
+ isVerifyingMfa,
656
+ // MFA State
657
+ mfaRequired: computed(() => mfaRequired.value),
658
+ mfaSessionId: computed(() => mfaSessionId.value),
659
+ availableMfaMethods: computed(() => availableMfaMethods.value),
660
+ // Methods
661
+ signIn,
662
+ signUp,
663
+ signOut,
664
+ refreshToken,
665
+ fetchProfile,
666
+ updateProfile,
667
+ updateUserSettings,
668
+ changeEmail,
669
+ changeUsername,
670
+ getUsernameCooldown,
671
+ checkUsernameAvailability,
672
+ initialize,
673
+ setAuthData,
674
+ verifyMfa,
675
+ sendMfaEmailCode,
676
+ getMfaWebAuthnChallenge,
677
+ registerHardwareKey,
678
+ completeHardwareKeyRegistration,
679
+ // Token management
680
+ startTokenRefreshTimer,
681
+ stopTokenRefreshTimer,
682
+ // Force re-initialization (useful for testing or navigation)
683
+ forceReInit: () => {
684
+ isInitialized.value = false;
685
+ isInitializing.value = true;
686
+ initialize();
687
+ }
688
+ };
689
+ }
690
+ export {
691
+ useStrandsAuth as u
692
+ };
693
+ //# sourceMappingURL=useStrandsAuth-CO9JEdxM.js.map