@amaster.ai/auth-client 1.0.0-alpha.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amaster Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,633 @@
1
+ # @amaster.ai/auth-client
2
+
3
+ Authentication SDK for Amaster Platform - Complete client-side authentication solution with OAuth, session management, and permission checks.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Multiple Authentication Methods**: Username/Email/Phone + Password, Verification Code, OAuth
8
+ - 🔄 **Automatic Token Refresh**: JWT token auto-refresh before expiration
9
+ - 📦 **Token Storage**: localStorage or sessionStorage with SSR support
10
+ - 🎯 **Permission System**: Role-based and permission-based access control
11
+ - 👤 **Anonymous Access**: Automatic support for anonymous users with configurable permissions
12
+ - 🔗 **OAuth Integration**: Google, GitHub, WeChat OAuth, WeChat Mini Program, and custom OAuth providers
13
+ - 📡 **Event System**: Listen to login, logout, and token events
14
+ - 💾 **Session Management**: View and revoke active sessions
15
+ - 🔒 **Type-Safe**: Full TypeScript support
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pnpm add @amaster.ai/auth-client
21
+ # or
22
+ npm install @amaster.ai/auth-client
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { createAuthClient } from "@amaster.ai/auth-client";
29
+
30
+ // Initialize the client (with optional callbacks)
31
+ const authClient = createAuthClient({
32
+ onTokenExpired: () => {
33
+ window.location.href = "/login";
34
+ },
35
+ onUnauthorized: () => {
36
+ window.location.href = "/login";
37
+ },
38
+ });
39
+
40
+ // Register a new user
41
+ await authClient.register({
42
+ email: "user@example.com",
43
+ password: "Password@123",
44
+ displayName: "John Doe",
45
+ });
46
+
47
+ // Login
48
+ const result = await authClient.login({
49
+ loginType: "email",
50
+ email: "user@example.com",
51
+ password: "Password@123",
52
+ });
53
+
54
+ if (result.data) {
55
+ console.log("Logged in:", result.data.user);
56
+ }
57
+
58
+ // Get current user
59
+ const user = await authClient.getMe();
60
+
61
+ // Check permissions
62
+ if (authClient.hasPermission("user.read")) {
63
+ // Show user list
64
+ }
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ ### Zero Configuration (Recommended)
70
+
71
+ The SDK works out of the box with **zero configuration**:
72
+
73
+ ```typescript
74
+ const authClient = createAuthClient();
75
+
76
+ // Storage auto-detects environment:
77
+ // - Browser → localStorage
78
+ // - WeChat Mini Program → wx.setStorageSync
79
+ // - SSR/Node.js → no-op (no persistence)
80
+ ```
81
+
82
+ ### Optional Configuration
83
+
84
+ ```typescript
85
+ interface AuthClientOptions {
86
+ baseURL?: string; // API base URL, defaults to window.location.origin
87
+ headers?: Record<string, string>; // Custom headers for all requests
88
+ onTokenExpired?: () => void; // Token expiration callback
89
+ onUnauthorized?: () => void; // Unauthorized (401) callback
90
+ }
91
+ ```
92
+
93
+ **Example:**
94
+
95
+ ```typescript
96
+ const authClient = createAuthClient({
97
+ baseURL: "https://api.example.com",
98
+ onTokenExpired: () => (window.location.href = "/login"),
99
+ onUnauthorized: () => alert("Session expired"),
100
+ });
101
+ ```
102
+
103
+ **Built-in Features (Zero Config):**
104
+
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
+
111
+ ## Authentication
112
+
113
+ ### Register
114
+
115
+ ```typescript
116
+ await authClient.register({
117
+ email: "user@example.com",
118
+ password: "Password@123",
119
+ displayName: "John Doe",
120
+ });
121
+ ```
122
+
123
+ **With Captcha:**
124
+
125
+ ```typescript
126
+ // 1. Get captcha
127
+ const captchaData = await authClient.getCaptcha();
128
+ document.getElementById("captcha-img").src = captchaData.data.captchaImage;
129
+
130
+ // 2. Register with captcha
131
+ const userInputCode = "AB12";
132
+ await authClient.register({
133
+ email: "user@example.com",
134
+ password: "Password@123",
135
+ captcha: `${captchaData.data.captchaId}:${userInputCode}`,
136
+ });
137
+ ```
138
+
139
+ ### Login
140
+
141
+ **Password Login:**
142
+
143
+ ```typescript
144
+ await authClient.login({
145
+ loginType: "email",
146
+ email: "user@example.com",
147
+ password: "Password@123",
148
+ });
149
+ ```
150
+
151
+ **Verification Code Login:**
152
+
153
+ ```typescript
154
+ // 1. Send code
155
+ await authClient.sendCode({
156
+ type: "email",
157
+ email: "user@example.com",
158
+ });
159
+
160
+ // 2. Login with code
161
+ await authClient.loginWithCode({
162
+ loginType: "email",
163
+ email: "user@example.com",
164
+ code: "123456",
165
+ });
166
+ ```
167
+
168
+ ### OAuth Login
169
+
170
+ **Full-page redirect (recommended):**
171
+
172
+ ```typescript
173
+ // Login page
174
+ sessionStorage.setItem("oauth_redirect", "/dashboard");
175
+ authClient.loginWithOAuth("google");
176
+
177
+ // Callback page (/auth/callback)
178
+ const { user } = await authClient.handleOAuthCallback();
179
+ const redirectUrl = sessionStorage.getItem("oauth_redirect") || "/";
180
+ window.location.href = redirectUrl;
181
+ ```
182
+
183
+ **Popup window:**
184
+
185
+ ```typescript
186
+ // Main page
187
+ const popup = window.open("/api/auth/oauth/google", "oauth-login", "width=600,height=700");
188
+
189
+ window.addEventListener("message", (event) => {
190
+ if (event.data.type === "oauth-success") {
191
+ authClient.setAccessToken(event.data.accessToken);
192
+ popup?.close();
193
+ }
194
+ });
195
+
196
+ // Callback page
197
+ if (window.opener) {
198
+ const { accessToken } = await authClient.handleOAuthCallback();
199
+ window.opener.postMessage({ type: "oauth-success", accessToken }, origin);
200
+ }
201
+ ```
202
+
203
+ ### WeChat Mini Program Login
204
+
205
+ The SDK automatically detects WeChat Mini Program environment and uses `wx.storage`:
206
+
207
+ ```typescript
208
+ import { createAuthClient } from "@amaster.ai/auth-client";
209
+
210
+ // Zero configuration - auto-detects WeChat environment
211
+ const authClient = createAuthClient({
212
+ baseURL: "https://api.yourdomain.com",
213
+ });
214
+
215
+ // Login with WeChat code
216
+ wx.login({
217
+ success: async (res) => {
218
+ if (res.code) {
219
+ const result = await authClient.loginWithMiniProgram(res.code);
220
+
221
+ if (result.data) {
222
+ console.log("Logged in:", result.data.user);
223
+ // Token automatically saved to wx.storage
224
+ wx.switchTab({ url: "/pages/index/index" });
225
+ } else if (result.error) {
226
+ wx.showToast({
227
+ title: result.error.message || "Login failed",
228
+ icon: "none",
229
+ });
230
+ }
231
+ }
232
+ },
233
+ fail: (err) => {
234
+ console.error("wx.login failed:", err);
235
+ },
236
+ });
237
+ ```
238
+
239
+ **Get user phone number (optional):**
240
+
241
+ ```xml
242
+ <!-- WXML -->
243
+ <button
244
+ open-type="getPhoneNumber"
245
+ bindgetphonenumber="onGetPhoneNumber"
246
+ >
247
+ Get Phone Number
248
+ </button>
249
+ ```
250
+
251
+ ```typescript
252
+ // JS
253
+ async onGetPhoneNumber(e) {
254
+ const { code } = e.detail;
255
+
256
+ if (code) {
257
+ const result = await authClient.getMiniProgramPhoneNumber(code);
258
+
259
+ if (result.data) {
260
+ console.log("Phone number:", result.data.phone);
261
+ console.log("Verified:", result.data.phoneVerified);
262
+
263
+ // Update UI with phone number
264
+ this.setData({
265
+ phone: result.data.phone
266
+ });
267
+ } else if (result.error) {
268
+ wx.showToast({
269
+ title: result.error.message || 'Failed to get phone number',
270
+ icon: 'none'
271
+ });
272
+ }
273
+ } else {
274
+ // User denied authorization
275
+ console.log("User cancelled phone number authorization");
276
+ }
277
+ }
278
+ ```
279
+
280
+ **Complete Mini Program example:**
281
+
282
+ ```typescript
283
+ // pages/login/login.js
284
+ import { createAuthClient } from "@amaster.ai/auth-client";
285
+
286
+ const authClient = createAuthClient({
287
+ baseURL: "https://api.yourdomain.com",
288
+ });
289
+
290
+ Page({
291
+ data: {
292
+ userInfo: null,
293
+ hasPhone: false,
294
+ },
295
+
296
+ // Auto-login on page load
297
+ onLoad() {
298
+ this.handleLogin();
299
+ },
300
+
301
+ // WeChat Mini Program login
302
+ async handleLogin() {
303
+ wx.showLoading({ title: "Logging in..." });
304
+
305
+ wx.login({
306
+ success: async (res) => {
307
+ if (res.code) {
308
+ const result = await authClient.loginWithMiniProgram(res.code);
309
+
310
+ wx.hideLoading();
311
+
312
+ if (result.data) {
313
+ this.setData({
314
+ userInfo: result.data.user,
315
+ });
316
+
317
+ // Navigate to home
318
+ wx.switchTab({ url: "/pages/index/index" });
319
+ } else {
320
+ wx.showToast({
321
+ title: "Login failed",
322
+ icon: "none",
323
+ });
324
+ }
325
+ }
326
+ },
327
+ fail: () => {
328
+ wx.hideLoading();
329
+ wx.showToast({
330
+ title: "Login failed",
331
+ icon: "none",
332
+ });
333
+ },
334
+ });
335
+ },
336
+
337
+ // Get phone number with user authorization
338
+ async onGetPhoneNumber(e) {
339
+ const { code } = e.detail;
340
+
341
+ if (!code) {
342
+ wx.showToast({
343
+ title: "Authorization cancelled",
344
+ icon: "none",
345
+ });
346
+ return;
347
+ }
348
+
349
+ wx.showLoading({ title: "Getting phone..." });
350
+
351
+ const result = await authClient.getMiniProgramPhoneNumber(code);
352
+
353
+ wx.hideLoading();
354
+
355
+ if (result.data) {
356
+ this.setData({
357
+ hasPhone: true,
358
+ });
359
+
360
+ wx.showToast({
361
+ title: "Phone number obtained",
362
+ icon: "success",
363
+ });
364
+ } else {
365
+ wx.showToast({
366
+ title: result.error?.message || "Failed to get phone",
367
+ icon: "none",
368
+ });
369
+ }
370
+ },
371
+ });
372
+ ```
373
+
374
+ ### Logout
375
+
376
+ ```typescript
377
+ await authClient.logout();
378
+ ```
379
+
380
+ ## User Management
381
+
382
+ ### Get Current User
383
+
384
+ ```typescript
385
+ const result = await authClient.getMe();
386
+ if (result.data) {
387
+ console.log(result.data); // User object
388
+ }
389
+ ```
390
+
391
+ ### Update Profile
392
+
393
+ ```typescript
394
+ await authClient.updateMe({
395
+ displayName: "New Name",
396
+ avatarUrl: "https://example.com/avatar.jpg",
397
+ });
398
+ ```
399
+
400
+ ### Change Password
401
+
402
+ ```typescript
403
+ await authClient.changePassword({
404
+ oldPassword: "OldPassword@123",
405
+ newPassword: "NewPassword@123",
406
+ });
407
+ ```
408
+
409
+ ## Permission Checks
410
+
411
+ ### Anonymous Access Support
412
+
413
+ The SDK automatically supports anonymous users with **zero configuration**:
414
+
415
+ ```typescript
416
+ // Create client
417
+ const authClient = createAuthClient();
418
+
419
+ // Permission checks work for both authenticated and anonymous users
420
+ if (authClient.hasPermission("article", "read")) {
421
+ showArticleList();
422
+ }
423
+
424
+ // Check if user is anonymous
425
+ if (authClient.isAnonymous()) {
426
+ showLoginPrompt();
427
+ }
428
+
429
+ // After login, permissions automatically update
430
+ await authClient.login({ ... });
431
+ ```
432
+
433
+ ### Local Permission Checks (Fast)
434
+
435
+ Permissions are cached locally for fast UI checks:
436
+
437
+ ```typescript
438
+ // Check role
439
+ if (authClient.hasRole("admin")) {
440
+ showAdminPanel();
441
+ }
442
+
443
+ if (authClient.isAnonymous()) {
444
+ showLoginPrompt();
445
+ }
446
+
447
+ // Check permission
448
+ if (authClient.hasPermission("user", "read")) {
449
+ showUserList();
450
+ }
451
+ ```
452
+
453
+ ````
454
+
455
+ ## OAuth Bindings
456
+
457
+ ### Get Bindings
458
+
459
+ ```typescript
460
+ const result = await authClient.getOAuthBindings();
461
+ if (result.data) {
462
+ console.log(result.data); // Array of OAuthBinding
463
+ }
464
+ ```
465
+
466
+ ### Bind OAuth Account
467
+
468
+ ```typescript
469
+ authClient.bindOAuth("google"); // Redirects to OAuth flow
470
+ ```
471
+
472
+ ### Unbind OAuth Account
473
+
474
+ ```typescript
475
+ await authClient.unbindOAuth("google");
476
+ ```
477
+
478
+ ## Session Management
479
+
480
+ ### Get Sessions
481
+
482
+ ```typescript
483
+ const result = await authClient.getSessions();
484
+ if (result.data) {
485
+ result.data.forEach((session) => {
486
+ console.log(session.ip, session.userAgent, session.isCurrent);
487
+ });
488
+ }
489
+ ```
490
+
491
+ ### Revoke Session
492
+
493
+ ```typescript
494
+ await authClient.revokeSession("session-id");
495
+ ```
496
+
497
+ ### Revoke All Sessions
498
+
499
+ ```typescript
500
+ const result = await authClient.revokeAllSessions();
501
+ if (result.data) {
502
+ console.log(`Revoked ${result.data.revokedCount} sessions`);
503
+ }
504
+ ```
505
+
506
+ ## Event System
507
+
508
+ ### Available Events
509
+
510
+ - `login` - User logged in, callback: `(user: User) => void`
511
+ - `logout` - User logged out, callback: `() => void`
512
+ - `tokenExpired` - Token expired, callback: `() => void`
513
+ - `tokenRefreshed` - Token refreshed, callback: `(token: string) => void`
514
+ - `unauthorized` - Unauthorized (401), callback: `() => void`
515
+
516
+ ### Usage
517
+
518
+ ```typescript
519
+ // Subscribe to events
520
+ authClient.on("unauthorized", () => {
521
+ authClient.clearAuth();
522
+ window.location.href = "/login";
523
+ });
524
+
525
+ authClient.on("login", (user) => {
526
+ console.log("User logged in:", user.displayName);
527
+ });
528
+
529
+ authClient.on("tokenRefreshed", (token) => {
530
+ console.log("Token refreshed");
531
+ });
532
+
533
+ // Unsubscribe
534
+ const handler = () => console.log("Logged out");
535
+ authClient.on("logout", handler);
536
+ authClient.off("logout", handler);
537
+ ```
538
+
539
+ ## Utility Methods
540
+
541
+ ### Check Authentication Status
542
+
543
+ ```typescript
544
+ if (authClient.isAuthenticated()) {
545
+ // User is logged in
546
+ }
547
+ ```
548
+
549
+ ### Get Access Token
550
+
551
+ ```typescript
552
+ const token = authClient.getAccessToken();
553
+ ```
554
+
555
+ ### Set Access Token
556
+
557
+ ```typescript
558
+ authClient.setAccessToken("your-token-here");
559
+ ```
560
+
561
+ ### Clear Auth Data
562
+
563
+ ```typescript
564
+ authClient.clearAuth(); // Clears token and user data
565
+ ```
566
+
567
+ ## Error Handling
568
+
569
+ ```typescript
570
+ try {
571
+ await authClient.login({ ... });
572
+ } catch (error) {
573
+ // Network error or exception
574
+ console.error("Login failed:", error);
575
+ }
576
+
577
+ // Or use result pattern
578
+ const result = await authClient.login({ ... });
579
+ if (result.error) {
580
+ // API error response
581
+ if (result.error.status === 401) {
582
+ alert("Invalid email or password");
583
+ } else if (result.error.status === 403) {
584
+ alert("Account is disabled");
585
+ } else {
586
+ alert(`Login failed: ${result.error.message}`);
587
+ }
588
+ }
589
+ ```
590
+
591
+ ## Best Practices
592
+
593
+ ### Token Management
594
+
595
+ - Tokens are automatically managed by the SDK
596
+ - Access Token refreshes 5 minutes before expiration
597
+ - No manual token handling required
598
+
599
+ ### Permission Checks
600
+
601
+ - Use permission checks for UI control (show/hide buttons, menus)
602
+ - Frontend checks are NOT security measures
603
+ - Backend must always verify permissions
604
+
605
+ ### SSR Support
606
+
607
+ The SDK works in both browser and SSR environments:
608
+
609
+ ```typescript
610
+ const authClient = createAuthClient();
611
+ ```
612
+
613
+ ## TypeScript
614
+
615
+ Full TypeScript support:
616
+
617
+ ```typescript
618
+ import type {
619
+ User,
620
+ LoginResponse,
621
+ Session,
622
+ OAuthBinding,
623
+ } from "@amaster.ai/auth-client";
624
+ ```
625
+
626
+ ## License
627
+
628
+ MIT
629
+
630
+ ## Contributing
631
+
632
+ Contributions are welcome! Please read our contributing guidelines before submitting PRs.
633
+ ````