@amaster.ai/auth-client 1.1.0-beta.3 → 1.1.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,7 @@ Authentication SDK for Amaster Platform - Complete client-side authentication so
8
8
  - 🔄 **Automatic Token Refresh**: JWT token auto-refresh before expiration
9
9
  - 📦 **Token Storage**: localStorage or sessionStorage with SSR support
10
10
  - 🎯 **Permission System**: Role-based and permission-based access control
11
+ - 👤 **Anonymous Access**: Automatic support for anonymous users with configurable permissions
11
12
  - 🔗 **OAuth Integration**: Google, GitHub, WeChat OAuth, WeChat Mini Program, and custom OAuth providers
12
13
  - 📡 **Event System**: Listen to login, logout, and token events
13
14
  - 💾 **Session Management**: View and revoke active sessions
@@ -82,10 +83,10 @@ const authClient = createAuthClient();
82
83
 
83
84
  ```typescript
84
85
  interface AuthClientOptions {
85
- baseURL?: string; // API base URL, defaults to window.location.origin
86
+ baseURL?: string; // API base URL, defaults to window.location.origin
86
87
  headers?: Record<string, string>; // Custom headers for all requests
87
- onTokenExpired?: () => void; // Token expiration callback
88
- onUnauthorized?: () => void; // Unauthorized (401) callback
88
+ onTokenExpired?: () => void; // Token expiration callback
89
+ onUnauthorized?: () => void; // Unauthorized (401) callback
89
90
  }
90
91
  ```
91
92
 
@@ -94,19 +95,18 @@ interface AuthClientOptions {
94
95
  ```typescript
95
96
  const authClient = createAuthClient({
96
97
  baseURL: "https://api.example.com",
97
- onTokenExpired: () => window.location.href = "/login",
98
+ onTokenExpired: () => (window.location.href = "/login"),
98
99
  onUnauthorized: () => alert("Session expired"),
99
100
  });
100
101
  ```
101
102
 
102
103
  **Built-in Features (Zero Config):**
103
104
 
104
- - ✅ **Auto Environment Detection**: Works in browser, WeChat Mini Program, SSR
105
- - ✅ **Auto Token Refresh**: Refreshes 5 minutes before expiry
106
- - ✅ **Auto Permission Sync**: Syncs on page load and every token refresh
107
- - ✅ **Smart Storage**: localStorage (browser), wx.storage (WeChat), no-op (SSR)
108
- - ✅ **Lightweight Data**: Only permission names cached, not full objects
109
- - ✅ **On-Demand DataScope**: Loads only when needed
105
+ - ✅ Auto environment detection (browser, WeChat Mini Program, SSR)
106
+ - ✅ Auto token refresh (5 minutes before expiry)
107
+ - ✅ Auto permission sync
108
+ - ✅ Auto anonymous support
109
+ - ✅ Smart storage (localStorage, wx.storage, no-op for SSR)
110
110
 
111
111
  ## Authentication
112
112
 
@@ -184,11 +184,7 @@ window.location.href = redirectUrl;
184
184
 
185
185
  ```typescript
186
186
  // Main page
187
- const popup = window.open(
188
- "/api/auth/oauth/google",
189
- "oauth-login",
190
- "width=600,height=700"
191
- );
187
+ const popup = window.open("/api/auth/oauth/google", "oauth-login", "width=600,height=700");
192
188
 
193
189
  window.addEventListener("message", (event) => {
194
190
  if (event.data.type === "oauth-success") {
@@ -213,7 +209,7 @@ import { createAuthClient } from "@amaster.ai/auth-client";
213
209
 
214
210
  // Zero configuration - auto-detects WeChat environment
215
211
  const authClient = createAuthClient({
216
- baseURL: "https://api.yourdomain.com"
212
+ baseURL: "https://api.yourdomain.com",
217
213
  });
218
214
 
219
215
  // Login with WeChat code
@@ -221,22 +217,22 @@ wx.login({
221
217
  success: async (res) => {
222
218
  if (res.code) {
223
219
  const result = await authClient.loginWithMiniProgram(res.code);
224
-
220
+
225
221
  if (result.data) {
226
222
  console.log("Logged in:", result.data.user);
227
223
  // Token automatically saved to wx.storage
228
- wx.switchTab({ url: '/pages/index/index' });
224
+ wx.switchTab({ url: "/pages/index/index" });
229
225
  } else if (result.error) {
230
226
  wx.showToast({
231
- title: result.error.message || 'Login failed',
232
- icon: 'none'
227
+ title: result.error.message || "Login failed",
228
+ icon: "none",
233
229
  });
234
230
  }
235
231
  }
236
232
  },
237
233
  fail: (err) => {
238
234
  console.error("wx.login failed:", err);
239
- }
235
+ },
240
236
  });
241
237
  ```
242
238
 
@@ -244,8 +240,8 @@ wx.login({
244
240
 
245
241
  ```xml
246
242
  <!-- WXML -->
247
- <button
248
- open-type="getPhoneNumber"
243
+ <button
244
+ open-type="getPhoneNumber"
249
245
  bindgetphonenumber="onGetPhoneNumber"
250
246
  >
251
247
  Get Phone Number
@@ -256,14 +252,14 @@ wx.login({
256
252
  // JS
257
253
  async onGetPhoneNumber(e) {
258
254
  const { code } = e.detail;
259
-
255
+
260
256
  if (code) {
261
257
  const result = await authClient.getMiniProgramPhoneNumber(code);
262
-
258
+
263
259
  if (result.data) {
264
260
  console.log("Phone number:", result.data.phone);
265
261
  console.log("Verified:", result.data.phoneVerified);
266
-
262
+
267
263
  // Update UI with phone number
268
264
  this.setData({
269
265
  phone: result.data.phone
@@ -288,13 +284,13 @@ async onGetPhoneNumber(e) {
288
284
  import { createAuthClient } from "@amaster.ai/auth-client";
289
285
 
290
286
  const authClient = createAuthClient({
291
- baseURL: "https://api.yourdomain.com"
287
+ baseURL: "https://api.yourdomain.com",
292
288
  });
293
289
 
294
290
  Page({
295
291
  data: {
296
292
  userInfo: null,
297
- hasPhone: false
293
+ hasPhone: false,
298
294
  },
299
295
 
300
296
  // Auto-login on page load
@@ -304,26 +300,26 @@ Page({
304
300
 
305
301
  // WeChat Mini Program login
306
302
  async handleLogin() {
307
- wx.showLoading({ title: 'Logging in...' });
308
-
303
+ wx.showLoading({ title: "Logging in..." });
304
+
309
305
  wx.login({
310
306
  success: async (res) => {
311
307
  if (res.code) {
312
308
  const result = await authClient.loginWithMiniProgram(res.code);
313
-
309
+
314
310
  wx.hideLoading();
315
-
311
+
316
312
  if (result.data) {
317
313
  this.setData({
318
- userInfo: result.data.user
314
+ userInfo: result.data.user,
319
315
  });
320
-
316
+
321
317
  // Navigate to home
322
- wx.switchTab({ url: '/pages/index/index' });
318
+ wx.switchTab({ url: "/pages/index/index" });
323
319
  } else {
324
320
  wx.showToast({
325
- title: 'Login failed',
326
- icon: 'none'
321
+ title: "Login failed",
322
+ icon: "none",
327
323
  });
328
324
  }
329
325
  }
@@ -331,47 +327,47 @@ Page({
331
327
  fail: () => {
332
328
  wx.hideLoading();
333
329
  wx.showToast({
334
- title: 'Login failed',
335
- icon: 'none'
330
+ title: "Login failed",
331
+ icon: "none",
336
332
  });
337
- }
333
+ },
338
334
  });
339
335
  },
340
336
 
341
337
  // Get phone number with user authorization
342
338
  async onGetPhoneNumber(e) {
343
339
  const { code } = e.detail;
344
-
340
+
345
341
  if (!code) {
346
342
  wx.showToast({
347
- title: 'Authorization cancelled',
348
- icon: 'none'
343
+ title: "Authorization cancelled",
344
+ icon: "none",
349
345
  });
350
346
  return;
351
347
  }
352
348
 
353
- wx.showLoading({ title: 'Getting phone...' });
354
-
349
+ wx.showLoading({ title: "Getting phone..." });
350
+
355
351
  const result = await authClient.getMiniProgramPhoneNumber(code);
356
-
352
+
357
353
  wx.hideLoading();
358
-
354
+
359
355
  if (result.data) {
360
356
  this.setData({
361
- hasPhone: true
357
+ hasPhone: true,
362
358
  });
363
-
359
+
364
360
  wx.showToast({
365
- title: 'Phone number obtained',
366
- icon: 'success'
361
+ title: "Phone number obtained",
362
+ icon: "success",
367
363
  });
368
364
  } else {
369
365
  wx.showToast({
370
- title: result.error?.message || 'Failed to get phone',
371
- icon: 'none'
366
+ title: result.error?.message || "Failed to get phone",
367
+ icon: "none",
372
368
  });
373
369
  }
374
- }
370
+ },
375
371
  });
376
372
  ```
377
373
 
@@ -412,67 +408,50 @@ await authClient.changePassword({
412
408
 
413
409
  ## Permission Checks
414
410
 
415
- ### Local Permission Checks (Fast)
411
+ ### Anonymous Access Support
416
412
 
417
- Permissions are cached locally as string arrays for fast UI checks:
413
+ The SDK automatically supports anonymous users with **zero configuration**:
418
414
 
419
415
  ```typescript
420
- // Check single role
421
- if (authClient.hasRole("admin")) {
422
- // Admin only
423
- }
416
+ // Create client
417
+ const authClient = createAuthClient();
424
418
 
425
- // Check single permission
426
- if (authClient.hasPermission("user.read")) {
427
- // Can read users
419
+ // Permission checks work for both authenticated and anonymous users
420
+ if (authClient.hasPermission("article", "read")) {
421
+ showArticleList();
428
422
  }
429
423
 
430
- // Check any permission
431
- if (authClient.hasAnyPermission(["user.read", "user.manage"])) {
432
- // Has at least one permission
424
+ // Check if user is anonymous
425
+ if (authClient.isAnonymous()) {
426
+ showLoginPrompt();
433
427
  }
434
428
 
435
- // Check all permissions
436
- if (authClient.hasAllPermissions(["user.read", "user.write"])) {
437
- // Has all permissions
438
- }
429
+ // After login, permissions automatically update
430
+ await authClient.login({ ... });
439
431
  ```
440
432
 
441
- ### On-Demand Data Scope Loading
433
+ ### Local Permission Checks (Fast)
442
434
 
443
- Data scopes are loaded only when needed to reduce data transfer:
435
+ Permissions are cached locally for fast UI checks:
444
436
 
445
437
  ```typescript
446
- // Get data scope for a specific permission
447
- const result = await authClient.getPermissionScope("user.read");
448
- if (result.data) {
449
- const { dataScope } = result.data;
450
-
451
- if (dataScope.scopeType === "department") {
452
- // Filter users by department
453
- const users = await api.getUsers({
454
- departmentId: dataScope.scopeFilter.departmentId
455
- });
456
- } else if (dataScope.scopeType === "all") {
457
- // Show all users
458
- const users = await api.getUsers();
459
- }
438
+ // Check role
439
+ if (authClient.hasRole("admin")) {
440
+ showAdminPanel();
460
441
  }
461
- ```
462
442
 
463
- **Optimized Data Structure:**
443
+ if (authClient.isAnonymous()) {
444
+ showLoginPrompt();
445
+ }
464
446
 
465
- ```typescript
466
- // User object (lightweight)
467
- {
468
- uid: "xxx",
469
- email: "user@example.com",
470
- roles: ["admin", "user"], // Only role codes
471
- permissions: ["user.read", "user.write", "order.read"], // Only permission names
472
- // NO dataScopes - loaded on demand
447
+ // Check permission
448
+ if (authClient.hasPermission("user", "read")) {
449
+ showUserList();
473
450
  }
474
451
  ```
475
452
 
453
+ ````
454
+
476
455
  ## OAuth Bindings
477
456
 
478
457
  ### Get Bindings
@@ -613,48 +592,32 @@ if (result.error) {
613
592
 
614
593
  ### Token Management
615
594
 
616
- - SDK automatically manages Access Token and Refresh Token
617
- - Access Token is stored in localStorage/sessionStorage
618
- - Refresh Token is managed by backend via HttpOnly Cookie
619
- - Token is automatically refreshed 5 minutes before expiration (configurable)
620
-
621
- ### Permission Sync
622
-
623
- - **On Page Load**: User info and permissions automatically sync from backend
624
- - **On Token Refresh**: Permissions re-sync every 5 minutes when token refreshes
625
- - **Manual Sync**: Call `await authClient.getMe()` to force sync anytime
626
-
627
- **Behavior:**
628
- - Permissions always stay fresh without any configuration
629
- - Page refresh loads latest permissions from backend
630
- - Background sync happens automatically every 5 minutes
595
+ - Tokens are automatically managed by the SDK
596
+ - Access Token refreshes 5 minutes before expiration
597
+ - No manual token handling required
631
598
 
632
599
  ### Permission Checks
633
600
 
634
- - **Frontend permission checks are for UI control only** (show/hide buttons, menus)
635
- - **Backend must verify permissions again** - frontend checks are NOT security measures
636
- - Use permission checks to improve UX, not as security enforcement
601
+ - Use permission checks for UI control (show/hide buttons, menus)
602
+ - Frontend checks are NOT security measures
603
+ - Backend must always verify permissions
637
604
 
638
605
  ### SSR Support
639
606
 
640
- The SDK detects SSR environments and uses a no-op storage adapter:
607
+ The SDK works in both browser and SSR environments:
641
608
 
642
609
  ```typescript
643
- // Safe in both browser and SSR
644
610
  const authClient = createAuthClient();
645
611
  ```
646
612
 
647
613
  ## TypeScript
648
614
 
649
- Full TypeScript support with comprehensive type definitions:
615
+ Full TypeScript support:
650
616
 
651
617
  ```typescript
652
618
  import type {
653
619
  User,
654
620
  LoginResponse,
655
- Permission,
656
- Role,
657
- DataScope,
658
621
  Session,
659
622
  OAuthBinding,
660
623
  } from "@amaster.ai/auth-client";
@@ -667,3 +630,4 @@ MIT
667
630
  ## Contributing
668
631
 
669
632
  Contributions are welcome! Please read our contributing guidelines before submitting PRs.
633
+ ````
package/dist/auth.d.cts CHANGED
@@ -14,7 +14,7 @@
14
14
  * ============================================================================
15
15
  */
16
16
  import { HttpClient, ClientResult } from '@amaster.ai/http-client';
17
- import { q as User, i as RegisterParams, e as LoginResponse, L as LoginParams, c as CodeLoginParams, S as SendCodeParams, p as SuccessResponse, b as CaptchaResponse, g as OAuthProvider, M as MiniProgramPhoneResponse, R as RefreshTokenResponse } from './types-Be3I8qJG.cjs';
17
+ import { q as User, i as RegisterParams, e as LoginResponse, L as LoginParams, c as CodeLoginParams, S as SendCodeParams, p as SuccessResponse, b as CaptchaResponse, g as OAuthProvider, M as MiniProgramPhoneResponse, R as RefreshTokenResponse } from './types-C56GAJKY.cjs';
18
18
 
19
19
  /**
20
20
  * Authentication Module
@@ -59,8 +59,14 @@ declare function createAuthModule(deps: AuthModuleDeps): {
59
59
  * @category Authentication
60
60
  * @example
61
61
  * ```typescript
62
+ * // Auto-detect loginType from username
63
+ * await auth.login({
64
+ * username: "john_doe",
65
+ * password: "Password@123",
66
+ * });
67
+ *
68
+ * // Auto-detect loginType from email
62
69
  * await auth.login({
63
- * loginType: "email",
64
70
  * email: "user@example.com",
65
71
  * password: "Password@123",
66
72
  * });
@@ -73,11 +79,17 @@ declare function createAuthModule(deps: AuthModuleDeps): {
73
79
  * @category Authentication
74
80
  * @example
75
81
  * ```typescript
82
+ * // Auto-detect loginType from email
76
83
  * await auth.loginWithCode({
77
- * loginType: "email",
78
84
  * email: "user@example.com",
79
85
  * code: "123456",
80
86
  * });
87
+ *
88
+ * // Auto-detect loginType from phone
89
+ * await auth.loginWithCode({
90
+ * phone: "13800138000",
91
+ * code: "123456",
92
+ * });
81
93
  * ```
82
94
  */
83
95
  loginWithCode(params: CodeLoginParams): Promise<ClientResult<LoginResponse>>;
package/dist/auth.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * ============================================================================
15
15
  */
16
16
  import { HttpClient, ClientResult } from '@amaster.ai/http-client';
17
- import { q as User, i as RegisterParams, e as LoginResponse, L as LoginParams, c as CodeLoginParams, S as SendCodeParams, p as SuccessResponse, b as CaptchaResponse, g as OAuthProvider, M as MiniProgramPhoneResponse, R as RefreshTokenResponse } from './types-Be3I8qJG.js';
17
+ import { q as User, i as RegisterParams, e as LoginResponse, L as LoginParams, c as CodeLoginParams, S as SendCodeParams, p as SuccessResponse, b as CaptchaResponse, g as OAuthProvider, M as MiniProgramPhoneResponse, R as RefreshTokenResponse } from './types-C56GAJKY.js';
18
18
 
19
19
  /**
20
20
  * Authentication Module
@@ -59,8 +59,14 @@ declare function createAuthModule(deps: AuthModuleDeps): {
59
59
  * @category Authentication
60
60
  * @example
61
61
  * ```typescript
62
+ * // Auto-detect loginType from username
63
+ * await auth.login({
64
+ * username: "john_doe",
65
+ * password: "Password@123",
66
+ * });
67
+ *
68
+ * // Auto-detect loginType from email
62
69
  * await auth.login({
63
- * loginType: "email",
64
70
  * email: "user@example.com",
65
71
  * password: "Password@123",
66
72
  * });
@@ -73,11 +79,17 @@ declare function createAuthModule(deps: AuthModuleDeps): {
73
79
  * @category Authentication
74
80
  * @example
75
81
  * ```typescript
82
+ * // Auto-detect loginType from email
76
83
  * await auth.loginWithCode({
77
- * loginType: "email",
78
84
  * email: "user@example.com",
79
85
  * code: "123456",
80
86
  * });
87
+ *
88
+ * // Auto-detect loginType from phone
89
+ * await auth.loginWithCode({
90
+ * phone: "13800138000",
91
+ * code: "123456",
92
+ * });
81
93
  * ```
82
94
  */
83
95
  loginWithCode(params: CodeLoginParams): Promise<ClientResult<LoginResponse>>;
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var httpClient=require('@amaster.ai/http-client');var y=class{constructor(){this.events={};}on(e,r){this.events[e]||(this.events[e]=[]),this.events[e].push(r);}off(e,r){this.events[e]&&(this.events[e]=this.events[e].filter(t=>t!==r));}emit(e,...r){this.events[e]&&this.events[e].forEach(t=>{try{t(...r);}catch(a){console.error(`[AuthClient] Error in event handler for "${e}":`,a);}});}removeAllListeners(){this.events={};}};var l={ACCESS_TOKEN:"amaster_access_token",REFRESH_TOKEN:"amaster_refresh_token",USER:"amaster_user"};function N(){return typeof wx<"u"&&wx.getStorageSync?"wechat-miniprogram":typeof window<"u"&&typeof window.localStorage<"u"?"browser":"node"}function S(){switch(N()){case "wechat-miniprogram":return $();case "browser":return L();case "node":return q()}}function L(){let s=window.localStorage;return {getItem(e){try{return s.getItem(e)}catch(r){return console.error("[AuthClient] Failed to get item from localStorage:",r),null}},setItem(e,r){try{s.setItem(e,r);}catch(t){console.error("[AuthClient] Failed to set item in localStorage:",t);}},removeItem(e){try{s.removeItem(e);}catch(r){console.error("[AuthClient] Failed to remove item from localStorage:",r);}},clear(){try{s.removeItem(l.ACCESS_TOKEN),s.removeItem(l.REFRESH_TOKEN),s.removeItem(l.USER);}catch(e){console.error("[AuthClient] Failed to clear localStorage:",e);}}}}function $(){return {getItem(s){try{return wx.getStorageSync(s)||null}catch(e){return console.error("[AuthClient] Failed to get item from WeChat storage:",e),null}},setItem(s,e){try{wx.setStorageSync(s,e);}catch(r){console.error("[AuthClient] Failed to set item in WeChat storage:",r);}},removeItem(s){try{wx.removeStorageSync(s);}catch(e){console.error("[AuthClient] Failed to remove item from WeChat storage:",e);}},clear(){try{wx.removeStorageSync(l.ACCESS_TOKEN),wx.removeStorageSync(l.REFRESH_TOKEN),wx.removeStorageSync(l.USER);}catch(s){console.error("[AuthClient] Failed to clear WeChat storage:",s);}}}}function q(){let s=new Map;return {getItem(e){return s.get(e)??null},setItem(e,r){s.set(e,r);},removeItem(e){s.delete(e);},clear(){s.clear();}}}function B(s){try{let e=s.split(".");if(e.length!==3)return null;let t=(e[1]||"").replace(/-/g,"+").replace(/_/g,"/");if(typeof atob<"u"){let a=decodeURIComponent(atob(t).split("").map(o=>"%"+("00"+o.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(a)}if(typeof Buffer<"u"){let a=Buffer.from(t,"base64").toString("utf-8");return JSON.parse(a)}return null}catch(e){return console.error("[AuthClient] Failed to parse JWT token:",e),null}}var C=class{constructor(){this.refreshTimer=null;this.isRefreshing=false;this.refreshCallback=null;}setRefreshCallback(e){this.refreshCallback=e;}scheduleRefresh(e){this.clearSchedule(),!(e<=0)&&(this.refreshTimer=setTimeout(()=>{this.refresh();},e));}scheduleRefreshFromToken(e,r=300){let t=B(e);if(!t||!t.exp){console.warn("[AuthClient] Cannot schedule refresh: invalid token or missing exp claim");return}let a=t.exp*1e3,o=Date.now(),c=a-o-r*1e3;c<=0?(console.warn("[AuthClient] Token already expired or expiring soon, refreshing immediately"),this.refresh()):this.scheduleRefresh(c);}async refresh(){if(!this.isRefreshing){if(!this.refreshCallback){console.error("[AuthClient] No refresh callback set");return}this.isRefreshing=true;try{await this.refreshCallback();}catch(e){console.error("[AuthClient] Token refresh failed:",e);}finally{this.isRefreshing=false;}}}clearSchedule(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null);}isCurrentlyRefreshing(){return this.isRefreshing}destroy(){this.clearSchedule(),this.refreshCallback=null,this.isRefreshing=false;}};function v(s){return s&&typeof s=="object"&&("statusCode"in s||"status"in s)&&"data"in s?s.data:s}var R={transformResponse:v,logErrors:true};function k(s){let{http:e,onLoginSuccess:r,storage:t,clearAuth:a}=s;return {async register(o){let n=await e.request({url:"/api/auth/register",method:"post",headers:{"Content-Type":"application/json"},data:o});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async login(o){let n=await e.request({url:"/api/auth/login",method:"post",headers:{"Content-Type":"application/json"},data:o});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async loginWithCode(o){let n=await e.request({url:"/api/auth/login-with-code",method:"post",headers:{"Content-Type":"application/json"},data:o});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async sendCode(o){return e.request({url:"/api/auth/send-code",method:"post",headers:{"Content-Type":"application/json"},data:o})},async getCaptcha(){return e.request({url:"/api/auth/captcha",method:"get"})},loginWithOAuth(o,n){if(typeof window>"u"){console.error("[AuthClient] OAuth login is only available in browser environment");return}let c=n?`/api/auth/oauth/${o}?redirect_url=${encodeURIComponent(n)}`:`/api/auth/oauth/${o}`;window.location.href=c;},async handleOAuthCallback(){if(typeof window>"u")return {data:null,error:{message:"OAuth callback is only available in browser environment",status:400},status:400};try{let o=window.location.hash.substring(1),n=new URLSearchParams(o),c=n.get("access_token"),h=n.get("user");if(!c||!h)return {data:null,error:{message:"OAuth callback failed: missing token or user data",status:400},status:400};let u=JSON.parse(decodeURIComponent(h));return r(u,c),{data:{user:u,accessToken:c},error:null,status:200}}catch(o){return {data:null,error:{message:`OAuth callback failed: ${o instanceof Error?o.message:String(o)}`,status:400},status:400}}},async loginWithMiniProgram(o){let n=await e.request({url:"/api/auth/miniprogram/login",method:"post",headers:{"Content-Type":"application/json"},data:{code:o}});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async getMiniProgramPhoneNumber(o){let n=t.getItem("amaster_access_token");return n?e.request({url:"/api/auth/miniprogram/phone",method:"post",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},data:{code:o}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async logout(){let o=t.getItem("amaster_access_token"),n=await e.request({url:"/api/auth/logout",method:"post",headers:o?{Authorization:`Bearer ${o}`}:void 0});return a(),n},async refreshToken(){return e.request({url:"/api/auth/refresh",method:"post"})}}}function P(s){let{getCurrentUser:e}=s;return {hasRole(r){let t=e();return !t||!t.roles?false:t.roles.includes(r)},hasPermission(r,t){let a=e();if(!a||!a.permissions)return false;let o=`${r}:${t}`;return a.permissions.includes(o)},hasAnyPermission(r){return r.some(({resource:t,action:a})=>this.hasPermission(t,a))},hasAllPermissions(r){return r.every(({resource:t,action:a})=>this.hasPermission(t,a))}}}function T(s){let{http:e,storage:r,onUserUpdate:t}=s;return {async getMe(){let a=r.getItem("amaster_access_token");if(!a)return {data:null,error:{message:"Not authenticated",status:401},status:401};let o=await e.request({url:"/api/auth/me",method:"get",headers:{Authorization:`Bearer ${a}`}});return o.data&&t(o.data),o},async updateMe(a){let o=r.getItem("amaster_access_token");if(!o)return {data:null,error:{message:"Not authenticated",status:401},status:401};let n=await e.request({url:"/api/auth/me",method:"put",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},data:a});return n.data&&t(n.data),n},async changePassword(a){let o=r.getItem("amaster_access_token");return o?e.request({url:"/api/auth/change-password",method:"post",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},data:a}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function w(s){let{http:e,storage:r}=s;return {async getOAuthBindings(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/oauth-bindings",method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},bindOAuth(t){if(typeof window>"u"){console.error("[AuthClient] OAuth binding is only available in browser environment");return}window.location.href=`/api/auth/oauth/${t}/bind`;},async unbindOAuth(t){let a=r.getItem("amaster_access_token");return a?e.request({url:`/api/auth/oauth/${t}/unbind`,method:"delete",headers:{Authorization:`Bearer ${a}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function E(s){let{http:e,storage:r}=s;return {async getSession(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/sessions/current",method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async getSessions(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/sessions",method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeSession(t){let a=r.getItem("amaster_access_token");return a?t?e.request({url:`/api/auth/sessions/${t}`,method:"delete",headers:{Authorization:`Bearer ${a}`}}):{data:null,error:{message:"Session ID is required",status:400},status:400}:{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeAllSessions(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/sessions",method:"delete",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function z(s={},e){let {baseURL:r,headers:t,onTokenExpired:a,onUnauthorized:o}=s,n=e||httpClient.createHttpClient({...R,baseURL:r,headers:t}),h=300,u=S(),m=new y,f=new C,g=null;try{let i=u.getItem(l.USER);i&&(g=JSON.parse(i));}catch(i){console.error("[AuthClient] Failed to load user from storage:",i);}let d;f.setRefreshCallback(async()=>{let i=await d.refreshToken();i.data?(await d.getMe(),m.emit("tokenRefreshed",i.data.accessToken)):(m.emit("tokenExpired"),a?.());});function M(i,p){u.setItem(l.ACCESS_TOKEN,p),u.setItem(l.USER,JSON.stringify(i)),g=i,f.scheduleRefreshFromToken(p,h),m.emit("login",i);}function O(i){g=i,u.setItem(l.USER,JSON.stringify(i));}function A(){u.clear(),g=null,f.clearSchedule();}function U(){return g}let b=k({http:n,onLoginSuccess:M,storage:u,clearAuth:A}),x=P({getCurrentUser:U}),I=T({http:n,storage:u,onUserUpdate:O}),_=w({http:n,storage:u}),H=E({http:n,storage:u});return d={...b,...x,...I,..._,...H,on(i,p){m.on(i,p);},off(i,p){m.off(i,p);},isAuthenticated(){return !!u.getItem(l.ACCESS_TOKEN)},getAccessToken(){return u.getItem(l.ACCESS_TOKEN)},setAccessToken(i){u.setItem(l.ACCESS_TOKEN,i),f.scheduleRefreshFromToken(i,h);},clearAuth:A},d.on("unauthorized",()=>{o?.();}),d.isAuthenticated()&&d.getMe().catch(i=>{console.warn("[AuthClient] Failed to sync user info on init:",i);}),d}exports.createAuthClient=z;exports.defaultHttpClientOptions=R;exports.transformAmasterResponse=v;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var httpClient=require('@amaster.ai/http-client');var y=class{constructor(){this.events={};}on(e,s){this.events[e]||(this.events[e]=[]),this.events[e].push(s);}off(e,s){this.events[e]&&(this.events[e]=this.events[e].filter(r=>r!==s));}emit(e,...s){this.events[e]&&this.events[e].forEach(r=>{try{r(...s);}catch(a){console.error(`[AuthClient] Error in event handler for "${e}":`,a);}});}removeAllListeners(){this.events={};}};var d={ACCESS_TOKEN:"amaster_access_token",REFRESH_TOKEN:"amaster_refresh_token",USER:"amaster_user"};function H(){return typeof wx<"u"&&wx.getStorageSync?"wechat-miniprogram":typeof window<"u"&&typeof window.localStorage<"u"?"browser":"node"}function S(){switch(H()){case "wechat-miniprogram":return q();case "browser":return N();case "node":return $()}}function N(){let t=window.localStorage;return {getItem(e){try{return t.getItem(e)}catch(s){return console.error("[AuthClient] Failed to get item from localStorage:",s),null}},setItem(e,s){try{t.setItem(e,s);}catch(r){console.error("[AuthClient] Failed to set item in localStorage:",r);}},removeItem(e){try{t.removeItem(e);}catch(s){console.error("[AuthClient] Failed to remove item from localStorage:",s);}},clear(){try{t.removeItem(d.ACCESS_TOKEN),t.removeItem(d.REFRESH_TOKEN),t.removeItem(d.USER);}catch(e){console.error("[AuthClient] Failed to clear localStorage:",e);}}}}function q(){return {getItem(t){try{return wx.getStorageSync(t)||null}catch(e){return console.error("[AuthClient] Failed to get item from WeChat storage:",e),null}},setItem(t,e){try{wx.setStorageSync(t,e);}catch(s){console.error("[AuthClient] Failed to set item in WeChat storage:",s);}},removeItem(t){try{wx.removeStorageSync(t);}catch(e){console.error("[AuthClient] Failed to remove item from WeChat storage:",e);}},clear(){try{wx.removeStorageSync(d.ACCESS_TOKEN),wx.removeStorageSync(d.REFRESH_TOKEN),wx.removeStorageSync(d.USER);}catch(t){console.error("[AuthClient] Failed to clear WeChat storage:",t);}}}}function $(){let t=new Map;return {getItem(e){return t.get(e)??null},setItem(e,s){t.set(e,s);},removeItem(e){t.delete(e);},clear(){t.clear();}}}function B(t){try{let e=t.split(".");if(e.length!==3)return null;let r=(e[1]||"").replace(/-/g,"+").replace(/_/g,"/");if(typeof atob<"u"){let a=decodeURIComponent(atob(r).split("").map(n=>"%"+("00"+n.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(a)}if(typeof Buffer<"u"){let a=Buffer.from(r,"base64").toString("utf-8");return JSON.parse(a)}return null}catch(e){return console.error("[AuthClient] Failed to parse JWT token:",e),null}}var C=class{constructor(){this.refreshTimer=null;this.isRefreshing=false;this.refreshCallback=null;}setRefreshCallback(e){this.refreshCallback=e;}scheduleRefresh(e){this.clearSchedule(),!(e<=0)&&(this.refreshTimer=setTimeout(()=>{this.refresh();},e));}scheduleRefreshFromToken(e,s=300){let r=B(e);if(!r||!r.exp){console.warn("[AuthClient] Cannot schedule refresh: invalid token or missing exp claim");return}let a=r.exp*1e3,n=Date.now(),c=a-n-s*1e3;c<=0?(console.warn("[AuthClient] Token already expired or expiring soon, refreshing immediately"),this.refresh()):this.scheduleRefresh(c);}async refresh(){if(!this.isRefreshing){if(!this.refreshCallback){console.error("[AuthClient] No refresh callback set");return}this.isRefreshing=true;try{await this.refreshCallback();}catch(e){console.error("[AuthClient] Token refresh failed:",e);}finally{this.isRefreshing=false;}}}clearSchedule(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null);}isCurrentlyRefreshing(){return this.isRefreshing}destroy(){this.clearSchedule(),this.refreshCallback=null,this.isRefreshing=false;}};function v(t){return t&&typeof t=="object"&&("statusCode"in t||"status"in t)&&"data"in t?t.data:t}var R={transformResponse:v,logErrors:true};function F(t){if(t.email)return "email";if(t.username)return "username";if(t.phone)return "phone"}function z(t){if(t.email)return "email";if(t.phone)return "phone"}function k(t){let{http:e,onLoginSuccess:s,storage:r,clearAuth:a}=t;return {async register(n){let o=await e.request({url:"/api/auth/register",method:"post",headers:{"Content-Type":"application/json"},data:n});return o.data?.user&&o.data?.accessToken&&s(o.data.user,o.data.accessToken),o},async login(n){let o=n.loginType||F(n);if(!o)return {data:null,error:{message:"Unable to determine login type. Please provide email, username, or phone.",status:400},status:400};let c={...n,loginType:o},u=await e.request({url:"/api/auth/login",method:"post",headers:{"Content-Type":"application/json"},data:c});return u.data?.user&&u.data?.accessToken&&s(u.data.user,u.data.accessToken),u},async loginWithCode(n){let o=n.loginType||z(n);if(!o)return {data:null,error:{message:"Unable to determine login type. Please provide email or phone.",status:400},status:400};let c={...n,loginType:o},u=await e.request({url:"/api/auth/login-with-code",method:"post",headers:{"Content-Type":"application/json"},data:c});return u.data?.user&&u.data?.accessToken&&s(u.data.user,u.data.accessToken),u},async sendCode(n){return e.request({url:"/api/auth/send-code",method:"post",headers:{"Content-Type":"application/json"},data:n})},async getCaptcha(){return e.request({url:"/api/auth/captcha",method:"get"})},loginWithOAuth(n,o){if(typeof window>"u"){console.error("[AuthClient] OAuth login is only available in browser environment");return}let c=o?`/api/auth/oauth/${n}?redirect_url=${encodeURIComponent(o)}`:`/api/auth/oauth/${n}`;window.location.href=c;},async handleOAuthCallback(){if(typeof window>"u")return {data:null,error:{message:"OAuth callback is only available in browser environment",status:400},status:400};try{let n=window.location.hash.substring(1),o=new URLSearchParams(n),c=o.get("access_token"),u=o.get("user");if(!c||!u)return {data:null,error:{message:"OAuth callback failed: missing token or user data",status:400},status:400};let l=JSON.parse(decodeURIComponent(u));return s(l,c),{data:{user:l,accessToken:c},error:null,status:200}}catch(n){return {data:null,error:{message:`OAuth callback failed: ${n instanceof Error?n.message:String(n)}`,status:400},status:400}}},async loginWithMiniProgram(n){let o=await e.request({url:"/api/auth/miniprogram/login",method:"post",headers:{"Content-Type":"application/json"},data:{code:n}});return o.data?.user&&o.data?.accessToken&&s(o.data.user,o.data.accessToken),o},async getMiniProgramPhoneNumber(n){let o=r.getItem("amaster_access_token");return o?e.request({url:"/api/auth/miniprogram/phone",method:"post",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},data:{code:n}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async logout(){let n=r.getItem("amaster_access_token"),o=await e.request({url:"/api/auth/logout",method:"post",headers:n?{Authorization:`Bearer ${n}`}:void 0});return a(),o},async refreshToken(){return e.request({url:"/api/auth/refresh",method:"post"})}}}function T(t){let{getCurrentUser:e}=t;return {hasRole(s){let r=e();return !r||!r.roles?false:r.roles.includes(s)},hasPermission(s,r){let a=e();if(!a||!a.permissions)return false;let n=`${s}:${r}`;return a.permissions.includes(n)},hasAnyPermission(s){return s.some(({resource:r,action:a})=>this.hasPermission(r,a))},hasAllPermissions(s){return s.every(({resource:r,action:a})=>this.hasPermission(r,a))}}}function P(t){let{http:e,storage:s,onUserUpdate:r}=t;return {async getMe(){let a=s.getItem("amaster_access_token");if(!a)return {data:null,error:{message:"Not authenticated",status:401},status:401};let n=await e.request({url:"/api/auth/me",method:"get",headers:{Authorization:`Bearer ${a}`}});return n.data&&r(n.data),n},async updateMe(a){let n=s.getItem("amaster_access_token");if(!n)return {data:null,error:{message:"Not authenticated",status:401},status:401};let o=await e.request({url:"/api/auth/me",method:"put",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},data:a});return o.data&&r(o.data),o},async changePassword(a){let n=s.getItem("amaster_access_token");return n?e.request({url:"/api/auth/change-password",method:"post",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},data:a}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function w(t){let{http:e,storage:s}=t;return {async getOAuthBindings(){let r=s.getItem("amaster_access_token");return r?e.request({url:"/api/auth/oauth-bindings",method:"get",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},bindOAuth(r){if(typeof window>"u"){console.error("[AuthClient] OAuth binding is only available in browser environment");return}window.location.href=`/api/auth/oauth/${r}/bind`;},async unbindOAuth(r){let a=s.getItem("amaster_access_token");return a?e.request({url:`/api/auth/oauth/${r}/unbind`,method:"delete",headers:{Authorization:`Bearer ${a}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function E(t){let{http:e,storage:s}=t;return {async getSession(){let r=s.getItem("amaster_access_token");return r?e.request({url:"/api/auth/sessions/current",method:"get",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async getSessions(){let r=s.getItem("amaster_access_token");return r?e.request({url:"/api/auth/sessions",method:"get",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeSession(r){let a=s.getItem("amaster_access_token");return a?r?e.request({url:`/api/auth/sessions/${r}`,method:"delete",headers:{Authorization:`Bearer ${a}`}}):{data:null,error:{message:"Session ID is required",status:400},status:400}:{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeAllSessions(){let r=s.getItem("amaster_access_token");return r?e.request({url:"/api/auth/sessions",method:"delete",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function J(t={},e){let {baseURL:s,headers:r,onTokenExpired:a,onUnauthorized:n}=t,o=e||httpClient.createHttpClient({...R,baseURL:s,headers:r}),u=300,l=S(),m=new y,f=new C,g=null;try{let i=l.getItem(d.USER);i&&(g=JSON.parse(i));}catch(i){console.error("[AuthClient] Failed to load user from storage:",i);}let p;f.setRefreshCallback(async()=>{let i=await p.refreshToken();i.data?(await p.getMe(),m.emit("tokenRefreshed",i.data.accessToken)):(m.emit("tokenExpired"),a?.());});function M(i,h){l.setItem(d.ACCESS_TOKEN,h),l.setItem(d.USER,JSON.stringify(i)),g=i,f.scheduleRefreshFromToken(h,u),m.emit("login",i);}function O(i){g=i,l.setItem(d.USER,JSON.stringify(i));}function A(){l.clear(),g=null,f.clearSchedule();}function U(){return g}let b=k({http:o,onLoginSuccess:M,storage:l,clearAuth:A}),x=T({getCurrentUser:U}),I=P({http:o,storage:l,onUserUpdate:O}),_=w({http:o,storage:l}),L=E({http:o,storage:l});return p={...b,...x,...I,..._,...L,on(i,h){m.on(i,h);},off(i,h){m.off(i,h);},isAuthenticated(){return !!l.getItem(d.ACCESS_TOKEN)},getAccessToken(){return l.getItem(d.ACCESS_TOKEN)},setAccessToken(i){l.setItem(d.ACCESS_TOKEN,i),f.scheduleRefreshFromToken(i,u);},clearAuth:A},p.on("unauthorized",()=>{n?.();}),p.isAuthenticated()&&p.getMe().catch(i=>{console.warn("[AuthClient] Failed to sync user info on init:",i);}),p}exports.createAuthClient=J;exports.defaultHttpClientOptions=R;exports.transformAmasterResponse=v;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map