authula 0.2.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 ADDED
@@ -0,0 +1,627 @@
1
+
2
+ <p align="center">
3
+ <img src="./project-logo.png" width="200" />
4
+ </p>
5
+
6
+ <p align="center">
7
+ This Node.js SDK provides seamless integration with an Authula server for both client-side and server-side applications and is framework agnostic.
8
+ </p>
9
+
10
+ ---
11
+
12
+ ## Features
13
+
14
+ - **Framework Agnostic**: Works with Next.js/React, Vue.js and more
15
+ - **Full TypeScript Support**: Complete TypeScript definitions with strict typing
16
+ - **SSR Safe**: Supports proper cookie handling for SSR apps
17
+ - **CSRF Protection**: Automatic CSRF token handling for mutating requests
18
+ - **Multiple Auth Methods**: Email/password, OAuth2, JWT-based authentication
19
+ - **Plugin Architecture**: Extensible plugin system for custom authentication flows
20
+ - **Automatic Token Refresh**: Built-in bearer token refresh mechanism
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install authula
26
+ # or
27
+ yarn add authula
28
+ # or
29
+ pnpm add authula
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### Basic Setup
35
+
36
+ ```typescript
37
+ import { createClient } from "authula";
38
+ import { EmailPasswordPlugin } from "authula/plugins";
39
+
40
+ // Create a client instance
41
+ const authulaClient = createClient({
42
+ // Your Authula server URL
43
+ url: "http://localhost:8080/auth",
44
+ plugins: [new EmailPasswordPlugin()],
45
+ });
46
+
47
+ // Now you can use the client to perform authentication operations
48
+ ```
49
+
50
+ ### Using with Cookies (SSR Compatible)
51
+
52
+ For server-side rendering or applications that need to handle cookies properly:
53
+
54
+ ```typescript
55
+ import { cookies } from "next/headers";
56
+
57
+ import { createClient } from "authula";
58
+ import { EmailPasswordPlugin, CSRFPlugin } from "authula/plugins";
59
+
60
+ const authulaClient = createClient({
61
+ url: "http://localhost:8080/auth",
62
+ cookies: cookies, // Provide cookie store for SSR
63
+ plugins: [
64
+ new EmailPasswordPlugin(),
65
+ new CSRFPlugin({
66
+ cookieName: "authula_csrf_token",
67
+ headerName: "X-AUTHULA-CSRF-TOKEN",
68
+ }),
69
+ ],
70
+ });
71
+ ```
72
+
73
+ ## Core Client Methods
74
+
75
+ The client provides built-in methods for essential authentication operations:
76
+
77
+ ### Get Current User
78
+
79
+ Retrieve information about the currently authenticated user:
80
+
81
+ ```typescript
82
+ const { user, session } = await authulaClient.getMe();
83
+
84
+ console.log(user.email);
85
+ console.log(session.id);
86
+ ```
87
+
88
+ ### Sign Out
89
+
90
+ Sign out the current user or all sessions:
91
+
92
+ ```typescript
93
+ // Sign out current session
94
+ await authulaClient.signOut({});
95
+
96
+ // Sign out all sessions
97
+ await authulaClient.signOut({
98
+ signOutAll: true,
99
+ });
100
+
101
+ // Sign out a specific session
102
+ await authulaClient.signOut({
103
+ sessionId: "session-id",
104
+ });
105
+ ```
106
+
107
+ ### Get Plugin Instance
108
+
109
+ Access a plugin instance programmatically:
110
+
111
+ ```typescript
112
+ const emailPasswordPlugin = authulaClient.getPlugin("emailPassword");
113
+
114
+ // Use the plugin
115
+ if (emailPasswordPlugin) {
116
+ // Plugin methods are now available
117
+ }
118
+ ```
119
+
120
+ ## Available Plugins
121
+
122
+ ### Access Control Plugin
123
+
124
+ Provides role-based access control (RBAC) management for users, including roles, permissions, and their assignments.
125
+
126
+ ```typescript
127
+ import { AccessControlPlugin } from "authula/plugins";
128
+
129
+ const authulaClient = createClient({
130
+ url: "http://localhost:8080/auth",
131
+ plugins: [new AccessControlPlugin()],
132
+ });
133
+ ```
134
+
135
+ #### Role Management
136
+
137
+ ```typescript
138
+ // Create a new role
139
+ await authulaClient.accessControl.createRole({
140
+ name: "Admin",
141
+ description: "Administrator role",
142
+ isSystem: false,
143
+ });
144
+
145
+ // Get all roles
146
+ const allRoles = await authulaClient.accessControl.getAllRoles();
147
+
148
+ // Get a specific role
149
+ const role = await authulaClient.accessControl.getRoleById("role-id");
150
+
151
+ // Update a role
152
+ await authulaClient.accessControl.updateRole("role-id", {
153
+ name: "Updated name",
154
+ description: "Updated description",
155
+ });
156
+
157
+ // Delete a role
158
+ await authulaClient.accessControl.deleteRole("role-id");
159
+ ```
160
+
161
+ #### Permission Management
162
+
163
+ ```typescript
164
+ // Create a new permission
165
+ await authulaClient.accessControl.createPermission({
166
+ key: "users.create",
167
+ description: "Create new users",
168
+ isSystem: false,
169
+ });
170
+
171
+ // Get all permissions
172
+ const allPermissions = await authulaClient.accessControl.getAllPermissions();
173
+
174
+ // Update a permission
175
+ await authulaClient.accessControl.updatePermission("permission-id", {
176
+ description: "Updated permission description",
177
+ });
178
+
179
+ // Delete a permission
180
+ await authulaClient.accessControl.deletePermission("permission-id");
181
+ ```
182
+
183
+ #### Role-Permission Management
184
+
185
+ ```typescript
186
+ // Add a permission to a role
187
+ await authulaClient.accessControl.addRolePermission("role-id", {
188
+ permissionId: "permission-id",
189
+ });
190
+
191
+ // Get all permissions for a role
192
+ const rolePermissions = await authulaClient.accessControl.getRolePermissions(
193
+ "role-id",
194
+ );
195
+
196
+ // Replace all permissions for a role
197
+ await authulaClient.accessControl.replaceRolePermissions("role-id", {
198
+ permissionIds: ["permission-id-1", "permission-id-2", "permission-id-3"],
199
+ });
200
+
201
+ // Remove a permission from a role
202
+ await authulaClient.accessControl.removeRolePermission(
203
+ "role-id",
204
+ "permission-id",
205
+ );
206
+ ```
207
+
208
+ #### User Role Management
209
+
210
+ ```typescript
211
+ // Get all roles assigned to a user
212
+ const userRoles = await authulaClient.accessControl.getUserRoles("user-id");
213
+
214
+ // Assign a role to a user
215
+ await authulaClient.accessControl.assignUserRole("user-id", {
216
+ roleId: "role-id",
217
+ expiresAt: new Date("2025-12-31"), // Optional: role expiration date
218
+ });
219
+
220
+ // Replace all roles for a user
221
+ await authulaClient.accessControl.replaceUserRoles("user-id", {
222
+ roleIds: ["role-id-1", "role-id-2"],
223
+ });
224
+
225
+ // Remove a role from a user
226
+ await authulaClient.accessControl.removeUserRole("user-id", "role-id");
227
+ ```
228
+
229
+ #### User Permission Management
230
+
231
+ ```typescript
232
+ // Get all effective permissions for a user (from all assigned roles)
233
+ const userPermissions = await authulaClient.accessControl.getUserEffectivePermissions("user-id");
234
+ ```
235
+
236
+ ---
237
+
238
+ ### Admin Plugin
239
+
240
+ Provides the ability to manage users, accounts, sessions, and impersonations.
241
+
242
+ ```typescript
243
+ import { AdminPlugin } from "authula/plugins";
244
+
245
+ const authulaClient = createClient({
246
+ url: "http://localhost:8080/auth",
247
+ plugins: [new AdminPlugin()],
248
+ });
249
+ ```
250
+
251
+ #### User Management
252
+
253
+ ```typescript
254
+ await authulaClient.admin.createUser({
255
+ name: "John Doe",
256
+ email: "user@example.com",
257
+ // other fields...
258
+ });
259
+
260
+ // Get all users with pagination
261
+ const users = await authulaClient.admin.getAllUsers(100);
262
+
263
+ // Get a specific user by ID
264
+ const user = await authulaClient.admin.getUserById("user-id");
265
+
266
+ // Update user information
267
+ await authulaClient.admin.updateUser("user-id", { name: "Jane" });
268
+
269
+ // Delete a user
270
+ await authulaClient.admin.deleteUser("user-id");
271
+ ```
272
+
273
+ #### Account Management
274
+
275
+ ```typescript
276
+ // Create a new account for a user
277
+ await authulaClient.admin.createAccount("user-id", { /* account data */ });
278
+
279
+ // Get all accounts for a user
280
+ await authulaClient.admin.getUserAccounts("user-id");
281
+
282
+ // Get a specific account by ID
283
+ await authulaClient.admin.getAccountById("account-id");
284
+
285
+ // Update account information
286
+ await authulaClient.admin.updateAccount("account-id", { /* data */ });
287
+
288
+ // Delete an account
289
+ await authulaClient.admin.deleteAccount("account-id");
290
+ ```
291
+
292
+ #### User State Management
293
+
294
+ ```typescript
295
+ // Get user state
296
+ await authulaClient.admin.getUserState("user-id");
297
+
298
+ // Create or update user state
299
+ await authulaClient.admin.createUserState("user-id", { /* state data */ });
300
+
301
+ // Update user state
302
+ await authulaClient.admin.updateUserState("user-id", { /* state data */ });
303
+
304
+ // Delete user state
305
+ await authulaClient.admin.deleteUserState("user-id");
306
+
307
+ // Get all banned user states
308
+ await authulaClient.admin.getBannedUserStates();
309
+
310
+ // Ban a user
311
+ await authulaClient.admin.banUser("user-id", { reason: "reason..." });
312
+
313
+ // Unban a user
314
+ await authulaClient.admin.unbanUser("user-id");
315
+
316
+ // Get all active sessions for a user
317
+ await authulaClient.admin.getUserAdminSessions("user-id");
318
+ ```
319
+
320
+ #### Session State Management
321
+
322
+ ```typescript
323
+ // Get session state
324
+ await authulaClient.admin.getSessionState("session-id");
325
+
326
+ // Create or update session state
327
+ await authulaClient.admin.createSessionState("session-id", { /* data */ });
328
+
329
+ // Update session state
330
+ await authulaClient.admin.updateSessionState("session-id", { /* data */ });
331
+
332
+ // Delete session state
333
+ await authulaClient.admin.deleteSessionState("session-id");
334
+
335
+ // Get all revoked session states
336
+ await authulaClient.admin.getRevokedSessionStates();
337
+
338
+ // Revoke a session
339
+ await authulaClient.admin.revokeSession("session-id", { reason: "reason..." });
340
+ ```
341
+
342
+ #### Impersonations
343
+
344
+ ```typescript
345
+ // Get all active impersonations
346
+ await authulaClient.admin.getAllImpersonations();
347
+
348
+ // Get a specific impersonation by ID
349
+ await authulaClient.admin.getImpersonationById("impersonation-id");
350
+
351
+ // Start impersonating a user
352
+ await authulaClient.admin.startImpersonation({ user_id: "user-id", /* data */ });
353
+
354
+ // Stop impersonation
355
+ await authulaClient.admin.stopImpersonation("impersonation-id");
356
+ ```
357
+
358
+ ---
359
+
360
+ ### Email Password Plugin
361
+
362
+ Handles traditional email/password authentication flows.
363
+
364
+ ```typescript
365
+ import { EmailPasswordPlugin } from "authula/plugins";
366
+
367
+ const authulaClient = createClient({
368
+ url: "http://localhost:8080/auth",
369
+ plugins: [new EmailPasswordPlugin()],
370
+ });
371
+
372
+ // Sign up
373
+ await authulaClient.emailPassword.signUp({
374
+ name: "John Doe",
375
+ email: "john@example.com",
376
+ password: "securePassword123",
377
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
378
+ });
379
+
380
+ // Sign in
381
+ const response = await authulaClient.emailPassword.signIn({
382
+ email: "john@example.com",
383
+ password: "securePassword123",
384
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
385
+ });
386
+
387
+ // Send email verification
388
+ await authulaClient.emailPassword.sendEmailVerification({
389
+ email: "john@example.com",
390
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
391
+ });
392
+
393
+ // Request password reset
394
+ await authulaClient.emailPassword.requestPasswordReset({
395
+ email: "john@example.com",
396
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
397
+ });
398
+
399
+ // Change password
400
+ await authulaClient.emailPassword.changePassword({
401
+ token: "reset-token",
402
+ password: "newSecurePassword123",
403
+ });
404
+
405
+ // Request email change
406
+ await authulaClient.emailPassword.requestEmailChange({
407
+ email: "john.doe@example.com",
408
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
409
+ });
410
+ ```
411
+
412
+ ---
413
+
414
+ ### OAuth2 Plugin
415
+
416
+ Handles OAuth2 authentication with popular providers.
417
+
418
+ ```typescript
419
+ import { OAuth2Plugin } from "authula/plugins";
420
+
421
+ const authulaClient = createClient({
422
+ url: "http://localhost:8080/auth",
423
+ plugins: [new OAuth2Plugin()],
424
+ });
425
+
426
+ // Redirect user to OAuth2 provider
427
+ const response = await authulaClient.oauth2.signIn({
428
+ provider: "google", // or "github", "discord"
429
+ redirect_to: "http://localhost:3000/callback",
430
+ });
431
+
432
+ // Redirect user to the authUrl
433
+ window.location.href = response.auth_url;
434
+ ```
435
+
436
+ ---
437
+
438
+ ### CSRF Plugin
439
+
440
+ Provides automatic CSRF protection for mutating requests.
441
+
442
+ ```typescript
443
+ import { CSRFPlugin } from "authula/plugins";
444
+
445
+ const authulaClient = createClient({
446
+ url: "http://localhost:8080/auth",
447
+ cookies: ..., // Provide cookie store for SSR if needed, else omit for SPA/Mobile apps
448
+ plugins: [
449
+ new CSRFPlugin({
450
+ cookieName: "csrf_token", // Name of the CSRF token cookie
451
+ headerName: "X-CSRF-TOKEN" // Header name to send the token
452
+ })
453
+ ]
454
+ });
455
+
456
+ // CSRF tokens will be automatically added to mutating requests (POST, PUT, PATCH, DELETE)
457
+ ```
458
+
459
+ ---
460
+
461
+ ### JWT Plugin
462
+
463
+ Handles JWT token operations including refresh.
464
+
465
+ ```typescript
466
+ import { JWTPlugin } from "authula/plugins";
467
+
468
+ const authulaClient = createClient({
469
+ url: "http://localhost:8080/auth",
470
+ plugins: [new JWTPlugin()],
471
+ });
472
+
473
+ // Refresh JWT tokens
474
+ const tokens = await authulaClient.jwt.refreshToken({
475
+ refresh_token: "your-refresh-token",
476
+ });
477
+
478
+ // Get JWKS keys
479
+ const jwksKeys = await authulaClient.jwt.getJWKSKeys();
480
+ ```
481
+
482
+ ---
483
+
484
+ ### Bearer Plugin
485
+
486
+ Provides automatic bearer token handling and refresh.
487
+
488
+ ```typescript
489
+ import { BearerPlugin, JWTPlugin } from "authula/plugins";
490
+
491
+ const authulaClient = createClient({
492
+ url: "http://localhost:8080/auth",
493
+ plugins: [
494
+ new JWTPlugin(),
495
+ new BearerPlugin({
496
+ headerName: "Authorization", // Can be omitted as default is Authorization
497
+ }),
498
+ ],
499
+ });
500
+
501
+ // The Bearer plugin will automatically:
502
+ // 1. Add Authorization: Bearer <token> header to requests
503
+ // 2. Handle token refresh when receiving 401 responses
504
+ // 3. Store tokens in localStorage (access_token and refresh_token)
505
+ ```
506
+
507
+ ---
508
+
509
+ ### Magic Link Plugin
510
+
511
+ Provides flows for passwordless authentication.
512
+
513
+ ```typescript
514
+ import { MagicLinkPlugin } from "authula/plugins";
515
+
516
+ const authulaClient = createClient({
517
+ url: "http://localhost:8080/auth",
518
+ plugins: [new MagicLinkPlugin()],
519
+ });
520
+
521
+ // Send magic link via email
522
+ await authulaClient.magicLink.signIn({
523
+ email: "john.doe@example.com",
524
+ name: "John Doe", // Optional name
525
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
526
+ });
527
+
528
+ // Verify magic link token
529
+ await authulaClient.magicLink.verify({
530
+ token: "magic-link-token",
531
+ callbackUrl: "http://localhost:3000/callback", // Optional callback URL
532
+ });
533
+
534
+ // Exchange token from verify endpoint for session
535
+ await authulaClient.magicLink.exchange({
536
+ token: "magic-link-token",
537
+ });
538
+ ```
539
+
540
+ ## Advanced Configuration
541
+
542
+ ### Fetch Options
543
+
544
+ Configure fetch behavior with timeout and other options:
545
+
546
+ ```typescript
547
+ const authulaClient = createClient({
548
+ url: "http://localhost:8080/auth",
549
+ fetchOptions: {
550
+ abortTimeout: 30, // Timeout in seconds
551
+ },
552
+ plugins: [
553
+ // your plugins
554
+ ],
555
+ });
556
+ ```
557
+
558
+ ### Custom Hooks
559
+
560
+ Add custom before/after fetch hooks for advanced customization:
561
+
562
+ ```typescript
563
+ const authulaClient = createClient({
564
+ url: "http://localhost:8080/auth",
565
+ plugins: [new EmailPasswordPlugin()],
566
+ });
567
+
568
+ // Register a before fetch hook
569
+ authulaClient.registerBeforeFetch(async (ctx) => {
570
+ console.log(`Making request to: ${ctx.url}`);
571
+ // Modify context if needed
572
+ ctx.init.headers = {
573
+ ...ctx.init.headers,
574
+ "X-Custom-Header": "value",
575
+ };
576
+ });
577
+
578
+ // Register an after fetch hook
579
+ authulaClient.registerAfterFetch(async (ctx, response) => {
580
+ console.log(`Received response: ${response.status}`);
581
+ if (response.status >= 400) {
582
+ // Handle error
583
+ }
584
+ });
585
+ ```
586
+
587
+ ## API Reference
588
+
589
+ ### Client Configuration
590
+
591
+ ```typescript
592
+ {
593
+ // Base URL of your Authula server
594
+ url: string,
595
+ // Optional fetch configuration
596
+ fetchOptions?: {
597
+ // Request timeout in seconds
598
+ abortTimeout?: number
599
+ },
600
+ // Cookie provider for SSR compatibility
601
+ cookies?: () => CookieStore
602
+ }
603
+ ```
604
+
605
+ ## Error Handling
606
+
607
+ All methods return promises that can be caught for error handling:
608
+
609
+ ```typescript
610
+ try {
611
+ const response = await authulaClient.emailPassword.signIn({
612
+ email: "user@example.com",
613
+ password: "password",
614
+ });
615
+ console.log("Signed in successfully", response);
616
+ } catch (error) {
617
+ console.error("Sign in failed:", error);
618
+ }
619
+ ```
620
+
621
+ ## Contributing
622
+
623
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
624
+
625
+ ## License
626
+
627
+ This project is licensed under the Apache 2.0 License - see the LICENSE file for details.