tenneo-auth-plugin 0.1.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,596 @@
1
+ # @tenneo/auth-plugin
2
+
3
+ Production-ready ReactJS authentication plugin using TypeScript with full OAuth 2.0 PKCE lifecycle support.
4
+
5
+ > **Note:** This is an internal scoped package with restricted access.
6
+
7
+ ## Features
8
+
9
+ - Full OAuth 2.0 PKCE flow implementation
10
+ - Multi-tenant support via tenant code resolution
11
+ - Automatic token refresh
12
+ - Session-based storage with namespacing
13
+ - React hooks and Context API integration
14
+ - TypeScript with strict typing
15
+ - Configurable storage adapters (session, cookie, memory)
16
+ - Auth engine architecture for advanced use cases
17
+ - Protected route component
18
+ - Callback handling component
19
+
20
+ ## Installation
21
+
22
+ ### Installation from npm registry (stable)
23
+
24
+ ```bash
25
+ npm install @tenneo/auth-plugin
26
+ ```
27
+
28
+ ### Installation from npm registry (beta)
29
+
30
+ ```bash
31
+ npm install @tenneo/auth-plugin@beta
32
+ ```
33
+
34
+ ### Installation from hosted .tgz file (without pack)
35
+
36
+ If the plugin is hosted on a server as a `.tgz` file, install directly from the URL:
37
+
38
+ ```bash
39
+ npm install https://your-server.com/path/tenneo-auth-plugin-0.1.0-beta.1.tgz
40
+ ```
41
+
42
+ Or add it to your `package.json`:
43
+
44
+ ```json
45
+ {
46
+ "dependencies": {
47
+ "@tenneo/auth-plugin": "https://your-server.com/path/tenneo-auth-plugin-0.1.0-beta.1.tgz"
48
+ }
49
+ }
50
+ ```
51
+
52
+ Then run:
53
+
54
+ ```bash
55
+ npm install
56
+ ```
57
+
58
+ ### Installation with npm pack
59
+
60
+ Use this method to create a distributable package from the source:
61
+
62
+ **Step 1: Build the plugin**
63
+
64
+ ```bash
65
+ cd tenneo-auth-plugin
66
+ npm install
67
+ npm run build
68
+ ```
69
+
70
+ **Step 2: Create the package**
71
+
72
+ ```bash
73
+ npm pack
74
+ ```
75
+
76
+ This creates a file like `tenneo-auth-plugin-0.1.0-beta.1.tgz` in the current directory.
77
+
78
+ **Step 3: Install in your application**
79
+
80
+ Copy the `.tgz` file to your application directory and install:
81
+
82
+ ```bash
83
+ npm install ./tenneo-auth-plugin-0.1.0-beta.1.tgz
84
+ ```
85
+
86
+ Or specify the full path:
87
+
88
+ ```bash
89
+ npm install /path/to/tenneo-auth-plugin-0.1.0-beta.1.tgz
90
+ ```
91
+
92
+ ### Installation with local source (development)
93
+
94
+ Use this method during development to link the plugin directly without packing:
95
+
96
+ **Method 1: npm link (recommended for development)**
97
+
98
+ ```bash
99
+ # In the plugin directory
100
+ cd tenneo-auth-plugin
101
+ npm install
102
+ npm run build
103
+ npm link
104
+
105
+ # In your application directory
106
+ cd your-application
107
+ npm link @tenneo/auth-plugin
108
+ ```
109
+
110
+ To unlink when done:
111
+
112
+ ```bash
113
+ # In your application directory
114
+ npm unlink @tenneo/auth-plugin
115
+
116
+ # In the plugin directory
117
+ npm unlink
118
+ ```
119
+
120
+ **Method 2: File path in package.json**
121
+
122
+ Add the local path to your application's `package.json`:
123
+
124
+ ```json
125
+ {
126
+ "dependencies": {
127
+ "@tenneo/auth-plugin": "file:../tenneo-auth-plugin"
128
+ }
129
+ }
130
+ ```
131
+
132
+ Then run:
133
+
134
+ ```bash
135
+ npm install
136
+ ```
137
+
138
+ **Method 3: Direct file install**
139
+
140
+ ```bash
141
+ npm install ../tenneo-auth-plugin
142
+ ```
143
+
144
+ > **Note:** For local development methods, ensure you run `npm run build` in the plugin directory whenever you make changes to the plugin source code.
145
+
146
+ ## Quick Start
147
+
148
+ ### 1. Wrap your app with AuthProvider
149
+
150
+ **Basic Usage:**
151
+
152
+ ```tsx
153
+ import { AuthProvider } from '@tenneo/auth-plugin';
154
+
155
+ function App() {
156
+ return (
157
+ <AuthProvider tenantCode="your-tenant-code">
158
+ <YourApp />
159
+ </AuthProvider>
160
+ );
161
+ }
162
+ ```
163
+
164
+ **With App ID (for storage namespacing):**
165
+
166
+ ```tsx
167
+ import { AuthProvider } from '@tenneo/auth-plugin';
168
+
169
+ function App() {
170
+ return (
171
+ <AuthProvider
172
+ tenantCode="your-tenant-code"
173
+ appId="my-app"
174
+ >
175
+ <YourApp />
176
+ </AuthProvider>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ### 2. Use authentication in your components
182
+
183
+ ```tsx
184
+ import { useAuth } from '@tenneo/auth-plugin';
185
+
186
+ function MyComponent() {
187
+ const { isAuthenticated, accessToken, isLoading, logout } = useAuth();
188
+
189
+ if (isLoading) {
190
+ return <div>Loading...</div>;
191
+ }
192
+
193
+ if (!isAuthenticated) {
194
+ return <div>Not authenticated</div>;
195
+ }
196
+
197
+ return (
198
+ <div>
199
+ <p>Authenticated!</p>
200
+ <button onClick={logout}>Logout</button>
201
+ </div>
202
+ );
203
+ }
204
+ ```
205
+
206
+ ### 3. Setup callback route
207
+
208
+ Add the `AuthCallback` component to handle OAuth redirects:
209
+
210
+ ```tsx
211
+ import { AuthCallback } from '@tenneo/auth-plugin';
212
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
213
+
214
+ function App() {
215
+ return (
216
+ <AuthProvider tenantCode="your-tenant-code">
217
+ <BrowserRouter>
218
+ <Routes>
219
+ <Route path="/callback" element={<AuthCallback successPath="/dashboard" />} />
220
+ <Route path="/dashboard" element={<Dashboard />} />
221
+ {/* ... other routes */}
222
+ </Routes>
223
+ </BrowserRouter>
224
+ </AuthProvider>
225
+ );
226
+ }
227
+ ```
228
+
229
+ ### 4. Protect routes (optional)
230
+
231
+ ```tsx
232
+ import { ProtectedRoute } from '@tenneo/auth-plugin';
233
+
234
+ function App() {
235
+ return (
236
+ <AuthProvider tenantCode="your-tenant-code">
237
+ <BrowserRouter>
238
+ <Routes>
239
+ <Route path="/callback" element={<AuthCallback />} />
240
+ <Route
241
+ path="/dashboard"
242
+ element={
243
+ <ProtectedRoute fallback={<div>Loading...</div>}>
244
+ <Dashboard />
245
+ </ProtectedRoute>
246
+ }
247
+ />
248
+ </Routes>
249
+ </BrowserRouter>
250
+ </AuthProvider>
251
+ );
252
+ }
253
+ ```
254
+
255
+ ## AuthProvider Props
256
+
257
+ | Prop | Type | Default | Description |
258
+ |------|------|---------|-------------|
259
+ | `tenantCode` | `string` | - | Tenant code used to resolve tenant configuration via API |
260
+ | `appId` | `string` | `''` | App identifier for storage namespacing |
261
+ | `autoInit` | `boolean` | `true` | Auto-initialize authentication on mount |
262
+ | `storageAdapter` | `StorageAdapter` | `null` | Custom storage adapter (defaults to secure session storage) |
263
+
264
+ ### Configuration Priority
265
+
266
+ 1. **Props** - Values passed to `AuthProvider`
267
+ 2. **Window config** - `window.__AUTH_CONFIG__` or `window.__APP_CONFIG__`
268
+ 3. **Environment variables** - `VITE_*` prefixed variables
269
+ 4. **Defaults** - Built-in fallback values
270
+
271
+ ## API Reference
272
+
273
+ ### Hooks
274
+
275
+ #### `useAuth()`
276
+
277
+ Returns authentication state and methods:
278
+
279
+ ```tsx
280
+ const {
281
+ isAuthenticated: boolean; // Whether user is authenticated
282
+ accessToken: string | null; // Current access token
283
+ isLoading: boolean; // Loading state
284
+ error: string | null; // Error message if any
285
+ login: () => Promise<void>; // Trigger login flow
286
+ logout: () => Promise<void>; // Logout and clear session
287
+ refreshToken: () => Promise<string>; // Refresh access token
288
+ getAccessToken: () => Promise<string>; // Get valid token (auto-refresh)
289
+ } = useAuth();
290
+ ```
291
+
292
+ #### `useAuthInit()`
293
+
294
+ Manual initialization hook for advanced use cases:
295
+
296
+ ```tsx
297
+ const { isLoading, error, init } = useAuthInit();
298
+
299
+ // Manually trigger initialization
300
+ await init();
301
+ ```
302
+
303
+ ### Components
304
+
305
+ #### `AuthCallback`
306
+
307
+ Handles OAuth callback and token exchange:
308
+
309
+ ```tsx
310
+ <AuthCallback
311
+ loginPath="/login" // Redirect path on error (default: '/login')
312
+ successPath="/dashboard" // Redirect path on success (default: '/dashboard')
313
+ homePath="/" // Home path fallback (default: '/')
314
+ />
315
+ ```
316
+
317
+ #### `ProtectedRoute`
318
+
319
+ Wraps components that require authentication:
320
+
321
+ ```tsx
322
+ <ProtectedRoute
323
+ fallback={<Loading />} // Component to show while checking auth
324
+ autoLogin={true} // Auto-trigger login if not authenticated (default: true)
325
+ >
326
+ <ProtectedContent />
327
+ </ProtectedRoute>
328
+ ```
329
+
330
+ ### Core Functions
331
+
332
+ #### `bootstrap()`
333
+
334
+ Initializes authentication flow (called automatically by AuthProvider):
335
+
336
+ ```tsx
337
+ import { bootstrap } from '@tenneo/auth-plugin';
338
+ await bootstrap();
339
+ ```
340
+
341
+ #### `getAccessToken()`
342
+
343
+ Gets current access token (auto-refreshes if expired):
344
+
345
+ ```tsx
346
+ import { getAccessToken } from '@tenneo/auth-plugin';
347
+ const token = await getAccessToken();
348
+ ```
349
+
350
+ #### `logout()`
351
+
352
+ Logs out user and clears session:
353
+
354
+ ```tsx
355
+ import { logout } from '@tenneo/auth-plugin';
356
+ await logout();
357
+ ```
358
+
359
+ ### Configuration
360
+
361
+ #### `Config` Object
362
+
363
+ Access resolved configuration values:
364
+
365
+ ```tsx
366
+ import { Config } from '@tenneo/auth-plugin';
367
+
368
+ Config.getAuthServerUrl(); // OAuth server URL
369
+ Config.getClientId(); // OAuth client ID
370
+ Config.getTenantId(); // Resolved tenant ID
371
+ Config.getClientCode(); // Tenant code
372
+ Config.getRedirectUri(); // OAuth redirect URI
373
+ Config.getBasePath(); // App base path
374
+ Config.getAll(); // All config as object
375
+ ```
376
+
377
+ #### `setConfig()`
378
+
379
+ Override runtime configuration:
380
+
381
+ ```tsx
382
+ import { setConfig } from '@tenneo/auth-plugin';
383
+
384
+ setConfig({
385
+ clientId: 'custom-client-id',
386
+ tenantId: 'custom-tenant-id',
387
+ basePath: '/my-app',
388
+ redirectUri: 'https://myapp.com/callback',
389
+ });
390
+ ```
391
+
392
+ ### Storage Adapters
393
+
394
+ Create custom storage adapters or use built-in ones:
395
+
396
+ ```tsx
397
+ import {
398
+ createSecureSessionStorageAdapter,
399
+ createCookieStorageAdapter,
400
+ createMemoryStorageAdapter,
401
+ setStorageContext,
402
+ } from '@tenneo/auth-plugin';
403
+
404
+ // Session storage (default)
405
+ const sessionAdapter = createSecureSessionStorageAdapter({
406
+ tenantId: 'tenant-123',
407
+ appId: 'my-app'
408
+ });
409
+
410
+ // Cookie storage
411
+ const cookieAdapter = createCookieStorageAdapter({
412
+ tenantId: 'tenant-123',
413
+ appId: 'my-app'
414
+ });
415
+
416
+ // Memory storage (for testing)
417
+ const memoryAdapter = createMemoryStorageAdapter({
418
+ tenantId: 'tenant-123',
419
+ appId: 'my-app'
420
+ });
421
+
422
+ // Use custom adapter
423
+ <AuthProvider tenantCode="abc" storageAdapter={cookieAdapter}>
424
+ <App />
425
+ </AuthProvider>
426
+ ```
427
+
428
+ ### Auth Engine (Advanced)
429
+
430
+ For advanced use cases, create and manage auth engine directly:
431
+
432
+ ```tsx
433
+ import { createAuthEngine, type AuthEngine } from '@tenneo/auth-plugin';
434
+
435
+ const engine = createAuthEngine(runtime);
436
+
437
+ // Subscribe to state changes
438
+ const unsubscribe = engine.subscribe((state) => {
439
+ console.log('Auth state:', state.status);
440
+ });
441
+
442
+ // Initialize
443
+ await engine.init();
444
+
445
+ // Get current state
446
+ const state = engine.getState();
447
+
448
+ // Manual operations
449
+ await engine.login();
450
+ await engine.logout();
451
+ await engine.refresh();
452
+ const token = await engine.getAccessToken();
453
+ ```
454
+
455
+ ### Events
456
+
457
+ Subscribe to authentication events:
458
+
459
+ ```tsx
460
+ import {
461
+ subscribeAuthEvent,
462
+ emitAuthEvent,
463
+ AuthEventNames
464
+ } from '@tenneo/auth-plugin';
465
+
466
+ // Subscribe to events
467
+ const unsubscribe = subscribeAuthEvent('auth:login', (payload) => {
468
+ console.log('User logged in');
469
+ });
470
+
471
+ // Available events
472
+ AuthEventNames.LOGIN // 'auth:login'
473
+ AuthEventNames.LOGOUT // 'auth:logout'
474
+ AuthEventNames.REFRESH // 'auth:refresh'
475
+ AuthEventNames.ERROR // 'auth:error'
476
+ ```
477
+
478
+ ## Environment Variables
479
+
480
+ Configure the plugin via environment variables (Vite apps use `VITE_` prefix):
481
+
482
+ ```env
483
+ # Auth Server URL - Your OAuth authorization server
484
+ VITE_AUTH_SERVER_URL=https://auth.example.com
485
+
486
+ # API base URLs
487
+ VITE_API_TENANT_RESOLUTION_BASE_URL=https://api.example.com/api/v1
488
+ VITE_API_AUTH_BASE_URL=https://auth.example.com
489
+
490
+ # Default values (optional)
491
+ VITE_CLIENT_ID=your-client-id
492
+ VITE_TENANT_ID=your-tenant-id
493
+ VITE_CLIENTCODE=your-tenant-code
494
+ ```
495
+
496
+ See `.env.example` for a complete template.
497
+
498
+ ## Window Configuration
499
+
500
+ For runtime configuration without rebuilding:
501
+
502
+ ```html
503
+ <script>
504
+ window.__AUTH_CONFIG__ = {
505
+ clientId: 'runtime-client-id',
506
+ tenantId: 'runtime-tenant-id',
507
+ basePath: '/my-app',
508
+ };
509
+ </script>
510
+ ```
511
+
512
+ ## Architecture
513
+
514
+ ```
515
+ @tenneo/auth-plugin/
516
+ └── src/
517
+ ├── index.ts # Main exports
518
+ ├── application/ # Application layer (bootstrap, flows, engine)
519
+ │ ├── bootstrap.ts # Auth initialization
520
+ │ ├── engine/ # Auth state machine
521
+ │ ├── flows/ # OAuth flow implementations
522
+ │ └── events/ # Event system
523
+ ├── infrastructure/ # Infrastructure adapters
524
+ │ ├── storage/ # Storage adapters
525
+ │ ├── runtime/ # Runtime context
526
+ │ ├── tenant/ # Tenant resolution
527
+ │ └── watchers/ # Storage/cookie watchers
528
+ ├── strategies/ # Auth strategies
529
+ │ └── oauth2-pkce/ # OAuth 2.0 PKCE implementation
530
+ ├── context/ # React Context
531
+ ├── components/ # React components
532
+ ├── hooks/ # React hooks
533
+ ├── config/ # Configuration
534
+ ├── domain/ # Domain types
535
+ └── utils/ # Utilities
536
+ ```
537
+
538
+ ## Storage Keys
539
+
540
+ Authentication data is stored with namespaced keys (`auth_{tenantId}_{appId}_*`):
541
+
542
+ - `*_access_token` - Current access token
543
+ - `*_refresh_token` - Refresh token
544
+ - `*_expires_at` - Token expiration timestamp
545
+ - `*_id_token` - ID token (if provided)
546
+ - `*_tenant_id` - Resolved tenant ID
547
+ - `*_tenant_key` - Tenant code/key
548
+ - `*_code_verifier` - PKCE code verifier (temporary)
549
+ - `*_state` - OAuth state parameter (temporary)
550
+
551
+ ## TypeScript Types
552
+
553
+ All types are exported for use in your application:
554
+
555
+ ```tsx
556
+ import type {
557
+ TenantConfig,
558
+ TokenResponse,
559
+ AuthState,
560
+ AuthContextValue,
561
+ EnvConfig,
562
+ AuthTokens,
563
+ StorageAdapter,
564
+ AuthEngine,
565
+ AuthEngineState,
566
+ SDKConfig,
567
+ RuntimeConfigOverrides,
568
+ } from '@tenneo/auth-plugin';
569
+ ```
570
+
571
+ ## Security
572
+
573
+ - PKCE (Proof Key for Code Exchange) prevents authorization code interception
574
+ - Tokens stored in sessionStorage (cleared on tab close)
575
+ - Automatic HTTPS enforcement in production
576
+ - State parameter validation prevents CSRF attacks
577
+ - Secure cookie options for cookie-based storage
578
+
579
+ npm run clean # Remove dist/
580
+ npm run build # Build with tsup
581
+ npm run dev # Watch mode
582
+ npm run type-check # TypeScript check
583
+ npm run release:beta # Publish beta version (restricted)
584
+ npm run release:minor # Publish minor version (restricted)
585
+ npm run release:major # Publish major version (restricted)
586
+
587
+ Publishing
588
+ npm login
589
+ npm run release:beta # First beta release
590
+
591
+ Consumers install via:
592
+ npm install @tenneo/auth-plugin@beta
593
+
594
+ ## License
595
+
596
+ MIT