@dainprotocol/oauth2-token-manager 0.1.0 โ†’ 0.1.1

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
@@ -2,6 +2,25 @@
2
2
 
3
3
  A powerful, storage-agnostic OAuth2 token management library built for scalable multi-system architectures. This library provides comprehensive token lifecycle management with pluggable storage adapters, built-in security features, and support for multiple OAuth2 providers.
4
4
 
5
+ ## ๐Ÿ“‹ Table of Contents
6
+
7
+ - [Features](#-features)
8
+ - [Installation](#-installation)
9
+ - [Quick Start](#-quick-start)
10
+ - [Granular API (Advanced)](#-granular-api-advanced)
11
+ - [Context-Aware Resolution](#context-aware-resolution)
12
+ - [User Management](#user-management)
13
+ - [Token Operations](#token-operations)
14
+ - [Bulk Operations](#bulk-operations)
15
+ - [System & Scope Management](#system--scope-management)
16
+ - [Architecture](#-architecture)
17
+ - [API Reference](#-api-reference)
18
+ - [Storage Adapters](#storage-adapters)
19
+ - [Provider Configuration](#provider-configuration)
20
+ - [Advanced Features](#-advanced-features)
21
+ - [Examples](#-examples)
22
+ - [Production Deployment](#-production-deployment)
23
+
5
24
  ## ๐Ÿš€ Features
6
25
 
7
26
  - **๐Ÿ”Œ Storage Agnostic**: Use any storage backend (In-Memory, PostgreSQL, or build your own adapter)
@@ -12,6 +31,7 @@ A powerful, storage-agnostic OAuth2 token management library built for scalable
12
31
  - **๐Ÿ‘ค User Management**: Comprehensive user lifecycle with email/external ID support
13
32
  - **๐Ÿ“ง Profile Integration**: Automatic profile fetching from OAuth providers
14
33
  - **๐ŸŽฏ Flexible Scoping**: Fine-grained permission management
34
+ - **๐ŸŽจ Default Configuration**: Automatic default system and scope creation
15
35
  - **๐Ÿ’ก Developer Friendly**: Both context-managed and granular APIs
16
36
  - **๐Ÿงช Fully Tested**: Comprehensive test coverage with Vitest
17
37
 
@@ -26,6 +46,9 @@ npm install @dainprotocol/oauth2-token-manager
26
46
  ```bash
27
47
  # PostgreSQL adapter
28
48
  npm install @dainprotocol/oauth2-storage-postgres
49
+
50
+ # Drizzle adapter (supports PostgreSQL, MySQL, SQLite)
51
+ npm install @dainprotocol/oauth2-storage-drizzle
29
52
  ```
30
53
 
31
54
  ## ๐Ÿš€ Quick Start
@@ -70,44 +93,219 @@ const { url, state } = await oauth.authorize({
70
93
  // Handle callback
71
94
  const result = await oauth.handleCallback(code, state);
72
95
  console.log('User authenticated:', result.userId);
96
+
97
+ // === NEW: Simplified Granular API (V2) ===
98
+ // Access tokens without specifying system/scope IDs
99
+ const accessToken = await oauth.granularV2.getAccessToken({
100
+ email: 'user@example.com',
101
+ provider: 'google',
102
+ });
73
103
  ```
74
104
 
75
- ### Advanced Setup with Custom Storage
105
+ ## ๐ŸŽฏ Granular API (Advanced)
106
+
107
+ The granular API (`oauth.granularV2`) provides full control over the token hierarchy with intelligent system and scope resolution. It's the recommended way to interact with tokens when you need fine-grained control.
108
+
109
+ ### Context-Aware Resolution
110
+
111
+ The API follows this priority order for resolving system and scope:
112
+
113
+ 1. **Explicit parameters** - If you provide systemId/scopeId in the method call, those are used
114
+ 2. **Context from OAuth2Client** - If not provided, it checks the current context (useSystem/setDefaultScope)
115
+ 3. **Default system/scope** - If no context is set, it falls back to the default system and scope
76
116
 
77
117
  ```typescript
78
- import { OAuth2Client } from '@dainprotocol/oauth2-token-manager';
79
- import { PostgresStorageFactory } from '@dainprotocol/oauth2-storage-postgres';
118
+ // === Context-Aware Usage ===
80
119
 
81
- // Custom storage adapter
82
- const storage = await PostgresStorageFactory.create({
83
- host: 'localhost',
84
- port: 5432,
85
- username: 'oauth2_user',
86
- password: 'secure_password',
87
- database: 'oauth2_db',
120
+ // Set context in main client
121
+ await oauth.useSystem('production-system');
122
+ oauth.setDefaultScope('api-access');
123
+
124
+ // Granular V2 will now use this context automatically
125
+ const user = await oauth.granularV2.getOrCreateUser({
126
+ email: 'user@example.com',
127
+ options: { metadata: { role: 'user' } },
128
+ // No need to specify systemId - uses 'production-system' from context
88
129
  });
89
130
 
90
- const oauth = new OAuth2Client({
91
- storage,
92
- providers: {
93
- google: {
94
- /* config */
95
- },
96
- github: {
97
- /* config */
98
- },
131
+ // Token operations use the context scope
132
+ const token = await oauth.granularV2.getAccessToken({
133
+ email: 'user@example.com',
134
+ provider: 'google',
135
+ // No need to specify scopeId - uses 'api-access' from context
136
+ });
137
+
138
+ // Override with explicit parameters when needed
139
+ const differentToken = await oauth.granularV2.getAccessToken({
140
+ email: 'user@example.com',
141
+ provider: 'google',
142
+ systemId: 'another-system', // This takes precedence
143
+ scopeId: 'different-scope', // This takes precedence
144
+ });
145
+ ```
146
+
147
+ ### User Management
148
+
149
+ ```typescript
150
+ // Create or get users with optional system specification
151
+ const user = await oauth.granularV2.getOrCreateUser({
152
+ email: 'user@example.com',
153
+ options: { metadata: { role: 'admin' } },
154
+ // systemId is optional - uses context or defaults
155
+ });
156
+
157
+ // Find users by various criteria
158
+ const userByEmail = await oauth.granularV2.findUserByEmail({
159
+ email: 'user@example.com',
160
+ });
161
+
162
+ const userByExternalId = await oauth.granularV2.findUserByExternalId({
163
+ externalId: 'external-123',
164
+ });
165
+
166
+ // Get all users in a system
167
+ const systemUsers = await oauth.granularV2.getUsersBySystem();
168
+ // Or specify a different system
169
+ const otherSystemUsers = await oauth.granularV2.getUsersBySystem('other-system-id');
170
+ ```
171
+
172
+ ### Token Operations
173
+
174
+ ```typescript
175
+ // Save new tokens
176
+ const savedToken = await oauth.granularV2.saveToken({
177
+ userId: user.id,
178
+ provider: 'google',
179
+ email: 'user@example.com',
180
+ token: oauthToken,
181
+ // systemId and scopeId are optional
182
+ });
183
+
184
+ // Query tokens with flexible parameters
185
+ const userTokens = await oauth.granularV2.getTokens({
186
+ userId: user.id,
187
+ });
188
+
189
+ const providerTokens = await oauth.granularV2.getTokens({
190
+ userId: user.id,
191
+ provider: 'google',
192
+ });
193
+
194
+ const emailTokens = await oauth.granularV2.getTokens({
195
+ email: 'user@example.com',
196
+ // systemId resolved from context or defaults
197
+ });
198
+
199
+ // Get valid tokens with auto-refresh
200
+ const validToken = await oauth.granularV2.getValidToken({
201
+ email: 'user@example.com',
202
+ provider: 'google',
203
+ options: {
204
+ autoRefresh: true,
205
+ refreshBuffer: 5, // Refresh 5 minutes before expiry
206
+ },
207
+ });
208
+
209
+ // Get access token directly
210
+ const accessToken = await oauth.granularV2.getAccessToken({
211
+ userId: user.id,
212
+ provider: 'google',
213
+ });
214
+
215
+ // Execute with valid token
216
+ await oauth.granularV2.withValidToken(
217
+ {
218
+ email: 'user@example.com',
219
+ provider: 'google',
99
220
  },
221
+ async (accessToken) => {
222
+ // Make API calls with the token
223
+ const response = await fetch('https://api.example.com/data', {
224
+ headers: { Authorization: `Bearer ${accessToken}` },
225
+ });
226
+ return response.json();
227
+ },
228
+ );
229
+ ```
230
+
231
+ ### Bulk Operations
232
+
233
+ ```typescript
234
+ // Get all valid tokens for a user
235
+ const allUserTokens = await oauth.granularV2.getAllValidTokens({
236
+ userId: user.id,
237
+ options: { autoRefresh: true },
238
+ });
239
+
240
+ // Get all valid tokens for an email
241
+ const allEmailTokens = await oauth.granularV2.getAllValidTokens({
242
+ email: 'user@example.com',
243
+ });
244
+
245
+ // Filter by provider
246
+ const googleTokens = await oauth.granularV2.getAllValidTokens({
247
+ email: 'user@example.com',
248
+ provider: 'google',
249
+ });
250
+
251
+ // Check token existence
252
+ const hasToken = await oauth.granularV2.hasToken({
253
+ email: 'user@example.com',
254
+ provider: 'google',
255
+ });
256
+
257
+ // Delete tokens
258
+ await oauth.granularV2.deleteTokens({
259
+ email: 'user@example.com',
260
+ provider: 'google',
261
+ });
262
+
263
+ // Delete all tokens for a user
264
+ await oauth.granularV2.deleteTokens({
265
+ userId: user.id,
100
266
  });
267
+ ```
268
+
269
+ ### System & Scope Management
101
270
 
102
- // Create system and scopes
103
- const system = await oauth.createSystem('MyApp');
104
- const scope = await oauth.createScope('api-access', {
271
+ ```typescript
272
+ // Get current defaults
273
+ const defaultSystem = await oauth.granularV2.getSystem();
274
+ const defaultScope = await oauth.granularV2.getScope();
275
+
276
+ // Create new scope in current/default system
277
+ const newScope = await oauth.granularV2.createScope({
278
+ name: 'api-access',
105
279
  type: 'access',
106
- permissions: ['read:profile', 'write:data'],
280
+ permissions: ['read', 'write'],
107
281
  isolated: true,
108
282
  });
283
+
284
+ // Create scope in specific system
285
+ const anotherScope = await oauth.granularV2.createScope({
286
+ systemId: 'specific-system-id',
287
+ name: 'admin-access',
288
+ type: 'access',
289
+ permissions: ['*'],
290
+ });
291
+
292
+ // Get all scopes in current system
293
+ const scopes = await oauth.granularV2.getScopesBySystem();
294
+
295
+ // Initialize defaults explicitly if needed
296
+ const { system, scope } = await oauth.granularV2.ensureDefaults();
109
297
  ```
110
298
 
299
+ ### Why Use Granular API?
300
+
301
+ The granular API is ideal for:
302
+
303
+ - **Single-system applications** - Simplified usage with automatic defaults
304
+ - **Multi-tenant applications** - Easy context switching between systems
305
+ - **Backend services** - Stateless operations with explicit parameters
306
+ - **Complex token management** - Full control over token lifecycle
307
+ - **Gradual migration** - Start simple, add complexity as needed
308
+
111
309
  ## ๐Ÿ—๏ธ Architecture
112
310
 
113
311
  ### Core Components
@@ -116,8 +314,8 @@ const scope = await oauth.createScope('api-access', {
116
314
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
117
315
  โ”‚ OAuth2Client โ”‚
118
316
  โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
119
- โ”‚ โ”‚ Context API โ”‚ โ”‚ Granular API โ”‚ โ”‚
120
- โ”‚ โ”‚ (Simplified) โ”‚ โ”‚ (Full Control) โ”‚ โ”‚
317
+ โ”‚ โ”‚ Context API โ”‚ โ”‚ Granular API V2 โ”‚ โ”‚
318
+ โ”‚ โ”‚ (Simplified) โ”‚ โ”‚ (Full Control + Smart) โ”‚ โ”‚
121
319
  โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
122
320
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
123
321
  โ”‚
@@ -181,19 +379,67 @@ interface UserToken {
181
379
  5. **Email Uniqueness**: For the same provider, a user cannot have multiple tokens with the same email (validated via profile fetcher)
182
380
  6. **Cross-Provider Emails**: The same email can exist across different providers
183
381
 
382
+ ### Default System and Scope
383
+
384
+ The library provides automatic initialization of a default system and scope for simplified usage:
385
+
386
+ ```typescript
387
+ import {
388
+ DEFAULT_SYSTEM_NAME,
389
+ DEFAULT_SCOPE_NAME,
390
+ initializeDefaults,
391
+ } from '@dainprotocol/oauth2-token-manager';
392
+
393
+ // Automatic initialization
394
+ const oauth = new OAuth2Client({
395
+ providers: {
396
+ /* ... */
397
+ },
398
+ });
399
+
400
+ // If no system/scope is set, they will be automatically initialized on first use
401
+ const { url, state } = await oauth.authorize({ provider: 'google' });
402
+
403
+ // Or manually initialize defaults
404
+ await oauth.initializeDefaults();
405
+
406
+ // Check if defaults exist
407
+ const defaultSystem = await oauth.getDefaultSystem();
408
+ const defaultScope = await oauth.getDefaultScope();
409
+
410
+ // Default constants
411
+ console.log(DEFAULT_SYSTEM_NAME); // 'oauth2-token-manager-default-system'
412
+ console.log(DEFAULT_SCOPE_NAME); // 'oauth2-token-manager-default-scope'
413
+ ```
414
+
415
+ The default system and scope are created with:
416
+
417
+ - **System**: Name-based identification, includes metadata `{ isDefault: true }`
418
+ - **Scope**: Type 'access', permissions ['read', 'write'], not isolated
419
+
184
420
  ## ๐Ÿ“š API Reference
185
421
 
186
422
  ### OAuth2Client
187
423
 
188
424
  The main client class providing both context-managed and granular APIs.
189
425
 
190
- #### Context-Managed API (Recommended)
426
+ #### Context-Managed API (Recommended for Simple Use Cases)
191
427
 
192
428
  ```typescript
193
429
  // System management
194
430
  await oauth.createSystem('MyApp');
431
+ await oauth.getOrCreateSystem('MyApp'); // Idempotent - returns existing if found
195
432
  await oauth.useSystem(systemId);
196
433
 
434
+ // Scope management
435
+ await oauth.createScope('api-access');
436
+ await oauth.getOrCreateScope('api-access'); // Idempotent - returns existing if found
437
+
438
+ // Default system/scope (automatic initialization)
439
+ await oauth.initializeDefaults(); // Creates default system and scope if they don't exist
440
+ const defaultSystem = await oauth.getDefaultSystem();
441
+ const defaultScope = await oauth.getDefaultScope();
442
+
197
443
  // User management
198
444
  const user = await oauth.getOrCreateUser({ email: 'user@example.com' });
199
445
  await oauth.useUser(userId);
@@ -218,89 +464,6 @@ const allTokens = await oauth.getAllValidTokensForUser(userId);
218
464
  await oauth.revokeTokens('google'); // Revokes for current user/scope/provider
219
465
  ```
220
466
 
221
- #### Granular API (Advanced)
222
-
223
- The granular API provides full control over the token hierarchy:
224
-
225
- ```typescript
226
- // === User-Centric Token Queries (Primary Key: User) ===
227
-
228
- // Get ALL tokens for a user across all scopes/providers
229
- const userTokens = await oauth.granular.getTokensByUser(userId);
230
-
231
- // Get tokens for user in specific scope (across all providers)
232
- const scopeTokens = await oauth.granular.getTokensByUserAndScope(userId, scopeId);
233
-
234
- // Get tokens for user with specific provider (across all scopes)
235
- const providerTokens = await oauth.granular.getTokensByUserAndProvider(userId, 'google');
236
-
237
- // Get tokens for user/scope/provider combination (can be multiple!)
238
- const specificTokens = await oauth.granular.getTokensByUserScopeProvider(userId, scopeId, 'google');
239
-
240
- // === Cross-User Queries (System/Scope Level) ===
241
-
242
- // Get all tokens in a scope across all users
243
- const scopeAllTokens = await oauth.granular.getTokensByScope(systemId, scopeId);
244
-
245
- // Get all tokens for a provider across all users in system
246
- const providerAllTokens = await oauth.granular.getTokensByProvider(systemId, 'google');
247
-
248
- // Get all tokens in a system
249
- const systemTokens = await oauth.granular.getTokensBySystem(systemId);
250
-
251
- // === Email-Based Queries ===
252
-
253
- // Find tokens by email (cross-user, cross-provider)
254
- const emailTokens = await oauth.granular.findTokensByEmail('user@example.com', systemId);
255
-
256
- // Find tokens by email in specific scope
257
- const emailScopeTokens = await oauth.granular.findTokensByEmailAndScope(
258
- 'user@example.com',
259
- systemId,
260
- scopeId,
261
- );
262
-
263
- // Find tokens by email for specific provider
264
- const emailProviderTokens = await oauth.granular.findTokensByEmailAndProvider(
265
- 'user@example.com',
266
- systemId,
267
- 'google',
268
- );
269
-
270
- // Find specific token by email/scope/provider (returns single token or null)
271
- const specificToken = await oauth.granular.findTokenByEmailScopeProvider(
272
- 'user@example.com',
273
- systemId,
274
- scopeId,
275
- 'google',
276
- );
277
-
278
- // === Token Operations ===
279
-
280
- // Get valid token for user (auto-refresh, takes first if multiple exist)
281
- const validToken = await oauth.granular.getValidTokenForUser(userId, scopeId, 'google');
282
-
283
- // Get access token for user (convenience method)
284
- const accessToken = await oauth.granular.getAccessTokenForUser(userId, scopeId, 'google');
285
-
286
- // Save new token for user
287
- const savedToken = await oauth.granular.saveTokenForUser(
288
- userId,
289
- systemId,
290
- scopeId,
291
- 'google',
292
- 'user@example.com',
293
- oauthToken,
294
- );
295
-
296
- // === Token Management ===
297
-
298
- // Delete tokens by different criteria
299
- await oauth.granular.deleteTokensByUser(userId); // All tokens for user
300
- await oauth.granular.deleteTokensByUserAndScope(userId, scopeId); // User's tokens in scope
301
- await oauth.granular.deleteTokensByUserAndProvider(userId, 'google'); // User's tokens for provider
302
- ```
303
-
304
467
  ### Storage Adapters
305
468
 
306
469
  #### Built-in Memory Adapter
@@ -327,18 +490,77 @@ const storage = await PostgresStorageFactory.create({
327
490
  });
328
491
  ```
329
492
 
493
+ #### Drizzle Adapter (Multi-Database)
494
+
495
+ ```typescript
496
+ import { DrizzleStorageFactory } from '@dainprotocol/oauth2-storage-drizzle';
497
+
498
+ // PostgreSQL
499
+ const pgStorage = await DrizzleStorageFactory.create({
500
+ type: 'postgresql',
501
+ config: {
502
+ host: 'localhost',
503
+ port: 5432,
504
+ user: 'oauth2_user',
505
+ password: 'password',
506
+ database: 'oauth2_db',
507
+ },
508
+ });
509
+
510
+ // MySQL
511
+ const mysqlStorage = await DrizzleStorageFactory.create({
512
+ type: 'mysql',
513
+ config: {
514
+ host: 'localhost',
515
+ port: 3306,
516
+ user: 'oauth2_user',
517
+ password: 'password',
518
+ database: 'oauth2_db',
519
+ },
520
+ });
521
+
522
+ // SQLite
523
+ const sqliteStorage = await DrizzleStorageFactory.create({
524
+ type: 'sqlite',
525
+ config: {
526
+ filename: './oauth2.db',
527
+ },
528
+ });
529
+ ```
530
+
330
531
  #### Custom Storage Adapter
331
532
 
332
533
  ```typescript
333
534
  import { StorageAdapter } from '@dainprotocol/oauth2-token-manager';
334
535
 
335
536
  class MyCustomAdapter implements StorageAdapter {
537
+ // System operations
336
538
  async createSystem(system) {
337
539
  /* implement */
338
540
  }
541
+ async getOrCreateSystem(system) {
542
+ // Find by name, create if not exists
543
+ /* implement */
544
+ }
339
545
  async getSystem(id) {
340
546
  /* implement */
341
547
  }
548
+
549
+ // Scope operations
550
+ async createScope(scope) {
551
+ /* implement */
552
+ }
553
+ async getOrCreateScope(scope) {
554
+ // Find by systemId + name, create if not exists
555
+ /* implement */
556
+ }
557
+
558
+ // User operations
559
+ async getOrCreateUser(input) {
560
+ // Find by email or externalId, create if not exists
561
+ /* implement */
562
+ }
563
+
342
564
  // ... implement all required methods
343
565
  }
344
566
  ```
@@ -398,64 +620,6 @@ class MyCustomAdapter implements StorageAdapter {
398
620
  }
399
621
  ```
400
622
 
401
- #### Google OAuth2 with Offline Access
402
-
403
- ```typescript
404
- const oauth = new OAuth2Client({
405
- providers: {
406
- google: {
407
- clientId: 'your-client-id',
408
- clientSecret: 'your-client-secret',
409
- redirectUri: 'http://localhost:3000/auth/callback',
410
- scopes: ['profile', 'email'],
411
- // Override default offline access parameters
412
- extraAuthParams: {
413
- access_type: 'offline', // Request refresh token
414
- prompt: 'consent', // Force consent screen
415
- include_granted_scopes: 'true' // Include previously granted scopes
416
- }
417
- }
418
- }
419
- });
420
-
421
- // The library automatically handles refresh tokens
422
- const token = await oauth.getAccessToken('google', {
423
- autoRefresh: true,
424
- refreshBuffer: 5 // Refresh 5 minutes before expiry
425
- });
426
- ```
427
-
428
- #### Customizing Authorization Parameters
429
-
430
- Each provider supports customization through `extraAuthParams` and `additionalParams`:
431
-
432
- ```typescript
433
- {
434
- google: {
435
- // ... other config ...
436
- extraAuthParams: {
437
- access_type: 'offline', // For refresh tokens
438
- prompt: 'select_account', // Force account selection
439
- hd: 'yourdomain.com' // Limit to specific Google Workspace domain
440
- }
441
- },
442
- microsoft: {
443
- // ... other config ...
444
- extraAuthParams: {
445
- prompt: 'select_account',
446
- domain_hint: 'yourdomain.com'
447
- }
448
- }
449
- }
450
- ```
451
-
452
- Available parameters for Google OAuth2:
453
- - `access_type`: 'online' (default) or 'offline' (for refresh tokens)
454
- - `prompt`: 'none', 'consent', 'select_account'
455
- - `include_granted_scopes`: 'true' or 'false'
456
- - `login_hint`: User's email address
457
- - `hd`: Google Workspace domain restriction
458
-
459
623
  ## ๐Ÿ”ง Advanced Features
460
624
 
461
625
  ### Token Auto-Refresh
@@ -463,7 +627,7 @@ Available parameters for Google OAuth2:
463
627
  ```typescript
464
628
  const accessToken = await oauth.getAccessToken('google', {
465
629
  autoRefresh: true,
466
- refreshBuffer: 5, // Refresh 5 minutes before expiry
630
+ refreshBuffer: 10, // Refresh early to avoid expiry
467
631
  expirationBuffer: 30, // Consider expired 30 seconds early
468
632
  });
469
633
  ```
@@ -480,86 +644,6 @@ const result = await oauth.handleCallback(code, state, {
480
644
  });
481
645
  ```
482
646
 
483
- ### Email-Based Operations
484
-
485
- ```typescript
486
- // Get all valid tokens for an email across all providers in a system
487
- // Note: This returns an array since one email can have tokens from multiple providers
488
- const emailTokens = await oauth.getAllValidTokensForEmail('user@example.com', systemId);
489
- // Returns: { provider: string; scopeId: string; token: OAuth2Token; userToken: UserToken }[]
490
-
491
- // Get specific token by email (returns single token or null)
492
- // This enforces the email uniqueness rule within provider/scope
493
- const token = await oauth.getTokenForEmail('user@example.com', systemId, scopeId, 'google');
494
-
495
- // Get valid token for email with auto-refresh
496
- const validToken = await oauth.getValidTokenForEmail(
497
- 'user@example.com',
498
- systemId,
499
- scopeId,
500
- 'google',
501
- { autoRefresh: true },
502
- );
503
-
504
- // Get access token for email
505
- const accessToken = await oauth.getAccessTokenForEmail(
506
- 'user@example.com',
507
- systemId,
508
- scopeId,
509
- 'google',
510
- );
511
-
512
- // Execute with valid token for email
513
- await oauth.withValidTokenForEmail(
514
- 'user@example.com',
515
- systemId,
516
- scopeId,
517
- 'google',
518
- async (accessToken) => {
519
- console.log('Using token for email:', accessToken);
520
- },
521
- );
522
-
523
- // Check if email has token for specific provider/scope
524
- const hasToken = await oauth.hasTokenForEmail('user@example.com', systemId, scopeId, 'google');
525
-
526
- // Revoke tokens for email
527
- await oauth.revokeTokensForEmail('user@example.com', systemId, scopeId, 'google');
528
- ```
529
-
530
- ### User-Centric Operations (Stateless)
531
-
532
- For backend APIs where you have explicit user IDs:
533
-
534
- ```typescript
535
- // Get access token for specific user/scope/provider
536
- // Note: Takes the first (most recent) token if multiple exist
537
- const accessToken = await oauth.getAccessTokenForUser(userId, systemId, scopeId, 'google', {
538
- autoRefresh: true,
539
- });
540
-
541
- // Execute with valid token for specific user
542
- await oauth.withValidTokenForUser(userId, systemId, scopeId, 'google', async (accessToken) => {
543
- // Make API calls with the token
544
- return apiResponse;
545
- });
546
-
547
- // Get all valid tokens for a user with auto-refresh
548
- const userTokens = await oauth.getAllValidTokensForUser(userId, {
549
- autoRefresh: true,
550
- refreshBuffer: 5, // Refresh 5 minutes before expiry
551
- });
552
-
553
- // Check if user has tokens for specific provider/scope
554
- const hasToken = await oauth.hasTokenForUser(userId, systemId, scopeId, 'google');
555
-
556
- // Get user token entity (includes metadata)
557
- const userToken = await oauth.getUserTokenForUser(userId, systemId, scopeId, 'google');
558
-
559
- // Revoke specific tokens
560
- await oauth.revokeTokensForUser(userId, systemId, scopeId, 'google');
561
- ```
562
-
563
647
  ### PKCE Support
564
648
 
565
649
  ```typescript
@@ -582,52 +666,25 @@ const isExpired = oauth.isTokenExpired(token, {
582
666
  const validToken = await oauth.ensureValidToken('google');
583
667
  ```
584
668
 
585
- ## ๐Ÿ”’ Security Features
586
-
587
- ### State Management
588
-
589
- - Cryptographically secure state generation
590
- - Automatic state validation and cleanup
591
- - Configurable state expiration
592
-
593
- ### PKCE (Proof Key for Code Exchange)
594
-
595
- - Built-in PKCE support for public clients
596
- - Automatic code verifier generation
597
- - Enhanced security for mobile and SPA applications
598
-
599
- ### Token Encryption
600
-
601
- - Secure token storage with optional encryption
602
- - Configurable seal keys for sensitive data
603
- - Protection against token theft
604
-
605
- ### Email Validation
606
-
607
- - Automatic email conflict detection
608
- - Profile-based user validation
609
- - Cross-provider email consistency
610
-
611
- ## ๐Ÿงช Testing
612
-
613
- The library includes comprehensive tests using Vitest:
669
+ ## ๐Ÿข Examples
614
670
 
615
- ```bash
616
- # Run tests
617
- npm test
671
+ ### Multi-Tenant Application
618
672
 
619
- # Run tests with UI
620
- npm run test:ui
673
+ ```typescript
674
+ // Each tenant gets their own system
675
+ const tenantSystem = await oauth.createSystem(`Tenant-${tenantId}`);
621
676
 
622
- # Run tests with coverage
623
- npm run test:coverage
677
+ // Tenant-specific user management
678
+ await oauth.useSystem(tenantSystem.id);
679
+ const tenantUser = await oauth.getOrCreateUser({
680
+ email: userEmail,
681
+ metadata: { tenantId, role: 'admin' },
682
+ });
624
683
 
625
- # Watch mode
626
- npm run test:watch
684
+ // Tenant-isolated tokens
685
+ const tokens = await oauth.granularV2.getTokens({ systemId: tenantSystem.id });
627
686
  ```
628
687
 
629
- ## ๐Ÿข Multi-System Examples
630
-
631
688
  ### SaaS Platform with Multiple Apps
632
689
 
633
690
  ```typescript
@@ -659,23 +716,6 @@ const { url } = await oauth.authorize({
659
716
  });
660
717
  ```
661
718
 
662
- ### Multi-Tenant Application
663
-
664
- ```typescript
665
- // Each tenant gets their own system
666
- const tenantSystem = await oauth.createSystem(`Tenant-${tenantId}`);
667
-
668
- // Tenant-specific user management
669
- await oauth.useSystem(tenantSystem.id);
670
- const tenantUser = await oauth.getOrCreateUser({
671
- email: userEmail,
672
- metadata: { tenantId, role: 'admin' },
673
- });
674
-
675
- // Tenant-isolated tokens
676
- const tokens = await oauth.granular.getTokensBySystem(tenantSystem.id);
677
- ```
678
-
679
719
  ## ๐Ÿš€ Production Deployment
680
720
 
681
721
  ### Environment Configuration
@@ -743,6 +783,50 @@ try {
743
783
  }
744
784
  ```
745
785
 
786
+ ## ๐Ÿ”’ Security Features
787
+
788
+ ### State Management
789
+
790
+ - Cryptographically secure state generation
791
+ - Automatic state validation and cleanup
792
+ - Configurable state expiration
793
+
794
+ ### PKCE (Proof Key for Code Exchange)
795
+
796
+ - Built-in PKCE support for public clients
797
+ - Automatic code verifier generation
798
+ - Enhanced security for mobile and SPA applications
799
+
800
+ ### Token Encryption
801
+
802
+ - Secure token storage with optional encryption
803
+ - Configurable seal keys for sensitive data
804
+ - Protection against token theft
805
+
806
+ ### Email Validation
807
+
808
+ - Automatic email conflict detection
809
+ - Profile-based user validation
810
+ - Cross-provider email consistency
811
+
812
+ ## ๐Ÿงช Testing
813
+
814
+ The library includes comprehensive tests using Vitest:
815
+
816
+ ```bash
817
+ # Run tests
818
+ npm test
819
+
820
+ # Run tests with UI
821
+ npm run test:ui
822
+
823
+ # Run tests with coverage
824
+ npm run test:coverage
825
+
826
+ # Watch mode
827
+ npm run test:watch
828
+ ```
829
+
746
830
  ## ๐Ÿค Contributing
747
831
 
748
832
  1. Fork the repository
@@ -777,10 +861,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
777
861
 
778
862
  ## ๐Ÿ™‹โ€โ™‚๏ธ Support
779
863
 
780
- - ๐Ÿ“š [Documentation](https://github.com/blureffect/oauth2-token-manager#readme)
781
- - ๐Ÿ› [Issue Tracker](https://github.com/blureffect/oauth2-token-manager/issues)
782
- - ๐Ÿ’ฌ [Discussions](https://github.com/blureffect/oauth2-token-manager/discussions)
783
-
784
- ## ๐Ÿ† Credits
785
-
786
- Created with โค๏ธ by [Blureffect](https://blureffect.co)
864
+ - ๐Ÿ“š [Documentation](https://github.com/dainprotocol/oauth2-token-manager#readme)
865
+ - ๐Ÿ› [Issue Tracker](https://github.com/dainprotocol/oauth2-token-manager/issues)
866
+ - ๐Ÿ’ฌ [Discussions](https://github.com/dainprotocol/oauth2-token-manager/discussions)