@amaster.ai/auth-client 1.0.0-beta.6 → 1.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -6
- package/dist/auth.d.cts +46 -1
- package/dist/auth.d.ts +46 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.cts +1 -1
- package/dist/oauth.d.ts +1 -1
- package/dist/permissions.d.cts +14 -22
- package/dist/permissions.d.ts +14 -22
- package/dist/sessions.d.cts +1 -1
- package/dist/sessions.d.ts +1 -1
- package/dist/{types-Bgi_Lwkp.d.cts → types-Be3I8qJG.d.cts} +37 -67
- package/dist/{types-Bgi_Lwkp.d.ts → types-Be3I8qJG.d.ts} +37 -67
- package/dist/user.d.cts +1 -1
- package/dist/user.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,7 +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
|
-
- 🔗 **OAuth Integration**: Google, GitHub, WeChat, and custom OAuth providers
|
|
11
|
+
- 🔗 **OAuth Integration**: Google, GitHub, WeChat OAuth, WeChat Mini Program, and custom OAuth providers
|
|
12
12
|
- 📡 **Event System**: Listen to login, logout, and token events
|
|
13
13
|
- 💾 **Session Management**: View and revoke active sessions
|
|
14
14
|
- 🔒 **Type-Safe**: Full TypeScript support
|
|
@@ -65,22 +65,48 @@ if (authClient.hasPermission("user.read")) {
|
|
|
65
65
|
|
|
66
66
|
## Configuration
|
|
67
67
|
|
|
68
|
+
### Zero Configuration (Recommended)
|
|
69
|
+
|
|
70
|
+
The SDK works out of the box with **zero configuration**:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const authClient = createAuthClient();
|
|
74
|
+
|
|
75
|
+
// Storage auto-detects environment:
|
|
76
|
+
// - Browser → localStorage
|
|
77
|
+
// - WeChat Mini Program → wx.setStorageSync
|
|
78
|
+
// - SSR/Node.js → no-op (no persistence)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Optional Configuration
|
|
82
|
+
|
|
68
83
|
```typescript
|
|
69
84
|
interface AuthClientOptions {
|
|
70
|
-
baseURL?: string;
|
|
71
|
-
|
|
72
|
-
onTokenExpired?: () => void;
|
|
73
|
-
onUnauthorized?: () => void;
|
|
85
|
+
baseURL?: string; // API base URL, defaults to window.location.origin
|
|
86
|
+
headers?: Record<string, string>; // Custom headers for all requests
|
|
87
|
+
onTokenExpired?: () => void; // Token expiration callback
|
|
88
|
+
onUnauthorized?: () => void; // Unauthorized (401) callback
|
|
74
89
|
}
|
|
75
90
|
```
|
|
76
91
|
|
|
92
|
+
**Example:**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const authClient = createAuthClient({
|
|
96
|
+
baseURL: "https://api.example.com",
|
|
97
|
+
onTokenExpired: () => window.location.href = "/login",
|
|
98
|
+
onUnauthorized: () => alert("Session expired"),
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
77
102
|
**Built-in Features (Zero Config):**
|
|
78
103
|
|
|
104
|
+
- ✅ **Auto Environment Detection**: Works in browser, WeChat Mini Program, SSR
|
|
79
105
|
- ✅ **Auto Token Refresh**: Refreshes 5 minutes before expiry
|
|
80
106
|
- ✅ **Auto Permission Sync**: Syncs on page load and every token refresh
|
|
107
|
+
- ✅ **Smart Storage**: localStorage (browser), wx.storage (WeChat), no-op (SSR)
|
|
81
108
|
- ✅ **Lightweight Data**: Only permission names cached, not full objects
|
|
82
109
|
- ✅ **On-Demand DataScope**: Loads only when needed
|
|
83
|
-
- ✅ **SSR Compatible**: Works in both browser and server environments
|
|
84
110
|
|
|
85
111
|
## Authentication
|
|
86
112
|
|
|
@@ -178,6 +204,177 @@ if (window.opener) {
|
|
|
178
204
|
}
|
|
179
205
|
```
|
|
180
206
|
|
|
207
|
+
### WeChat Mini Program Login
|
|
208
|
+
|
|
209
|
+
The SDK automatically detects WeChat Mini Program environment and uses `wx.storage`:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { createAuthClient } from "@amaster.ai/auth-client";
|
|
213
|
+
|
|
214
|
+
// Zero configuration - auto-detects WeChat environment
|
|
215
|
+
const authClient = createAuthClient({
|
|
216
|
+
baseURL: "https://api.yourdomain.com"
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Login with WeChat code
|
|
220
|
+
wx.login({
|
|
221
|
+
success: async (res) => {
|
|
222
|
+
if (res.code) {
|
|
223
|
+
const result = await authClient.loginWithMiniProgram(res.code);
|
|
224
|
+
|
|
225
|
+
if (result.data) {
|
|
226
|
+
console.log("Logged in:", result.data.user);
|
|
227
|
+
// Token automatically saved to wx.storage
|
|
228
|
+
wx.switchTab({ url: '/pages/index/index' });
|
|
229
|
+
} else if (result.error) {
|
|
230
|
+
wx.showToast({
|
|
231
|
+
title: result.error.message || 'Login failed',
|
|
232
|
+
icon: 'none'
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
fail: (err) => {
|
|
238
|
+
console.error("wx.login failed:", err);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Get user phone number (optional):**
|
|
244
|
+
|
|
245
|
+
```xml
|
|
246
|
+
<!-- WXML -->
|
|
247
|
+
<button
|
|
248
|
+
open-type="getPhoneNumber"
|
|
249
|
+
bindgetphonenumber="onGetPhoneNumber"
|
|
250
|
+
>
|
|
251
|
+
Get Phone Number
|
|
252
|
+
</button>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// JS
|
|
257
|
+
async onGetPhoneNumber(e) {
|
|
258
|
+
const { code } = e.detail;
|
|
259
|
+
|
|
260
|
+
if (code) {
|
|
261
|
+
const result = await authClient.getMiniProgramPhoneNumber(code);
|
|
262
|
+
|
|
263
|
+
if (result.data) {
|
|
264
|
+
console.log("Phone number:", result.data.phone);
|
|
265
|
+
console.log("Verified:", result.data.phoneVerified);
|
|
266
|
+
|
|
267
|
+
// Update UI with phone number
|
|
268
|
+
this.setData({
|
|
269
|
+
phone: result.data.phone
|
|
270
|
+
});
|
|
271
|
+
} else if (result.error) {
|
|
272
|
+
wx.showToast({
|
|
273
|
+
title: result.error.message || 'Failed to get phone number',
|
|
274
|
+
icon: 'none'
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
// User denied authorization
|
|
279
|
+
console.log("User cancelled phone number authorization");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Complete Mini Program example:**
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// pages/login/login.js
|
|
288
|
+
import { createAuthClient } from "@amaster.ai/auth-client";
|
|
289
|
+
|
|
290
|
+
const authClient = createAuthClient({
|
|
291
|
+
baseURL: "https://api.yourdomain.com"
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
Page({
|
|
295
|
+
data: {
|
|
296
|
+
userInfo: null,
|
|
297
|
+
hasPhone: false
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
// Auto-login on page load
|
|
301
|
+
onLoad() {
|
|
302
|
+
this.handleLogin();
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// WeChat Mini Program login
|
|
306
|
+
async handleLogin() {
|
|
307
|
+
wx.showLoading({ title: 'Logging in...' });
|
|
308
|
+
|
|
309
|
+
wx.login({
|
|
310
|
+
success: async (res) => {
|
|
311
|
+
if (res.code) {
|
|
312
|
+
const result = await authClient.loginWithMiniProgram(res.code);
|
|
313
|
+
|
|
314
|
+
wx.hideLoading();
|
|
315
|
+
|
|
316
|
+
if (result.data) {
|
|
317
|
+
this.setData({
|
|
318
|
+
userInfo: result.data.user
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Navigate to home
|
|
322
|
+
wx.switchTab({ url: '/pages/index/index' });
|
|
323
|
+
} else {
|
|
324
|
+
wx.showToast({
|
|
325
|
+
title: 'Login failed',
|
|
326
|
+
icon: 'none'
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
fail: () => {
|
|
332
|
+
wx.hideLoading();
|
|
333
|
+
wx.showToast({
|
|
334
|
+
title: 'Login failed',
|
|
335
|
+
icon: 'none'
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
// Get phone number with user authorization
|
|
342
|
+
async onGetPhoneNumber(e) {
|
|
343
|
+
const { code } = e.detail;
|
|
344
|
+
|
|
345
|
+
if (!code) {
|
|
346
|
+
wx.showToast({
|
|
347
|
+
title: 'Authorization cancelled',
|
|
348
|
+
icon: 'none'
|
|
349
|
+
});
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
wx.showLoading({ title: 'Getting phone...' });
|
|
354
|
+
|
|
355
|
+
const result = await authClient.getMiniProgramPhoneNumber(code);
|
|
356
|
+
|
|
357
|
+
wx.hideLoading();
|
|
358
|
+
|
|
359
|
+
if (result.data) {
|
|
360
|
+
this.setData({
|
|
361
|
+
hasPhone: true
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
wx.showToast({
|
|
365
|
+
title: 'Phone number obtained',
|
|
366
|
+
icon: 'success'
|
|
367
|
+
});
|
|
368
|
+
} else {
|
|
369
|
+
wx.showToast({
|
|
370
|
+
title: result.error?.message || 'Failed to get phone',
|
|
371
|
+
icon: 'none'
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
181
378
|
### Logout
|
|
182
379
|
|
|
183
380
|
```typescript
|
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 {
|
|
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';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Authentication Module
|
|
@@ -116,6 +116,51 @@ declare function createAuthModule(deps: AuthModuleDeps): {
|
|
|
116
116
|
* @category Authentication
|
|
117
117
|
*/
|
|
118
118
|
handleOAuthCallback(): Promise<ClientResult<LoginResponse>>;
|
|
119
|
+
/**
|
|
120
|
+
* Login with WeChat Mini Program code
|
|
121
|
+
*
|
|
122
|
+
* @category Authentication
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* // In WeChat Mini Program
|
|
126
|
+
* wx.login({
|
|
127
|
+
* success: async (res) => {
|
|
128
|
+
* if (res.code) {
|
|
129
|
+
* const result = await auth.loginWithMiniProgram(res.code);
|
|
130
|
+
* if (result.data) {
|
|
131
|
+
* console.log("Logged in:", result.data.user);
|
|
132
|
+
* }
|
|
133
|
+
* }
|
|
134
|
+
* }
|
|
135
|
+
* });
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
loginWithMiniProgram(code: string): Promise<ClientResult<LoginResponse>>;
|
|
139
|
+
/**
|
|
140
|
+
* Get WeChat Mini Program user phone number
|
|
141
|
+
* Requires user authorization via getPhoneNumber button
|
|
142
|
+
*
|
|
143
|
+
* @category Authentication
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // WXML
|
|
147
|
+
* <button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
|
|
148
|
+
* Get Phone Number
|
|
149
|
+
* </button>
|
|
150
|
+
*
|
|
151
|
+
* // JS
|
|
152
|
+
* async onGetPhoneNumber(e) {
|
|
153
|
+
* const { code } = e.detail;
|
|
154
|
+
* if (code) {
|
|
155
|
+
* const result = await auth.getMiniProgramPhoneNumber(code);
|
|
156
|
+
* if (result.data) {
|
|
157
|
+
* console.log("Phone:", result.data.phone);
|
|
158
|
+
* }
|
|
159
|
+
* }
|
|
160
|
+
* }
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
getMiniProgramPhoneNumber(code: string): Promise<ClientResult<MiniProgramPhoneResponse>>;
|
|
119
164
|
/**
|
|
120
165
|
* Logout current user
|
|
121
166
|
*
|
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 {
|
|
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';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Authentication Module
|
|
@@ -116,6 +116,51 @@ declare function createAuthModule(deps: AuthModuleDeps): {
|
|
|
116
116
|
* @category Authentication
|
|
117
117
|
*/
|
|
118
118
|
handleOAuthCallback(): Promise<ClientResult<LoginResponse>>;
|
|
119
|
+
/**
|
|
120
|
+
* Login with WeChat Mini Program code
|
|
121
|
+
*
|
|
122
|
+
* @category Authentication
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* // In WeChat Mini Program
|
|
126
|
+
* wx.login({
|
|
127
|
+
* success: async (res) => {
|
|
128
|
+
* if (res.code) {
|
|
129
|
+
* const result = await auth.loginWithMiniProgram(res.code);
|
|
130
|
+
* if (result.data) {
|
|
131
|
+
* console.log("Logged in:", result.data.user);
|
|
132
|
+
* }
|
|
133
|
+
* }
|
|
134
|
+
* }
|
|
135
|
+
* });
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
loginWithMiniProgram(code: string): Promise<ClientResult<LoginResponse>>;
|
|
139
|
+
/**
|
|
140
|
+
* Get WeChat Mini Program user phone number
|
|
141
|
+
* Requires user authorization via getPhoneNumber button
|
|
142
|
+
*
|
|
143
|
+
* @category Authentication
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // WXML
|
|
147
|
+
* <button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
|
|
148
|
+
* Get Phone Number
|
|
149
|
+
* </button>
|
|
150
|
+
*
|
|
151
|
+
* // JS
|
|
152
|
+
* async onGetPhoneNumber(e) {
|
|
153
|
+
* const { code } = e.detail;
|
|
154
|
+
* if (code) {
|
|
155
|
+
* const result = await auth.getMiniProgramPhoneNumber(code);
|
|
156
|
+
* if (result.data) {
|
|
157
|
+
* console.log("Phone:", result.data.phone);
|
|
158
|
+
* }
|
|
159
|
+
* }
|
|
160
|
+
* }
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
getMiniProgramPhoneNumber(code: string): Promise<ClientResult<MiniProgramPhoneResponse>>;
|
|
119
164
|
/**
|
|
120
165
|
* Logout current user
|
|
121
166
|
*
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var httpClient=require('@amaster.ai/http-client');var g=class{constructor(){this.events={};}on(e,n){this.events[e]||(this.events[e]=[]),this.events[e].push(n);}off(e,n){this.events[e]&&(this.events[e]=this.events[e].filter(s=>s!==n));}emit(e,...n){this.events[e]&&this.events[e].forEach(s=>{try{s(...n);}catch(r){console.error(`[AuthClient] Error in event handler for "${e}":`,r);}});}removeAllListeners(){this.events={};}};var l={ACCESS_TOKEN:"amaster_access_token",REFRESH_TOKEN:"amaster_refresh_token",USER:"amaster_user"};function R(i="localStorage"){if(!(typeof window<"u"&&typeof window[i]<"u"))return b();let n=window[i==="sessionStorage"?"sessionStorage":"localStorage"];return {getItem(s){try{return n.getItem(s)}catch(r){return console.error(`[AuthClient] Failed to get item from ${i}:`,r),null}},setItem(s,r){try{n.setItem(s,r);}catch(t){console.error(`[AuthClient] Failed to set item in ${i}:`,t);}},removeItem(s){try{n.removeItem(s);}catch(r){console.error(`[AuthClient] Failed to remove item from ${i}:`,r);}},clear(){try{n.removeItem(l.ACCESS_TOKEN),n.removeItem(l.REFRESH_TOKEN),n.removeItem(l.USER);}catch(s){console.error(`[AuthClient] Failed to clear ${i}:`,s);}}}}function b(){return {getItem(){return null},setItem(){},removeItem(){},clear(){}}}function _(i){try{let e=i.split(".");if(e.length!==3)return null;let s=(e[1]||"").replace(/-/g,"+").replace(/_/g,"/");if(typeof atob<"u"){let r=decodeURIComponent(atob(s).split("").map(t=>"%"+("00"+t.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(r)}if(typeof Buffer<"u"){let r=Buffer.from(s,"base64").toString("utf-8");return JSON.parse(r)}return null}catch(e){return console.error("[AuthClient] Failed to parse JWT token:",e),null}}var f=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,n=300){let s=_(e);if(!s||!s.exp){console.warn("[AuthClient] Cannot schedule refresh: invalid token or missing exp claim");return}let r=s.exp*1e3,t=Date.now(),u=r-t-n*1e3;u<=0?(console.warn("[AuthClient] Token already expired or expiring soon, refreshing immediately"),this.refresh()):this.scheduleRefresh(u);}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 C(i){let{http:e,onLoginSuccess:n,storage:s,clearAuth:r}=i;return {async register(t){let o=await e.request({url:"/api/auth/register",method:"post",headers:{"Content-Type":"application/json"},data:t});return o.data?.user&&o.data?.accessToken&&n(o.data.user,o.data.accessToken),o},async login(t){let o=await e.request({url:"/api/auth/login",method:"post",headers:{"Content-Type":"application/json"},data:t});return o.data?.user&&o.data?.accessToken&&n(o.data.user,o.data.accessToken),o},async loginWithCode(t){let o=await e.request({url:"/api/auth/login-with-code",method:"post",headers:{"Content-Type":"application/json"},data:t});return o.data?.user&&o.data?.accessToken&&n(o.data.user,o.data.accessToken),o},async sendCode(t){return e.request({url:"/api/auth/send-code",method:"post",headers:{"Content-Type":"application/json"},data:t})},async getCaptcha(){return e.request({url:"/api/auth/captcha",method:"get"})},loginWithOAuth(t,o){if(typeof window>"u"){console.error("[AuthClient] OAuth login is only available in browser environment");return}let u=o?`/api/auth/oauth/${t}?redirect_url=${encodeURIComponent(o)}`:`/api/auth/oauth/${t}`;window.location.href=u;},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 t=window.location.hash.substring(1),o=new URLSearchParams(t),u=o.get("access_token"),c=o.get("user");if(!u||!c)return {data:null,error:{message:"OAuth callback failed: missing token or user data",status:400},status:400};let d=JSON.parse(decodeURIComponent(c));return n(d,u),{data:{user:d,accessToken:u},error:null,status:200}}catch(t){return {data:null,error:{message:`OAuth callback failed: ${t instanceof Error?t.message:String(t)}`,status:400},status:400}}},async logout(){let t=s.getItem("amaster_access_token"),o=await e.request({url:"/api/auth/logout",method:"post",headers:t?{Authorization:`Bearer ${t}`}:void 0});return r(),o},async refreshToken(){return e.request({url:"/api/auth/refresh",method:"post"})}}}function A(i){let{http:e,getCurrentUser:n,storage:s}=i;return {hasRole(r){let t=n();return !t||!t.roles?false:t.roles.includes(r)},hasPermission(r){let t=n();return !t||!t.permissions?false:t.permissions.includes(r)},hasAnyPermission(r){return r.some(t=>this.hasPermission(t))},hasAllPermissions(r){return r.every(t=>this.hasPermission(t))},async getPermissionScope(r){let t=s.getItem("amaster_access_token");return t?r?e.request({url:`/api/auth/permissions/${r}/scope`,method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Permission name is required",status:400},status:400}:{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function S(i){let{http:e,storage:n,onUserUpdate:s}=i;return {async getMe(){let r=n.getItem("amaster_access_token");if(!r)return {data:null,error:{message:"Not authenticated",status:401},status:401};let t=await e.request({url:"/api/auth/me",method:"get",headers:{Authorization:`Bearer ${r}`}});return t.data&&s(t.data),t},async updateMe(r){let t=n.getItem("amaster_access_token");if(!t)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 ${t}`,"Content-Type":"application/json"},data:r});return o.data&&s(o.data),o},async changePassword(r){let t=n.getItem("amaster_access_token");return t?e.request({url:"/api/auth/change-password",method:"post",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},data:r}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function v(i){let{http:e,storage:n}=i;return {async getOAuthBindings(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/oauth-bindings",method:"get",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},bindOAuth(s){if(typeof window>"u"){console.error("[AuthClient] OAuth binding is only available in browser environment");return}window.location.href=`/api/auth/oauth/${s}/bind`;},async unbindOAuth(s){let r=n.getItem("amaster_access_token");return r?e.request({url:`/api/auth/oauth/${s}/unbind`,method:"delete",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function k(i){let{http:e,storage:n}=i;return {async getSession(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/sessions/current",method:"get",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async getSessions(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/sessions",method:"get",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeSession(s){let r=n.getItem("amaster_access_token");return r?s?e.request({url:`/api/auth/sessions/${s}`,method:"delete",headers:{Authorization:`Bearer ${r}`}}):{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 s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/sessions",method:"delete",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function H(i={},e=httpClient.createHttpClient()){let {storage:n="localStorage",onTokenExpired:s,onUnauthorized:r}=i,o=300,u=R(n),c=new g,d=new f,m=null;try{let a=u.getItem(l.USER);a&&(m=JSON.parse(a));}catch(a){console.error("[AuthClient] Failed to load user from storage:",a);}let h;d.setRefreshCallback(async()=>{let a=await h.refreshToken();a.data?(await h.getMe(),c.emit("tokenRefreshed",a.data.accessToken)):(c.emit("tokenExpired"),s?.());});function P(a,p){u.setItem(l.ACCESS_TOKEN,p),u.setItem(l.USER,JSON.stringify(a)),m=a,d.scheduleRefreshFromToken(p,o),c.emit("login",a);}function T(a){m=a,u.setItem(l.USER,JSON.stringify(a));}function y(){u.clear(),m=null,d.clearSchedule();}function E(){return m}let M=C({http:e,onLoginSuccess:P,storage:u,clearAuth:y}),U=A({http:e,getCurrentUser:E,storage:u}),w=S({http:e,storage:u,onUserUpdate:T}),O=v({http:e,storage:u}),I=k({http:e,storage:u});return h={...M,...U,...w,...O,...I,on(a,p){c.on(a,p);},off(a,p){c.off(a,p);},isAuthenticated(){return !!u.getItem(l.ACCESS_TOKEN)},getAccessToken(){return u.getItem(l.ACCESS_TOKEN)},setAccessToken(a){u.setItem(l.ACCESS_TOKEN,a),d.scheduleRefreshFromToken(a,o);},clearAuth:y},h.on("unauthorized",()=>{r?.();}),h.isAuthenticated()&&h.getMe().catch(a=>{console.warn("[AuthClient] Failed to sync user info on init:",a);}),h}exports.createAuthClient=H;//# sourceMappingURL=index.cjs.map
|
|
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
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|