@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 +21 -0
- package/README.md +633 -0
- package/dist/auth.d.cts +196 -0
- package/dist/auth.d.ts +196 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth.d.cts +76 -0
- package/dist/oauth.d.ts +76 -0
- package/dist/permissions.d.cts +100 -0
- package/dist/permissions.d.ts +100 -0
- package/dist/sessions.d.cts +96 -0
- package/dist/sessions.d.ts +96 -0
- package/dist/types-BhHE_geU.d.cts +481 -0
- package/dist/types-BhHE_geU.d.ts +481 -0
- package/dist/user.d.cts +82 -0
- package/dist/user.d.ts +82 -0
- package/package.json +72 -0
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
|
+
````
|