@classic-homes/auth 0.1.23 → 0.1.24

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.
@@ -1,5 +1,14 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
1
11
  // src/core/config.ts
2
- var config = null;
3
12
  function initAuth(options) {
4
13
  config = {
5
14
  ...options,
@@ -44,10 +53,714 @@ function getFetch() {
44
53
  const cfg = getConfig();
45
54
  return cfg.fetch ?? fetch;
46
55
  }
56
+ var config;
57
+ var init_config = __esm({
58
+ "src/core/config.ts"() {
59
+ config = null;
60
+ }
61
+ });
62
+
63
+ // src/core/jwt.ts
64
+ function decodeJWT(token) {
65
+ try {
66
+ const parts = token.split(".");
67
+ if (parts.length !== 3) {
68
+ return null;
69
+ }
70
+ const payload = parts[1];
71
+ const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
72
+ return JSON.parse(decoded);
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+ function isTokenExpired(token) {
78
+ const payload = decodeJWT(token);
79
+ if (!payload || !payload.exp) {
80
+ return true;
81
+ }
82
+ return payload.exp * 1e3 < Date.now();
83
+ }
84
+ function getTokenRemainingTime(token) {
85
+ const payload = decodeJWT(token);
86
+ if (!payload || !payload.exp) {
87
+ return 0;
88
+ }
89
+ const remainingMs = payload.exp * 1e3 - Date.now();
90
+ return Math.max(0, remainingMs);
91
+ }
92
+ function getTokenExpiration(token) {
93
+ const payload = decodeJWT(token);
94
+ if (!payload || !payload.exp) {
95
+ return null;
96
+ }
97
+ return new Date(payload.exp * 1e3);
98
+ }
99
+ function extractClaims(token, claims) {
100
+ const payload = decodeJWT(token);
101
+ if (!payload) {
102
+ return null;
103
+ }
104
+ const result = {};
105
+ for (const claim of claims) {
106
+ if (claim in payload) {
107
+ result[claim] = payload[claim];
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+ var init_jwt = __esm({
113
+ "src/core/jwt.ts"() {
114
+ }
115
+ });
116
+
117
+ // src/core/api.ts
118
+ var authApi;
119
+ var init_api = __esm({
120
+ "src/core/api.ts"() {
121
+ init_client();
122
+ init_config();
123
+ authApi = {
124
+ // ============================================================================
125
+ // Authentication
126
+ // ============================================================================
127
+ /**
128
+ * Login with username and password.
129
+ */
130
+ async login(credentials) {
131
+ const response = await api.post("/auth/login", credentials);
132
+ return extractData(response);
133
+ },
134
+ /**
135
+ * Logout the current user.
136
+ * Returns SSO logout URL if applicable for SSO users.
137
+ */
138
+ async logout() {
139
+ try {
140
+ const response = await api.post("/auth/logout", {}, true);
141
+ return extractData(response);
142
+ } catch {
143
+ return { success: true };
144
+ }
145
+ },
146
+ /**
147
+ * Register a new user.
148
+ */
149
+ async register(data) {
150
+ const response = await api.post("/auth/register", data);
151
+ return extractData(response);
152
+ },
153
+ /**
154
+ * Request a password reset email.
155
+ */
156
+ async forgotPassword(email) {
157
+ await api.post("/auth/forgot-password", { email });
158
+ },
159
+ /**
160
+ * Reset password with a token.
161
+ */
162
+ async resetPassword(token, newPassword) {
163
+ await api.post("/auth/reset-password", { token, newPassword });
164
+ },
165
+ /**
166
+ * Refresh the access token.
167
+ */
168
+ async refreshToken(refreshToken) {
169
+ const response = await api.post("/auth/refresh", { refreshToken });
170
+ return extractData(response);
171
+ },
172
+ /**
173
+ * Initiate SSO login by redirecting to the SSO provider.
174
+ * @param options.callbackUrl - The URL where the SSO provider should redirect after auth
175
+ * @param options.redirectUrl - The final URL to redirect to after processing the callback
176
+ */
177
+ initiateSSOLogin(options) {
178
+ if (typeof window === "undefined") return;
179
+ const config2 = getConfig();
180
+ const authorizeUrl = config2.sso?.authorizeUrl ?? `${config2.baseUrl}/auth/sso/authorize`;
181
+ const params = new URLSearchParams();
182
+ if (options?.callbackUrl) {
183
+ params.set("callback", options.callbackUrl);
184
+ }
185
+ if (options?.redirectUrl) {
186
+ params.set("redirect", options.redirectUrl);
187
+ }
188
+ const url = params.toString() ? `${authorizeUrl}?${params}` : authorizeUrl;
189
+ window.location.href = url;
190
+ },
191
+ // ============================================================================
192
+ // Profile
193
+ // ============================================================================
194
+ /**
195
+ * Get the current user's profile.
196
+ */
197
+ async getProfile(customFetch) {
198
+ const response = await api.get("/auth/profile", true, customFetch);
199
+ return response;
200
+ },
201
+ /**
202
+ * Update the current user's profile.
203
+ */
204
+ async updateProfile(data) {
205
+ const response = await api.put("/auth/profile", data, true);
206
+ return extractData(response);
207
+ },
208
+ /**
209
+ * Change the current user's password.
210
+ */
211
+ async changePassword(data) {
212
+ await api.put("/auth/change-password", data, true);
213
+ },
214
+ // ============================================================================
215
+ // Email Verification
216
+ // ============================================================================
217
+ /**
218
+ * Resend email verification.
219
+ */
220
+ async resendVerification() {
221
+ await api.post("/auth/resend-verification", {}, true);
222
+ },
223
+ /**
224
+ * Verify email with a token.
225
+ */
226
+ async verifyEmail(token) {
227
+ const response = await api.post("/auth/verify-email", {
228
+ token
229
+ });
230
+ return extractData(response);
231
+ },
232
+ // ============================================================================
233
+ // Session Management
234
+ // ============================================================================
235
+ /**
236
+ * Get all active sessions.
237
+ */
238
+ async getSessions(customFetch) {
239
+ const response = await api.get(
240
+ "/auth/sessions",
241
+ true,
242
+ customFetch
243
+ );
244
+ return response;
245
+ },
246
+ /**
247
+ * Revoke a specific session.
248
+ */
249
+ async revokeSession(sessionId) {
250
+ await api.delete(`/auth/sessions/${sessionId}`, true);
251
+ },
252
+ /**
253
+ * Revoke all sessions except the current one.
254
+ */
255
+ async revokeAllSessions() {
256
+ await api.delete("/auth/sessions", true);
257
+ },
258
+ // ============================================================================
259
+ // API Key Management
260
+ // ============================================================================
261
+ /**
262
+ * Get all API keys.
263
+ */
264
+ async getApiKeys(customFetch) {
265
+ const response = await api.get("/auth/api-keys", true, customFetch);
266
+ return response;
267
+ },
268
+ /**
269
+ * Create a new API key.
270
+ */
271
+ async createApiKey(data) {
272
+ const response = await api.post("/auth/api-keys", data, true);
273
+ return extractData(response);
274
+ },
275
+ /**
276
+ * Revoke an API key.
277
+ */
278
+ async revokeApiKey(keyId) {
279
+ await api.delete(`/auth/api-keys/${keyId}`, true);
280
+ },
281
+ /**
282
+ * Update an API key's name.
283
+ */
284
+ async updateApiKey(keyId, data) {
285
+ await api.put(`/auth/api-keys/${keyId}`, data, true);
286
+ },
287
+ // ============================================================================
288
+ // MFA Management
289
+ // ============================================================================
290
+ /**
291
+ * Get MFA status for the current user.
292
+ */
293
+ async getMFAStatus() {
294
+ const response = await api.get("/auth/mfa/status", true);
295
+ return response;
296
+ },
297
+ /**
298
+ * Setup MFA (get QR code and backup codes).
299
+ */
300
+ async setupMFA() {
301
+ const response = await api.post("/auth/mfa-setup", {}, true);
302
+ return extractData(response);
303
+ },
304
+ /**
305
+ * Verify MFA setup with a code.
306
+ */
307
+ async verifyMFASetup(code) {
308
+ await api.post("/auth/mfa/verify", { code }, true);
309
+ },
310
+ /**
311
+ * Disable MFA.
312
+ */
313
+ async disableMFA(password) {
314
+ await api.delete("/auth/mfa", true, void 0, { password });
315
+ },
316
+ /**
317
+ * Regenerate MFA backup codes.
318
+ */
319
+ async regenerateBackupCodes(password) {
320
+ const response = await api.post(
321
+ "/auth/mfa/regenerate-backup-codes",
322
+ { password },
323
+ true
324
+ );
325
+ return extractData(response);
326
+ },
327
+ /**
328
+ * Verify MFA challenge during login.
329
+ */
330
+ async verifyMFAChallenge(data) {
331
+ const config2 = getConfig();
332
+ const fetchFn = config2.fetch ?? fetch;
333
+ const requestBody = {
334
+ code: data.code,
335
+ type: data.method === "backup" ? "backup" : "totp",
336
+ trustDevice: data.trustDevice
337
+ };
338
+ const response = await fetchFn(`${config2.baseUrl}/auth/mfa/challenge`, {
339
+ method: "POST",
340
+ headers: {
341
+ "Content-Type": "application/json",
342
+ Authorization: `Bearer ${data.mfaToken}`
343
+ },
344
+ body: JSON.stringify(requestBody)
345
+ });
346
+ if (!response.ok) {
347
+ const error = await response.json().catch(() => ({ message: "Verification failed" }));
348
+ throw new Error(error.error?.message || error.message || "Invalid verification code");
349
+ }
350
+ const result = await response.json();
351
+ return extractData(result);
352
+ },
353
+ // ============================================================================
354
+ // Device Management
355
+ // ============================================================================
356
+ /**
357
+ * Get all devices.
358
+ */
359
+ async getDevices(customFetch) {
360
+ const response = await api.get(
361
+ "/auth/devices",
362
+ true,
363
+ customFetch
364
+ );
365
+ return response;
366
+ },
367
+ /**
368
+ * Trust a device.
369
+ */
370
+ async trustDevice(deviceId) {
371
+ await api.put(`/auth/devices/${deviceId}/trust`, {}, true);
372
+ },
373
+ /**
374
+ * Revoke device trust.
375
+ */
376
+ async revokeDevice(deviceId) {
377
+ await api.delete(`/auth/devices/${deviceId}/revoke`, true);
378
+ },
379
+ /**
380
+ * Remove a device completely.
381
+ */
382
+ async removeDevice(deviceId) {
383
+ await api.delete(`/auth/devices/${deviceId}`, true);
384
+ },
385
+ /**
386
+ * Approve a device with a token.
387
+ */
388
+ async approveDevice(token) {
389
+ const response = await api.post("/auth/device/approve", {
390
+ token
391
+ });
392
+ return extractData(response);
393
+ },
394
+ /**
395
+ * Block a device with a token.
396
+ */
397
+ async blockDevice(token) {
398
+ const response = await api.post("/auth/device/block", {
399
+ token
400
+ });
401
+ return extractData(response);
402
+ },
403
+ // ============================================================================
404
+ // Preferences
405
+ // ============================================================================
406
+ /**
407
+ * Get user preferences.
408
+ */
409
+ async getPreferences(customFetch) {
410
+ const response = await api.get(
411
+ "/auth/preferences",
412
+ true,
413
+ customFetch
414
+ );
415
+ if (response && typeof response === "object" && "preferences" in response) {
416
+ return response.preferences;
417
+ }
418
+ return response ?? {};
419
+ },
420
+ /**
421
+ * Update user preferences.
422
+ */
423
+ async updatePreferences(data) {
424
+ await api.put("/auth/preferences", data, true);
425
+ },
426
+ // ============================================================================
427
+ // SSO / Linked Accounts
428
+ // ============================================================================
429
+ /**
430
+ * Get SSO linked accounts.
431
+ */
432
+ async getSSOAccounts(customFetch) {
433
+ const response = await api.get(
434
+ "/auth/sso/accounts",
435
+ true,
436
+ customFetch
437
+ );
438
+ if (response && typeof response === "object" && "accounts" in response) {
439
+ return response.accounts;
440
+ }
441
+ return Array.isArray(response) ? response : [];
442
+ },
443
+ /**
444
+ * Unlink an SSO account.
445
+ */
446
+ async unlinkSSOAccount(provider, password) {
447
+ await api.delete("/auth/sso/unlink", true, void 0, { provider, password });
448
+ },
449
+ /**
450
+ * Link an SSO account (redirects to SSO provider).
451
+ */
452
+ async linkSSOAccount(provider = "authentik") {
453
+ if (typeof window === "undefined") return;
454
+ const accessToken = getAccessToken();
455
+ if (!accessToken) {
456
+ throw new Error("Not authenticated");
457
+ }
458
+ const config2 = getConfig();
459
+ const params = new URLSearchParams({
460
+ token: accessToken,
461
+ provider
462
+ });
463
+ window.location.href = `${config2.baseUrl}/auth/sso/link?${params.toString()}`;
464
+ },
465
+ // ============================================================================
466
+ // Security Events
467
+ // ============================================================================
468
+ /**
469
+ * Get security event history.
470
+ */
471
+ async getSecurityEvents(params, customFetch) {
472
+ const queryParams = new URLSearchParams();
473
+ if (params?.page) queryParams.append("page", params.page.toString());
474
+ if (params?.limit) queryParams.append("limit", params.limit.toString());
475
+ if (params?.type) queryParams.append("type", params.type);
476
+ const response = await api.get(`/auth/security/events?${queryParams.toString()}`, true, customFetch);
477
+ return extractData(response);
478
+ }
479
+ };
480
+ }
481
+ });
482
+
483
+ // src/svelte/stores/auth.svelte.ts
484
+ var auth_svelte_exports = {};
485
+ __export(auth_svelte_exports, {
486
+ authActions: () => authActions,
487
+ authStore: () => authStore,
488
+ currentUser: () => currentUser,
489
+ isAuthenticated: () => isAuthenticated
490
+ });
491
+ function getStorageKey() {
492
+ return isInitialized() ? getConfig().storageKey ?? "classic_auth" : "classic_auth";
493
+ }
494
+ function getStorageAdapter() {
495
+ return isInitialized() ? getStorage() : getDefaultStorage();
496
+ }
497
+ function loadAuthFromStorage() {
498
+ try {
499
+ const storage = getStorageAdapter();
500
+ const data = storage.getItem(getStorageKey());
501
+ if (!data) {
502
+ return {
503
+ accessToken: null,
504
+ refreshToken: null,
505
+ user: null,
506
+ isAuthenticated: false
507
+ };
508
+ }
509
+ const parsed = JSON.parse(data);
510
+ return {
511
+ accessToken: parsed.accessToken ?? null,
512
+ refreshToken: parsed.refreshToken ?? null,
513
+ user: parsed.user ?? null,
514
+ isAuthenticated: !!parsed.accessToken && !!parsed.user
515
+ };
516
+ } catch {
517
+ return {
518
+ accessToken: null,
519
+ refreshToken: null,
520
+ user: null,
521
+ isAuthenticated: false
522
+ };
523
+ }
524
+ }
525
+ function saveAuthToStorage(state) {
526
+ try {
527
+ const storage = getStorageAdapter();
528
+ const key = getStorageKey();
529
+ if (!state.accessToken) {
530
+ storage.removeItem(key);
531
+ } else {
532
+ storage.setItem(key, JSON.stringify(state));
533
+ }
534
+ } catch {
535
+ }
536
+ }
537
+ var AuthStore, authStore, authActions, isAuthenticated, currentUser;
538
+ var init_auth_svelte = __esm({
539
+ "src/svelte/stores/auth.svelte.ts"() {
540
+ init_config();
541
+ init_jwt();
542
+ init_api();
543
+ AuthStore = class {
544
+ constructor() {
545
+ this.subscribers = /* @__PURE__ */ new Set();
546
+ this.state = loadAuthFromStorage();
547
+ }
548
+ /**
549
+ * Subscribe to state changes (Svelte store contract).
550
+ */
551
+ subscribe(subscriber) {
552
+ this.subscribers.add(subscriber);
553
+ subscriber(this.state);
554
+ return () => this.subscribers.delete(subscriber);
555
+ }
556
+ notify() {
557
+ this.subscribers.forEach((sub) => sub(this.state));
558
+ }
559
+ // Getters for direct access
560
+ get accessToken() {
561
+ return this.state.accessToken;
562
+ }
563
+ get refreshToken() {
564
+ return this.state.refreshToken;
565
+ }
566
+ get user() {
567
+ return this.state.user;
568
+ }
569
+ get isAuthenticated() {
570
+ return this.state.isAuthenticated;
571
+ }
572
+ /**
573
+ * Get the current auth state snapshot.
574
+ */
575
+ getState() {
576
+ return { ...this.state };
577
+ }
578
+ /**
579
+ * Set auth data after successful login.
580
+ */
581
+ setAuth(accessToken, refreshToken, user, sessionToken) {
582
+ const jwtPayload = decodeJWT(accessToken);
583
+ const userWithPermissions = {
584
+ ...user,
585
+ permissions: user.permissions || jwtPayload?.permissions || [],
586
+ roles: user.roles || jwtPayload?.roles || (user.role ? [user.role] : [])
587
+ };
588
+ this.state = {
589
+ accessToken,
590
+ refreshToken,
591
+ user: userWithPermissions,
592
+ isAuthenticated: true
593
+ };
594
+ saveAuthToStorage(this.state);
595
+ if (sessionToken && typeof window !== "undefined") {
596
+ getStorageAdapter().setItem("sessionToken", sessionToken);
597
+ }
598
+ if (typeof window !== "undefined") {
599
+ window.dispatchEvent(
600
+ new CustomEvent("auth:login", {
601
+ detail: { user: userWithPermissions }
602
+ })
603
+ );
604
+ }
605
+ this.notify();
606
+ }
607
+ /**
608
+ * Update tokens (e.g., after refresh).
609
+ */
610
+ updateTokens(accessToken, refreshToken) {
611
+ const jwtPayload = decodeJWT(accessToken);
612
+ const updatedUser = this.state.user ? {
613
+ ...this.state.user,
614
+ permissions: jwtPayload?.permissions || this.state.user.permissions || [],
615
+ roles: jwtPayload?.roles || this.state.user.roles || (this.state.user.role ? [this.state.user.role] : [])
616
+ } : null;
617
+ this.state = {
618
+ ...this.state,
619
+ accessToken,
620
+ refreshToken,
621
+ user: updatedUser
622
+ };
623
+ saveAuthToStorage(this.state);
624
+ if (typeof window !== "undefined") {
625
+ window.dispatchEvent(new CustomEvent("auth:token-refresh"));
626
+ }
627
+ this.notify();
628
+ }
629
+ /**
630
+ * Update user data (e.g., after profile update).
631
+ */
632
+ updateUser(user) {
633
+ const jwtPayload = this.state.accessToken ? decodeJWT(this.state.accessToken) : null;
634
+ const updatedUser = {
635
+ ...user,
636
+ permissions: user.permissions || jwtPayload?.permissions || [],
637
+ roles: user.roles || jwtPayload?.roles || (user.role ? [user.role] : [])
638
+ };
639
+ this.state = {
640
+ ...this.state,
641
+ user: updatedUser
642
+ };
643
+ saveAuthToStorage(this.state);
644
+ if (typeof window !== "undefined") {
645
+ window.dispatchEvent(
646
+ new CustomEvent("auth:profile-update", {
647
+ detail: { user: updatedUser }
648
+ })
649
+ );
650
+ }
651
+ this.notify();
652
+ }
653
+ /**
654
+ * Clear auth state (logout).
655
+ */
656
+ logout() {
657
+ this.state = {
658
+ accessToken: null,
659
+ refreshToken: null,
660
+ user: null,
661
+ isAuthenticated: false
662
+ };
663
+ const storage = getStorageAdapter();
664
+ storage.removeItem(getStorageKey());
665
+ storage.removeItem("sessionToken");
666
+ if (typeof window !== "undefined") {
667
+ window.dispatchEvent(new CustomEvent("auth:logout"));
668
+ }
669
+ if (isInitialized()) {
670
+ getConfig().onLogout?.();
671
+ }
672
+ this.notify();
673
+ }
674
+ /**
675
+ * Logout with SSO support.
676
+ * Calls the logout API and returns the SSO logout URL if applicable.
677
+ * Clears local state regardless of API response.
678
+ */
679
+ async logoutWithSSO() {
680
+ try {
681
+ const response = await authApi.logout();
682
+ this.logout();
683
+ if (response?.ssoLogout && response?.logoutUrl) {
684
+ return { ssoLogoutUrl: response.logoutUrl };
685
+ }
686
+ } catch {
687
+ this.logout();
688
+ }
689
+ return {};
690
+ }
691
+ /**
692
+ * Check if user has a specific permission.
693
+ */
694
+ hasPermission(permission) {
695
+ return this.state.user?.permissions?.includes(permission) ?? false;
696
+ }
697
+ /**
698
+ * Check if user has a specific role.
699
+ */
700
+ hasRole(role) {
701
+ return this.state.user?.roles?.includes(role) ?? false;
702
+ }
703
+ /**
704
+ * Check if user has any of the specified roles.
705
+ */
706
+ hasAnyRole(roles) {
707
+ return roles.some((role) => this.state.user?.roles?.includes(role)) ?? false;
708
+ }
709
+ /**
710
+ * Check if user has all of the specified roles.
711
+ */
712
+ hasAllRoles(roles) {
713
+ return roles.every((role) => this.state.user?.roles?.includes(role)) ?? false;
714
+ }
715
+ /**
716
+ * Check if user has any of the specified permissions.
717
+ */
718
+ hasAnyPermission(permissions) {
719
+ return permissions.some((perm) => this.state.user?.permissions?.includes(perm)) ?? false;
720
+ }
721
+ /**
722
+ * Check if user has all of the specified permissions.
723
+ */
724
+ hasAllPermissions(permissions) {
725
+ return permissions.every((perm) => this.state.user?.permissions?.includes(perm)) ?? false;
726
+ }
727
+ /**
728
+ * Re-hydrate state from storage (useful after config changes).
729
+ */
730
+ rehydrate() {
731
+ this.state = loadAuthFromStorage();
732
+ this.notify();
733
+ }
734
+ };
735
+ authStore = new AuthStore();
736
+ authActions = {
737
+ setAuth: (accessToken, refreshToken, user, sessionToken) => authStore.setAuth(accessToken, refreshToken, user, sessionToken),
738
+ updateTokens: (accessToken, refreshToken) => authStore.updateTokens(accessToken, refreshToken),
739
+ updateUser: (user) => authStore.updateUser(user),
740
+ logout: () => authStore.logout(),
741
+ logoutWithSSO: () => authStore.logoutWithSSO(),
742
+ hasPermission: (permission) => authStore.hasPermission(permission),
743
+ hasRole: (role) => authStore.hasRole(role),
744
+ hasAnyRole: (roles) => authStore.hasAnyRole(roles),
745
+ hasAllRoles: (roles) => authStore.hasAllRoles(roles),
746
+ hasAnyPermission: (permissions) => authStore.hasAnyPermission(permissions),
747
+ hasAllPermissions: (permissions) => authStore.hasAllPermissions(permissions),
748
+ rehydrate: () => authStore.rehydrate()
749
+ };
750
+ isAuthenticated = {
751
+ subscribe: (subscriber) => {
752
+ return authStore.subscribe((state) => subscriber(state.isAuthenticated));
753
+ }
754
+ };
755
+ currentUser = {
756
+ subscribe: (subscriber) => {
757
+ return authStore.subscribe((state) => subscriber(state.user));
758
+ }
759
+ };
760
+ }
761
+ });
47
762
 
48
763
  // src/core/client.ts
49
- var isRefreshing = false;
50
- var refreshSubscribers = [];
51
764
  function subscribeTokenRefresh(cb) {
52
765
  refreshSubscribers.push(cb);
53
766
  }
@@ -98,6 +811,10 @@ function updateStoredTokens(accessToken, refreshToken) {
98
811
  parsed.accessToken = accessToken;
99
812
  parsed.refreshToken = refreshToken;
100
813
  storage.setItem(storageKey, JSON.stringify(parsed));
814
+ Promise.resolve().then(() => (init_auth_svelte(), auth_svelte_exports)).then(({ authStore: authStore2 }) => {
815
+ authStore2.updateTokens(accessToken, refreshToken);
816
+ }).catch(() => {
817
+ });
101
818
  config2.onTokenRefresh?.({ accessToken, refreshToken });
102
819
  }
103
820
  function clearStoredAuth() {
@@ -209,376 +926,63 @@ async function apiRequest(endpoint, options = {}) {
209
926
  throw new Error("API request failed");
210
927
  }
211
928
  }
212
- var api = {
213
- get: (endpoint, authenticated = false, customFetch) => apiRequest(endpoint, { method: "GET", authenticated, customFetch }),
214
- post: (endpoint, body, authenticated = false, customFetch) => apiRequest(endpoint, {
215
- method: "POST",
216
- body,
217
- authenticated,
218
- customFetch
219
- }),
220
- put: (endpoint, body, authenticated = false, customFetch) => apiRequest(endpoint, {
221
- method: "PUT",
222
- body,
223
- authenticated,
224
- customFetch
225
- }),
226
- patch: (endpoint, body, authenticated = false, customFetch) => apiRequest(endpoint, {
227
- method: "PATCH",
228
- body,
229
- authenticated,
230
- customFetch
231
- }),
232
- delete: (endpoint, authenticated = false, customFetch, body) => apiRequest(endpoint, {
233
- method: "DELETE",
234
- body,
235
- authenticated,
236
- customFetch
237
- })
238
- };
239
-
240
- // src/core/api.ts
241
- var authApi = {
242
- // ============================================================================
243
- // Authentication
244
- // ============================================================================
245
- /**
246
- * Login with username and password.
247
- */
248
- async login(credentials) {
249
- const response = await api.post("/auth/login", credentials);
250
- return extractData(response);
251
- },
252
- /**
253
- * Logout the current user.
254
- */
255
- async logout() {
256
- await api.post("/auth/logout", {}, true);
257
- },
258
- /**
259
- * Register a new user.
260
- */
261
- async register(data) {
262
- const response = await api.post("/auth/register", data);
263
- return extractData(response);
264
- },
265
- /**
266
- * Request a password reset email.
267
- */
268
- async forgotPassword(email) {
269
- await api.post("/auth/forgot-password", { email });
270
- },
271
- /**
272
- * Reset password with a token.
273
- */
274
- async resetPassword(token, newPassword) {
275
- await api.post("/auth/reset-password", { token, newPassword });
276
- },
277
- /**
278
- * Refresh the access token.
279
- */
280
- async refreshToken(refreshToken) {
281
- const response = await api.post("/auth/refresh", { refreshToken });
282
- return extractData(response);
283
- },
284
- /**
285
- * Initiate SSO login by redirecting to the SSO provider.
286
- */
287
- initiateSSOLogin() {
288
- if (typeof window === "undefined") return;
289
- const config2 = getConfig();
290
- const authorizeUrl = config2.sso?.authorizeUrl ?? `${config2.baseUrl}/auth/sso/authorize`;
291
- window.location.href = authorizeUrl;
292
- },
293
- // ============================================================================
294
- // Profile
295
- // ============================================================================
296
- /**
297
- * Get the current user's profile.
298
- */
299
- async getProfile(customFetch) {
300
- const response = await api.get("/auth/profile", true, customFetch);
301
- return response;
302
- },
303
- /**
304
- * Update the current user's profile.
305
- */
306
- async updateProfile(data) {
307
- const response = await api.put("/auth/profile", data, true);
308
- return extractData(response);
309
- },
310
- /**
311
- * Change the current user's password.
312
- */
313
- async changePassword(data) {
314
- await api.put("/auth/change-password", data, true);
315
- },
316
- // ============================================================================
317
- // Email Verification
318
- // ============================================================================
319
- /**
320
- * Resend email verification.
321
- */
322
- async resendVerification() {
323
- await api.post("/auth/resend-verification", {}, true);
324
- },
325
- /**
326
- * Verify email with a token.
327
- */
328
- async verifyEmail(token) {
329
- const response = await api.post("/auth/verify-email", {
330
- token
331
- });
332
- return extractData(response);
333
- },
334
- // ============================================================================
335
- // Session Management
336
- // ============================================================================
337
- /**
338
- * Get all active sessions.
339
- */
340
- async getSessions(customFetch) {
341
- const response = await api.get(
342
- "/auth/sessions",
343
- true,
344
- customFetch
345
- );
346
- return response;
347
- },
348
- /**
349
- * Revoke a specific session.
350
- */
351
- async revokeSession(sessionId) {
352
- await api.delete(`/auth/sessions/${sessionId}`, true);
353
- },
354
- /**
355
- * Revoke all sessions except the current one.
356
- */
357
- async revokeAllSessions() {
358
- await api.delete("/auth/sessions", true);
359
- },
360
- // ============================================================================
361
- // API Key Management
362
- // ============================================================================
363
- /**
364
- * Get all API keys.
365
- */
366
- async getApiKeys(customFetch) {
367
- const response = await api.get("/auth/api-keys", true, customFetch);
368
- return response;
369
- },
370
- /**
371
- * Create a new API key.
372
- */
373
- async createApiKey(data) {
374
- const response = await api.post("/auth/api-keys", data, true);
375
- return extractData(response);
376
- },
377
- /**
378
- * Revoke an API key.
379
- */
380
- async revokeApiKey(keyId) {
381
- await api.delete(`/auth/api-keys/${keyId}`, true);
382
- },
383
- /**
384
- * Update an API key's name.
385
- */
386
- async updateApiKey(keyId, data) {
387
- await api.put(`/auth/api-keys/${keyId}`, data, true);
388
- },
389
- // ============================================================================
390
- // MFA Management
391
- // ============================================================================
392
- /**
393
- * Get MFA status for the current user.
394
- */
395
- async getMFAStatus() {
396
- const response = await api.get("/auth/mfa/status", true);
397
- return response;
398
- },
399
- /**
400
- * Setup MFA (get QR code and backup codes).
401
- */
402
- async setupMFA() {
403
- const response = await api.post("/auth/mfa-setup", {}, true);
404
- return extractData(response);
405
- },
406
- /**
407
- * Verify MFA setup with a code.
408
- */
409
- async verifyMFASetup(code) {
410
- await api.post("/auth/mfa/verify", { code }, true);
411
- },
412
- /**
413
- * Disable MFA.
414
- */
415
- async disableMFA(password) {
416
- await api.delete("/auth/mfa", true, void 0, { password });
417
- },
418
- /**
419
- * Regenerate MFA backup codes.
420
- */
421
- async regenerateBackupCodes(password) {
422
- const response = await api.post(
423
- "/auth/mfa/regenerate-backup-codes",
424
- { password },
425
- true
426
- );
427
- return extractData(response);
428
- },
429
- /**
430
- * Verify MFA challenge during login.
431
- */
432
- async verifyMFAChallenge(data) {
433
- const config2 = getConfig();
434
- const fetchFn = config2.fetch ?? fetch;
435
- const requestBody = {
436
- code: data.code,
437
- type: data.method === "backup" ? "backup" : "totp",
438
- trustDevice: data.trustDevice
929
+ var isRefreshing, refreshSubscribers, api;
930
+ var init_client = __esm({
931
+ "src/core/client.ts"() {
932
+ init_config();
933
+ isRefreshing = false;
934
+ refreshSubscribers = [];
935
+ api = {
936
+ get: (endpoint, authenticated = false, customFetch) => apiRequest(endpoint, { method: "GET", authenticated, customFetch }),
937
+ post: (endpoint, body, authenticated = false, customFetch) => apiRequest(endpoint, {
938
+ method: "POST",
939
+ body,
940
+ authenticated,
941
+ customFetch
942
+ }),
943
+ put: (endpoint, body, authenticated = false, customFetch) => apiRequest(endpoint, {
944
+ method: "PUT",
945
+ body,
946
+ authenticated,
947
+ customFetch
948
+ }),
949
+ patch: (endpoint, body, authenticated = false, customFetch) => apiRequest(endpoint, {
950
+ method: "PATCH",
951
+ body,
952
+ authenticated,
953
+ customFetch
954
+ }),
955
+ delete: (endpoint, authenticated = false, customFetch, body) => apiRequest(endpoint, {
956
+ method: "DELETE",
957
+ body,
958
+ authenticated,
959
+ customFetch
960
+ })
439
961
  };
440
- const response = await fetchFn(`${config2.baseUrl}/auth/mfa/challenge`, {
441
- method: "POST",
442
- headers: {
443
- "Content-Type": "application/json",
444
- Authorization: `Bearer ${data.mfaToken}`
445
- },
446
- body: JSON.stringify(requestBody)
447
- });
448
- if (!response.ok) {
449
- const error = await response.json().catch(() => ({ message: "Verification failed" }));
450
- throw new Error(error.error?.message || error.message || "Invalid verification code");
451
- }
452
- const result = await response.json();
453
- return extractData(result);
454
- },
455
- // ============================================================================
456
- // Device Management
457
- // ============================================================================
458
- /**
459
- * Get all devices.
460
- */
461
- async getDevices(customFetch) {
462
- const response = await api.get(
463
- "/auth/devices",
464
- true,
465
- customFetch
466
- );
467
- return response;
468
- },
469
- /**
470
- * Trust a device.
471
- */
472
- async trustDevice(deviceId) {
473
- await api.put(`/auth/devices/${deviceId}/trust`, {}, true);
474
- },
475
- /**
476
- * Revoke device trust.
477
- */
478
- async revokeDevice(deviceId) {
479
- await api.delete(`/auth/devices/${deviceId}/revoke`, true);
480
- },
481
- /**
482
- * Remove a device completely.
483
- */
484
- async removeDevice(deviceId) {
485
- await api.delete(`/auth/devices/${deviceId}`, true);
486
- },
487
- /**
488
- * Approve a device with a token.
489
- */
490
- async approveDevice(token) {
491
- const response = await api.post("/auth/device/approve", {
492
- token
493
- });
494
- return extractData(response);
495
- },
496
- /**
497
- * Block a device with a token.
498
- */
499
- async blockDevice(token) {
500
- const response = await api.post("/auth/device/block", {
501
- token
502
- });
503
- return extractData(response);
504
- },
505
- // ============================================================================
506
- // Preferences
507
- // ============================================================================
508
- /**
509
- * Get user preferences.
510
- */
511
- async getPreferences(customFetch) {
512
- const response = await api.get(
513
- "/auth/preferences",
514
- true,
515
- customFetch
516
- );
517
- if (response && typeof response === "object" && "preferences" in response) {
518
- return response.preferences;
519
- }
520
- return response ?? {};
521
- },
522
- /**
523
- * Update user preferences.
524
- */
525
- async updatePreferences(data) {
526
- await api.put("/auth/preferences", data, true);
527
- },
528
- // ============================================================================
529
- // SSO / Linked Accounts
530
- // ============================================================================
531
- /**
532
- * Get SSO linked accounts.
533
- */
534
- async getSSOAccounts(customFetch) {
535
- const response = await api.get(
536
- "/auth/sso/accounts",
537
- true,
538
- customFetch
539
- );
540
- if (response && typeof response === "object" && "accounts" in response) {
541
- return response.accounts;
542
- }
543
- return Array.isArray(response) ? response : [];
544
- },
545
- /**
546
- * Unlink an SSO account.
547
- */
548
- async unlinkSSOAccount(provider, password) {
549
- await api.delete("/auth/sso/unlink", true, void 0, { provider, password });
550
- },
551
- /**
552
- * Link an SSO account (redirects to SSO provider).
553
- */
554
- async linkSSOAccount(provider = "authentik") {
555
- if (typeof window === "undefined") return;
556
- const accessToken = getAccessToken();
557
- if (!accessToken) {
558
- throw new Error("Not authenticated");
559
- }
560
- const config2 = getConfig();
561
- const params = new URLSearchParams({
562
- token: accessToken,
563
- provider
564
- });
565
- window.location.href = `${config2.baseUrl}/auth/sso/link?${params.toString()}`;
566
- },
567
- // ============================================================================
568
- // Security Events
569
- // ============================================================================
570
- /**
571
- * Get security event history.
572
- */
573
- async getSecurityEvents(params, customFetch) {
574
- const queryParams = new URLSearchParams();
575
- if (params?.page) queryParams.append("page", params.page.toString());
576
- if (params?.limit) queryParams.append("limit", params.limit.toString());
577
- if (params?.type) queryParams.append("type", params.type);
578
- const response = await api.get(`/auth/security/events?${queryParams.toString()}`, true, customFetch);
579
- return extractData(response);
580
962
  }
581
- };
963
+ });
964
+
965
+ // src/core/index.ts
966
+ init_config();
967
+ init_client();
968
+ init_api();
969
+
970
+ // src/core/service.ts
971
+ init_api();
972
+
973
+ // src/core/guards.ts
974
+ function isMfaChallengeResponse(response) {
975
+ return "requiresMFA" in response && response.requiresMFA === true || "mfaRequired" in response && response.mfaRequired === true;
976
+ }
977
+ function isLoginSuccessResponse(response) {
978
+ return "accessToken" in response && "user" in response && !isMfaChallengeResponse(response);
979
+ }
980
+ function getMfaToken(response) {
981
+ return response.mfaToken ?? response.mfaChallengeToken;
982
+ }
983
+ function getAvailableMethods(response) {
984
+ return response.availableMethods ?? ["totp"];
985
+ }
582
986
 
583
987
  // src/core/service.ts
584
988
  var AuthService = class {
@@ -587,15 +991,32 @@ var AuthService = class {
587
991
  // ============================================================================
588
992
  /**
589
993
  * Login with username and password.
590
- */
591
- async login(credentials) {
592
- return await authApi.login(credentials);
994
+ * By default, automatically sets the auth state on successful login (unless MFA is required).
995
+ * @param credentials - Username and password
996
+ * @param options - Optional settings for login behavior
997
+ */
998
+ async login(credentials, options) {
999
+ const response = await authApi.login(credentials);
1000
+ if (options?.autoSetAuth !== false && !isMfaChallengeResponse(response)) {
1001
+ try {
1002
+ const { authStore: authStore2 } = await Promise.resolve().then(() => (init_auth_svelte(), auth_svelte_exports));
1003
+ authStore2.setAuth(
1004
+ response.accessToken,
1005
+ response.refreshToken,
1006
+ response.user,
1007
+ response.sessionToken
1008
+ );
1009
+ } catch {
1010
+ }
1011
+ }
1012
+ return response;
593
1013
  }
594
1014
  /**
595
1015
  * Logout the current user.
1016
+ * Returns SSO logout URL if applicable for SSO users.
596
1017
  */
597
1018
  async logout() {
598
- await authApi.logout();
1019
+ return await authApi.logout();
599
1020
  }
600
1021
  /**
601
1022
  * Register a new user.
@@ -629,9 +1050,11 @@ var AuthService = class {
629
1050
  }
630
1051
  /**
631
1052
  * Initiate SSO login (redirects to SSO provider).
1053
+ * @param options.callbackUrl - The URL where the SSO provider should redirect after auth
1054
+ * @param options.redirectUrl - The final URL to redirect to after processing the callback
632
1055
  */
633
- initiateSSOLogin() {
634
- authApi.initiateSSOLogin();
1056
+ initiateSSOLogin(options) {
1057
+ authApi.initiateSSOLogin(options);
635
1058
  }
636
1059
  // ============================================================================
637
1060
  // Profile
@@ -743,9 +1166,25 @@ var AuthService = class {
743
1166
  }
744
1167
  /**
745
1168
  * Verify MFA challenge during login.
746
- */
747
- async verifyMFAChallenge(data) {
748
- return await authApi.verifyMFAChallenge(data);
1169
+ * By default, automatically sets the auth state on successful verification.
1170
+ * @param data - MFA challenge data including token and code
1171
+ * @param options - Optional settings for verification behavior
1172
+ */
1173
+ async verifyMFAChallenge(data, options) {
1174
+ const response = await authApi.verifyMFAChallenge(data);
1175
+ if (options?.autoSetAuth !== false) {
1176
+ try {
1177
+ const { authStore: authStore2 } = await Promise.resolve().then(() => (init_auth_svelte(), auth_svelte_exports));
1178
+ authStore2.setAuth(
1179
+ response.accessToken,
1180
+ response.refreshToken,
1181
+ response.user,
1182
+ response.sessionToken
1183
+ );
1184
+ } catch {
1185
+ }
1186
+ }
1187
+ return response;
749
1188
  }
750
1189
  // ============================================================================
751
1190
  // Device Management
@@ -834,54 +1273,7 @@ var AuthService = class {
834
1273
  };
835
1274
  var authService = new AuthService();
836
1275
 
837
- // src/core/jwt.ts
838
- function decodeJWT(token) {
839
- try {
840
- const parts = token.split(".");
841
- if (parts.length !== 3) {
842
- return null;
843
- }
844
- const payload = parts[1];
845
- const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
846
- return JSON.parse(decoded);
847
- } catch {
848
- return null;
849
- }
850
- }
851
- function isTokenExpired(token) {
852
- const payload = decodeJWT(token);
853
- if (!payload || !payload.exp) {
854
- return true;
855
- }
856
- return payload.exp * 1e3 < Date.now();
857
- }
858
- function getTokenRemainingTime(token) {
859
- const payload = decodeJWT(token);
860
- if (!payload || !payload.exp) {
861
- return 0;
862
- }
863
- const remainingMs = payload.exp * 1e3 - Date.now();
864
- return Math.max(0, remainingMs);
865
- }
866
- function getTokenExpiration(token) {
867
- const payload = decodeJWT(token);
868
- if (!payload || !payload.exp) {
869
- return null;
870
- }
871
- return new Date(payload.exp * 1e3);
872
- }
873
- function extractClaims(token, claims) {
874
- const payload = decodeJWT(token);
875
- if (!payload) {
876
- return null;
877
- }
878
- const result = {};
879
- for (const claim of claims) {
880
- if (claim in payload) {
881
- result[claim] = payload[claim];
882
- }
883
- }
884
- return result;
885
- }
1276
+ // src/core/index.ts
1277
+ init_jwt();
886
1278
 
887
- export { AuthService, api, apiRequest, authApi, authService, clearStoredAuth, decodeJWT, extractClaims, extractData, getAccessToken, getConfig, getDefaultStorage, getFetch, getRefreshToken, getSessionToken, getStorage, getTokenExpiration, getTokenRemainingTime, initAuth, isInitialized, isTokenExpired, resetConfig, updateStoredTokens };
1279
+ export { AuthService, api, apiRequest, authApi, authService, clearStoredAuth, decodeJWT, extractClaims, extractData, getAccessToken, getAvailableMethods, getConfig, getDefaultStorage, getFetch, getMfaToken, getRefreshToken, getSessionToken, getStorage, getTokenExpiration, getTokenRemainingTime, initAuth, isInitialized, isLoginSuccessResponse, isMfaChallengeResponse, isTokenExpired, resetConfig, updateStoredTokens };