@bagelink/auth 1.4.171 → 1.4.174

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
@@ -1,186 +1,330 @@
1
1
  # @bagelink/auth
2
2
 
3
- A framework-agnostic authentication composable with Vue support.
3
+ A user-centric authentication library with Vue support. Handles both person and entity accounts seamlessly.
4
4
 
5
5
  ## Features
6
6
 
7
- - Framework-agnostic reactivity system
8
- - Automatic Vue integration
9
- - TypeScript support
10
- - Complete authentication flow (login, signup, password recovery, etc.)
11
- - Error handling
12
- - User management
13
- - Event listeners for authentication state changes
7
+ - 🎯 **User-Centric Design** - Access user data directly, not buried in account objects
8
+ - 🔄 **Multi-Account Support** - Works with person, entity, and service accounts
9
+ - 🛡️ **Type-Safe** - Full TypeScript support
10
+ - **Vue Integration** - Reactive refs with Vue 3 composables
11
+ - 🔐 **Complete Auth Flow** - Login, signup, password management, email verification
12
+ - 📡 **Session Management** - View, refresh, and revoke sessions
13
+ - 🎪 **Event System** - Listen to authentication state changes
14
+ - 🌐 **Framework Agnostic** - Core auth logic works anywhere
14
15
 
15
16
  ## Installation
16
17
 
17
18
  ```bash
19
+ pnpm add @bagelink/auth
20
+ # or
21
+ npm install @bagelink/auth
22
+ # or
18
23
  bun add @bagelink/auth
19
24
  ```
20
25
 
21
- ## Usage
26
+ ## Quick Start
22
27
 
23
- ### Basic Setup
28
+ ### 1. Initialize Auth
24
29
 
25
30
  ```typescript
26
- import { initAuth, AuthState } from '@bagelink/auth'
31
+ import { initAuth } from '@bagelink/auth'
32
+ import axios from 'axios'
27
33
 
28
- // Initialize with axios instance
29
34
  const auth = initAuth({
30
- axios: axiosInstance,
35
+ axios: axios,
31
36
  baseURL: 'https://api.example.com'
32
37
  })
33
38
 
34
- // Add event listeners for authentication state changes
39
+ // Listen to auth events
35
40
  auth.on(AuthState.LOGIN, () => {
36
41
  console.log('User logged in!')
37
- // Redirect to dashboard, update UI, etc.
38
42
  })
43
+ ```
39
44
 
40
- auth.on(AuthState.LOGOUT, () => {
41
- console.log('User logged out!')
42
- // Redirect to login page, clear sensitive data, etc.
45
+ ### 2. Use in Vue Components
46
+
47
+ ```vue
48
+ <script setup lang="ts">
49
+ import { useAuth } from '@bagelink/auth'
50
+
51
+ const {
52
+ user, // Primary state - use this!
53
+ getIsLoggedIn,
54
+ login,
55
+ logout
56
+ } = useAuth()
57
+
58
+ const handleLogin = async () => {
59
+ await login({
60
+ email: 'user@example.com',
61
+ password: 'password'
62
+ })
63
+ }
64
+ </script>
65
+
66
+ <template>
67
+ <div v-if="user">
68
+ <h1>Welcome, {{ user.name }}!</h1>
69
+ <p>Email: {{ user.email }}</p>
70
+ <button @click="logout">Logout</button>
71
+ </div>
72
+ <div v-else>
73
+ <button @click="handleLogin">Login</button>
74
+ </div>
75
+ </template>
76
+ ```
77
+
78
+ ## Core Concepts
79
+
80
+ ### The `user` Object
81
+
82
+ The `user` is a **computed ref** that provides a unified interface for both person and entity accounts:
83
+
84
+ ```typescript
85
+ const { user } = useAuth()
86
+
87
+ // Available for all account types
88
+ user.value?.id // Person ID or Entity ID
89
+ user.value?.name // Display name
90
+ user.value?.email // Email address
91
+ user.value?.type // 'person', 'entity', or 'service'
92
+ user.value?.isActive // Is account active
93
+ user.value?.isVerified // Is account verified
94
+
95
+ // Person-specific
96
+ user.value?.roles // User roles (e.g., ['admin', 'user'])
97
+
98
+ // Entity-specific
99
+ user.value?.entityType // Entity type (e.g., 'company', 'organization')
100
+ user.value?.metadata // Additional entity metadata
101
+ ```
102
+
103
+ ### Account Info (Advanced)
104
+
105
+ For authentication-specific data, use `accountInfo`:
106
+
107
+ ```typescript
108
+ const { accountInfo } = useAuth()
109
+
110
+ accountInfo.value?.authentication_methods // Auth methods
111
+ accountInfo.value?.last_login // Last login timestamp
112
+ accountInfo.value?.person // Raw person data
113
+ accountInfo.value?.entity // Raw entity data
114
+ ```
115
+
116
+ ## API Reference
117
+
118
+ ### State
119
+
120
+ ```typescript
121
+ const {
122
+ user, // Computed<User | null> - Primary state
123
+ accountInfo, // Ref<AccountInfo | null> - Full account data
124
+ } = useAuth()
125
+ ```
126
+
127
+ ### Getters
128
+
129
+ ```typescript
130
+ const {
131
+ getFullName, // () => string
132
+ getIsLoggedIn, // () => boolean
133
+ getEmail, // () => string
134
+ getRoles, // () => string[]
135
+ getAccountType, // () => 'person' | 'entity' | 'service'
136
+ isPersonAccount, // () => boolean
137
+ isEntityAccount, // () => boolean
138
+ } = useAuth()
139
+ ```
140
+
141
+ ### Authentication
142
+
143
+ ```typescript
144
+ // Login
145
+ await login({ email: 'user@example.com', password: 'password' })
146
+
147
+ // Signup
148
+ await signup({
149
+ email: 'user@example.com',
150
+ first_name: 'John',
151
+ last_name: 'Doe',
152
+ password: 'password',
153
+ confirmPassword: 'password'
43
154
  })
155
+
156
+ // Logout
157
+ await logout()
158
+
159
+ // Check auth status
160
+ const isAuthenticated = await checkAuth()
161
+
162
+ // Refresh session
163
+ await refreshSession()
44
164
  ```
45
165
 
46
- ### Vue Integration
166
+ ### Password Management
47
167
 
48
168
  ```typescript
49
- // In your main.ts
50
- import { createApp } from 'vue'
51
- import { initAuth } from '@bagelink/auth'
169
+ // Change password (requires current password)
170
+ await changePassword({
171
+ current_password: 'oldPassword',
172
+ new_password: 'newPassword',
173
+ confirmNewPassword: 'newPassword'
174
+ })
52
175
 
53
- const app = createApp(App)
176
+ // Forgot password
177
+ await forgotPassword('user@example.com')
54
178
 
55
- // Initialize auth
56
- const auth = initAuth({
57
- axios: axiosInstance,
58
- errorHandler: (error) => console.error(error)
179
+ // Verify reset token
180
+ await verifyResetToken(token)
181
+
182
+ // Reset password with token
183
+ await resetPassword(token, 'newPassword')
184
+ ```
185
+
186
+ ### Email Verification
187
+
188
+ ```typescript
189
+ // Send verification email
190
+ await sendVerification('user@example.com')
191
+
192
+ // Verify email with token
193
+ await verifyEmail(token)
194
+ ```
195
+
196
+ ### Profile Management
197
+
198
+ ```typescript
199
+ // Update profile
200
+ await updateProfile({
201
+ first_name: 'Jane',
202
+ last_name: 'Smith',
203
+ email: 'jane@example.com'
59
204
  })
60
205
 
61
- // Use as a plugin
62
- app.use(auth)
206
+ // Delete current user account
207
+ await deleteCurrentUser()
208
+ ```
63
209
 
64
- // In your components
65
- import { useAuth } from '@bagelink/auth'
210
+ ### Session Management
66
211
 
67
- export default {
68
- setup() {
69
- const { login, currentUser, getIsLoggedIn } = useAuth()
70
-
71
- // Use auth methods
72
- const handleLogin = async () => {
73
- await login({
74
- email: 'user@example.com',
75
- password: 'password'
76
- })
77
- }
78
-
79
- return {
80
- currentUser,
81
- getIsLoggedIn,
82
- handleLogin
83
- }
84
- }
85
- }
212
+ ```typescript
213
+ // Get active sessions
214
+ const { data } = await getSessions()
215
+ const sessions = data.sessions
216
+
217
+ // Revoke specific session
218
+ await revokeSession(sessionToken)
219
+
220
+ // Revoke all sessions
221
+ await revokeAllSessions()
222
+ ```
223
+
224
+ ### Admin Actions
225
+
226
+ ```typescript
227
+ // Activate/deactivate accounts
228
+ await activateAccount(accountId)
229
+ await deactivateAccount(accountId)
230
+
231
+ // Delete account
232
+ await deleteAccount(accountId)
86
233
  ```
87
234
 
88
- ### Event Listeners
235
+ ## Event System
89
236
 
90
- You can listen to authentication state changes using the event system:
237
+ Listen to authentication state changes:
91
238
 
92
239
  ```typescript
93
240
  import { initAuth, AuthState } from '@bagelink/auth'
94
241
 
95
- const auth = initAuth({
96
- axios: axiosInstance,
97
- baseURL: 'https://api.example.com'
98
- })
242
+ const auth = initAuth({ axios })
99
243
 
100
- // Listen to login events
244
+ // Login
101
245
  auth.on(AuthState.LOGIN, () => {
102
- router.push('/dashboard')
246
+ console.log('User logged in')
103
247
  })
104
248
 
105
- // Listen to logout events
249
+ // Logout
106
250
  auth.on(AuthState.LOGOUT, () => {
107
- router.push('/login')
251
+ console.log('User logged out')
108
252
  })
109
253
 
110
- // Listen to signup events
254
+ // Signup
111
255
  auth.on(AuthState.SIGNUP, () => {
112
- router.push('/welcome')
256
+ console.log('User signed up')
257
+ })
258
+
259
+ // Password changed
260
+ auth.on(AuthState.PASSWORD_CHANGE, () => {
261
+ console.log('Password changed')
113
262
  })
114
263
 
115
- // Listen to password reset events
264
+ // Password reset
116
265
  auth.on(AuthState.PASSWORD_RESET, () => {
117
- console.log('Password has been reset')
266
+ console.log('Password reset')
118
267
  })
119
268
 
120
- // Listen to profile update events
269
+ // Profile updated
121
270
  auth.on(AuthState.PROFILE_UPDATE, () => {
122
- console.log('Profile updated successfully')
271
+ console.log('Profile updated')
123
272
  })
124
273
 
125
- // Listen to auth check events (when user is authenticated)
274
+ // Auth check completed
126
275
  auth.on(AuthState.AUTH_CHECK, () => {
127
- console.log('User authentication verified')
276
+ console.log('Auth verified')
128
277
  })
129
- ```
130
-
131
- #### Event Methods
132
278
 
133
- - `auth.on(event, handler)`: Add an event listener
134
- - `auth.off(event, handler)`: Remove a specific event listener
135
- - `auth.removeAllListeners(event?)`: Remove all listeners for an event (or all events if no event specified)
136
-
137
- #### Available Events
279
+ // Email verified
280
+ auth.on(AuthState.EMAIL_VERIFIED, () => {
281
+ console.log('Email verified')
282
+ })
138
283
 
139
- - `AuthState.LOGIN`: Fired when user successfully logs in
140
- - `AuthState.LOGOUT`: Fired when user logs out
141
- - `AuthState.SIGNUP`: Fired when user successfully signs up
142
- - `AuthState.PASSWORD_RESET`: Fired when password is reset or updated
143
- - `AuthState.PROFILE_UPDATE`: Fired when user profile is updated
144
- - `AuthState.AUTH_CHECK`: Fired when user authentication is verified
284
+ // Session refreshed
285
+ auth.on(AuthState.SESSION_REFRESH, () => {
286
+ console.log('Session refreshed')
287
+ })
288
+ ```
145
289
 
146
- ### Vanilla JavaScript
290
+ ### Event Methods
147
291
 
148
292
  ```typescript
149
- import { initAuth } from '@bagelink/auth'
293
+ // Add listener
294
+ auth.on(AuthState.LOGIN, handler)
150
295
 
151
- const auth = initAuth({
152
- axios: axiosInstance,
153
- baseURL: 'https://api.example.com'
154
- })
155
-
156
- const { login, currentUser, getIsLoggedIn } = auth.useAuth()
157
- ```
296
+ // Remove listener
297
+ auth.off(AuthState.LOGIN, handler)
158
298
 
159
- ## API
299
+ // Remove all listeners for an event
300
+ auth.removeAllListeners(AuthState.LOGIN)
160
301
 
161
- ### State
302
+ // Remove all listeners
303
+ auth.removeAllListeners()
304
+ ```
162
305
 
163
- - `currentUser`: Current user information
164
- - `passwordForm`: Password update form state
306
+ ## TypeScript Support
165
307
 
166
- ### Getters
308
+ Full TypeScript support with exported types:
167
309
 
168
- - `getFullName()`: Get user's full name
169
- - `getIsLoggedIn()`: Check if user is logged in
310
+ ```typescript
311
+ import type {
312
+ User,
313
+ AccountInfo,
314
+ PersonInfo,
315
+ EntityInfo,
316
+ RegisterRequest,
317
+ UpdateAccountRequest,
318
+ AuthenticationResponse,
319
+ SessionInfo,
320
+ // ... and more
321
+ } from '@bagelink/auth'
322
+ ```
170
323
 
171
- ### Actions
324
+ ## Migration
172
325
 
173
- - `login(credentials)`: Login with email and password
174
- - `logout()`: Logout current user
175
- - `checkAuth()`: Check authentication status
176
- - `signup(user)`: Register new user
177
- - `recoverPassword(email)`: Request password recovery
178
- - `resetPassword(form)`: Reset password
179
- - `updatePassword()`: Update current user's password
180
- - `updateProfile(user)`: Update user profile
181
- - `toggleUserStatus(userId, isActive)`: Toggle user active status
182
- - `deleteUser(userId)`: Delete user
326
+ Upgrading from an older version? See [MIGRATION.md](./MIGRATION.md) for detailed migration instructions.
183
327
 
184
328
  ## License
185
329
 
186
- MIT
330
+ MIT
package/dist/index.cjs CHANGED
@@ -263,10 +263,52 @@ var AuthState = /* @__PURE__ */ ((AuthState2) => {
263
263
  AuthState2["SESSION_REFRESH"] = "session_refresh";
264
264
  return AuthState2;
265
265
  })(AuthState || {});
266
+ function accountToUser(account) {
267
+ if (account === null) return null;
268
+ if (account.person !== void 0) {
269
+ return {
270
+ id: account.person.id,
271
+ accountId: account.id,
272
+ name: account.person.name,
273
+ email: account.person.email,
274
+ type: account.account_type,
275
+ roles: account.person.roles,
276
+ isActive: account.is_active,
277
+ isVerified: account.is_verified,
278
+ lastLogin: account.last_login
279
+ };
280
+ }
281
+ if (account.entity !== void 0) {
282
+ return {
283
+ id: account.entity.id,
284
+ accountId: account.id,
285
+ name: account.entity.name,
286
+ type: account.account_type,
287
+ isActive: account.is_active,
288
+ isVerified: account.is_verified,
289
+ lastLogin: account.last_login,
290
+ entityType: account.entity.type,
291
+ metadata: account.entity.metadata
292
+ };
293
+ }
294
+ const emailMethod = account.authentication_methods?.find(
295
+ (m) => m.type === "password" || m.type === "email_token"
296
+ );
297
+ return {
298
+ id: account.id,
299
+ accountId: account.id,
300
+ name: account.display_name,
301
+ email: emailMethod?.identifier,
302
+ type: account.account_type,
303
+ isActive: account.is_active,
304
+ isVerified: account.is_verified,
305
+ lastLogin: account.last_login
306
+ };
307
+ }
266
308
 
267
309
  let authApi = null;
268
310
  let eventEmitter = null;
269
- const currentUser = vue.ref(null);
311
+ const accountInfo = vue.ref(null);
270
312
  function initAuth({
271
313
  axios,
272
314
  baseURL
@@ -308,32 +350,33 @@ function useAuth() {
308
350
  }
309
351
  const api = authApi;
310
352
  const emitter = eventEmitter;
353
+ const user = vue.computed(() => accountToUser(accountInfo.value));
311
354
  const getFullName = () => {
312
- const user = currentUser.value;
313
- if (user === null) return "";
314
- if (user.person !== void 0) return user.person.name;
315
- return user.display_name ?? "";
355
+ return user.value?.name ?? "";
316
356
  };
317
357
  const getIsLoggedIn = () => {
318
- const user = currentUser.value;
319
- return user !== null && user.id.length > 0;
358
+ return user.value !== null;
320
359
  };
321
360
  const getEmail = () => {
322
- const user = currentUser.value;
323
- if (user === null) return "";
324
- if (user.person?.email !== void 0) {
325
- return user.person.email;
326
- }
327
- const emailMethod = user.authentication_methods?.find(
328
- (m) => m.type === "password" || m.type === "email_token"
329
- );
330
- return emailMethod?.identifier ?? "";
361
+ return user.value?.email ?? "";
362
+ };
363
+ const getRoles = () => {
364
+ return user.value?.roles ?? [];
365
+ };
366
+ const getAccountType = () => {
367
+ return user.value?.type ?? "person";
368
+ };
369
+ const isPersonAccount = () => {
370
+ return user.value?.type === "person";
371
+ };
372
+ const isEntityAccount = () => {
373
+ return user.value?.type === "entity";
331
374
  };
332
375
  async function logout() {
333
376
  const logoutPromise = api.logout();
334
377
  await logoutPromise.catch(() => {
335
378
  });
336
- currentUser.value = null;
379
+ accountInfo.value = null;
337
380
  emitter.emit(AuthState.LOGOUT);
338
381
  }
339
382
  async function login(credentials) {
@@ -350,26 +393,27 @@ function useAuth() {
350
393
  async function checkAuth() {
351
394
  try {
352
395
  const { data } = await api.getCurrentUser();
353
- currentUser.value = data;
396
+ accountInfo.value = data;
354
397
  if (getIsLoggedIn()) {
355
398
  emitter.emit(AuthState.AUTH_CHECK);
356
399
  }
357
400
  return true;
358
401
  } catch {
359
- currentUser.value = null;
402
+ accountInfo.value = null;
360
403
  return false;
361
404
  }
362
405
  }
363
- async function signup(user) {
364
- if (user.password && user.password !== user.confirmPassword) {
406
+ async function signup(newUser) {
407
+ const hasPassword = newUser.password !== void 0 && newUser.password.length > 0;
408
+ if (hasPassword && newUser.password !== newUser.confirmPassword) {
365
409
  throw new Error("Passwords do not match");
366
410
  }
367
411
  const { data } = await api.register({
368
- email: user.email,
369
- first_name: user.first_name,
370
- last_name: user.last_name,
371
- phone_number: user.phone_number,
372
- password: user.password
412
+ email: newUser.email,
413
+ first_name: newUser.first_name,
414
+ last_name: newUser.last_name,
415
+ phone_number: newUser.phone_number,
416
+ password: newUser.password
373
417
  });
374
418
  if (data.success === true && data.requires_verification !== true) {
375
419
  await checkAuth();
@@ -399,7 +443,7 @@ function useAuth() {
399
443
  }
400
444
  async function updateProfile(updates) {
401
445
  const { data } = await api.updateCurrentUser(updates);
402
- currentUser.value = data;
446
+ accountInfo.value = data;
403
447
  emitter.emit(AuthState.PROFILE_UPDATE);
404
448
  }
405
449
  async function activateAccount(accountId) {
@@ -413,7 +457,7 @@ function useAuth() {
413
457
  }
414
458
  async function deleteCurrentUser() {
415
459
  await api.deleteCurrentUser();
416
- currentUser.value = null;
460
+ accountInfo.value = null;
417
461
  }
418
462
  async function sendVerification(email) {
419
463
  await api.sendVerification({ email });
@@ -428,7 +472,7 @@ function useAuth() {
428
472
  emitter.emit(AuthState.SESSION_REFRESH);
429
473
  }
430
474
  async function getSessions(accountId) {
431
- const id = accountId ?? currentUser.value?.id;
475
+ const id = accountId ?? user.value?.accountId;
432
476
  if (id === void 0 || id === "") {
433
477
  throw new Error("No account ID available");
434
478
  }
@@ -438,19 +482,25 @@ function useAuth() {
438
482
  await api.revokeSession(sessionToken);
439
483
  }
440
484
  async function revokeAllSessions(accountId) {
441
- const id = accountId ?? currentUser.value?.id;
485
+ const id = accountId ?? user.value?.accountId;
442
486
  if (id === void 0 || id === "") {
443
487
  throw new Error("No account ID available");
444
488
  }
445
489
  await api.revokeAllSessions(id);
446
490
  }
447
491
  return {
448
- // State
449
- currentUser,
492
+ // Primary State (use this!)
493
+ user,
494
+ // Full account info (for advanced use cases)
495
+ accountInfo,
450
496
  // Getters
451
497
  getFullName,
452
498
  getIsLoggedIn,
453
499
  getEmail,
500
+ getRoles,
501
+ getAccountType,
502
+ isPersonAccount,
503
+ isEntityAccount,
454
504
  // Authentication Actions
455
505
  login,
456
506
  logout,
@@ -481,5 +531,6 @@ function useAuth() {
481
531
 
482
532
  exports.AuthApi = AuthApi;
483
533
  exports.AuthState = AuthState;
534
+ exports.accountToUser = accountToUser;
484
535
  exports.initAuth = initAuth;
485
536
  exports.useAuth = useAuth;