@vunexa/lixa 0.1.6-alpha.2 → 0.1.6-alpha.4

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
@@ -28,36 +28,7 @@ A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library
28
28
 
29
29
  ## Architecture Overview
30
30
 
31
- ```mermaid
32
- graph TD
33
- User([User Client / Browser])
34
-
35
- subgraph Lixa ["Lixa Core Engine"]
36
- AuthNGuard["Primary AuthN Guard<br/>(Minimal Identity Scopes Only)"]
37
- SessionMgr["Session Handler & Unified Session"]
38
- AccountLinker["Account Linker<br/>(AUTO_LINK_BY_VERIFIED_EMAIL)"]
39
- ResourceConn["Resource Connection API<br/>(Post-Login AuthZ)"]
40
- end
41
-
42
- subgraph IdentityProviders ["Identity Providers (AuthN)"]
43
- GoogleAuth["Google / OIDC"]
44
- GitHubAuth["GitHub OAuth"]
45
- end
46
-
47
- subgraph ResourceProviders ["Resource APIs (AuthZ)"]
48
- GitHubAPI["GitHub API (repos, orgs)"]
49
- GoogleDriveAPI["Google Drive API"]
50
- end
51
-
52
- User -->|"1. Primary Login / Link Account"| AuthNGuard
53
- AuthNGuard -->|"Request Identity"| IdentityProviders
54
- IdentityProviders -->|"Tokens + UserInfo"| AccountLinker
55
- AccountLinker -->|"Unified Session (session.accounts)"| SessionMgr
56
-
57
- User -->|"2. Connect Resource API (Post-Login)"| ResourceConn
58
- ResourceConn -->|"Request Permissions (repo, drive)"| ResourceProviders
59
- ResourceProviders -->|"Resource Access Tokens"| SessionMgr
60
- ```
31
+ ![Architecture Overview](https://cdn.jsdelivr.net/npm/@vunexa/lixa/docs/images/architecture.svg)
61
32
 
62
33
  ---
63
34
 
@@ -216,15 +187,16 @@ app.get("/connect/github/callback", async (req, res) => {
216
187
  res.redirect("/dashboard");
217
188
  });
218
189
 
219
- // 3. Query Connected Resource Access Token
190
+ // 3. Query Connected Resource Access Token (Auto-Refreshes Expired Tokens)
220
191
  app.get("/api/github/repos", async (req, res) => {
192
+ // Queries by active session OR user ID, auto-refreshing expired access tokens
221
193
  const resource = await lixa.getConnectedResource(req.cookies.session_id, "github");
222
194
 
223
195
  if (!resource) {
224
196
  return res.status(403).json({ error: "GitHub resource not connected" });
225
197
  }
226
198
 
227
- // Call GitHub API with resource access token
199
+ // Call GitHub API with active resource access token
228
200
  const response = await fetch("https://api.github.com/user/repos", {
229
201
  headers: { Authorization: `Bearer ${resource.accessToken}` },
230
202
  });
@@ -233,7 +205,10 @@ app.get("/api/github/repos", async (req, res) => {
233
205
  res.json(repos);
234
206
  });
235
207
 
236
- // 4. Disconnect Resource Provider
208
+ // 4. Query Resource Directly by User ID (e.g. inside background cron jobs or webhooks)
209
+ const githubResource = await lixa.getUserResource(userId, "github");
210
+
211
+ // 5. Disconnect Resource Provider
237
212
  app.delete("/connect/github", async (req, res) => {
238
213
  await lixa.disconnectResource(req.cookies.session_id, "github");
239
214
  res.json({ success: true });
@@ -371,10 +346,8 @@ export const lixa = new Lixa({
371
346
  ```json
372
347
  {
373
348
  "id": "e4a91f82c3b4a07f",
374
- "token": "ya29.a0ARW5m7...",
375
349
  "userId": "1049281048",
376
350
  "email": "alex.developer@example.com",
377
- "provider": "google",
378
351
  "accounts": {
379
352
  "google": {
380
353
  "provider": "google",
@@ -398,11 +371,6 @@ export const lixa = new Lixa({
398
371
  "scopes": ["repo", "read:org"],
399
372
  "connectedAt": 1771657300000
400
373
  }
401
- },
402
- "raw": {
403
- "access_token": "ya29.a0ARW5m7...",
404
- "token_type": "Bearer",
405
- "expires_in": 3599
406
374
  }
407
375
  }
408
376
  ```
@@ -513,10 +481,8 @@ export const lixa = new Lixa({
513
481
  "ttl": 1771743600,
514
482
  "sessionData": {
515
483
  "id": "e4a91f82c3b4a07f",
516
- "token": "ya29.a0ARW5m7...",
517
484
  "userId": "1049281048",
518
485
  "email": "alex.developer@example.com",
519
- "provider": "google",
520
486
  "accounts": {
521
487
  "google": {
522
488
  "provider": "google",
@@ -540,14 +506,115 @@ export const lixa = new Lixa({
540
506
  "scopes": ["repo", "read:org"],
541
507
  "connectedAt": 1771657300000
542
508
  }
543
- },
544
- "raw": {
545
- "access_token": "ya29.a0ARW5m7...",
546
- "token_type": "Bearer",
547
- "expires_in": 3599
548
509
  }
510
+ ---
511
+
512
+ ## Custom Resource Storage Implementation (`ResourceStorage`)
513
+
514
+ While `SessionStorage` manages short-lived user authentication sessions (indexed by `sessionId`), **`ResourceStorage`** manages long-lived third-party API tokens (**AuthZ**) bound directly to a **User ID** (or Email). This ensures that third-party credentials (such as GitHub Repositories or Google Drive) persist across session expirations and logouts.
515
+
516
+ ### `ResourceStorage` Interface
517
+
518
+ ```typescript
519
+ export interface ResourceStorage {
520
+ saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void>;
521
+ getResource(userId: string, provider: string): Promise<ConnectedResource | null>;
522
+ getUserResources(userId: string): Promise<Record<string, ConnectedResource>>;
523
+ deleteResource(userId: string, provider: string): Promise<void>;
524
+ }
525
+ ```
526
+
527
+ ### 1. SQLite Resource Storage (`better-sqlite3`)
528
+
529
+ #### Table Schema (SQL DDL)
530
+
531
+ ```sql
532
+ CREATE TABLE IF NOT EXISTS user_resources (
533
+ user_id TEXT NOT NULL,
534
+ provider TEXT NOT NULL,
535
+ data TEXT NOT NULL,
536
+ updated_at INTEGER NOT NULL,
537
+ PRIMARY KEY (user_id, provider)
538
+ );
539
+ ```
540
+
541
+ #### TypeScript Implementation
542
+
543
+ ```typescript
544
+ import Database from "better-sqlite3";
545
+ import { Lixa, type ResourceStorage, type ConnectedResource } from "@vunexa/lixa";
546
+
547
+ export class SqliteResourceStorage implements ResourceStorage {
548
+ private db = new Database("lixa_resources.db");
549
+
550
+ constructor() {
551
+ this.db.exec(`
552
+ CREATE TABLE IF NOT EXISTS user_resources (
553
+ user_id TEXT NOT NULL,
554
+ provider TEXT NOT NULL,
555
+ data TEXT NOT NULL,
556
+ updated_at INTEGER NOT NULL,
557
+ PRIMARY KEY (user_id, provider)
558
+ );
559
+ `);
560
+ }
561
+
562
+ async saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void> {
563
+ const stmt = this.db.prepare(`
564
+ INSERT INTO user_resources (user_id, provider, data, updated_at)
565
+ VALUES (?, ?, ?, ?)
566
+ ON CONFLICT(user_id, provider) DO UPDATE SET
567
+ data = excluded.data,
568
+ updated_at = excluded.updated_at
569
+ `);
570
+ stmt.run(userId, provider.toLowerCase(), JSON.stringify(resource), Date.now());
571
+ }
572
+
573
+ async getResource(userId: string, provider: string): Promise<ConnectedResource | null> {
574
+ const stmt = this.db.prepare(`SELECT data FROM user_resources WHERE user_id = ? AND provider = ?`);
575
+ const row = stmt.get(userId, provider.toLowerCase()) as { data: string } | undefined;
576
+ return row ? (JSON.parse(row.data) as ConnectedResource) : null;
577
+ }
578
+
579
+ async getUserResources(userId: string): Promise<Record<string, ConnectedResource>> {
580
+ const stmt = this.db.prepare(`SELECT provider, data FROM user_resources WHERE user_id = ?`);
581
+ const rows = stmt.all(userId) as Array<{ provider: string; data: string }>;
582
+ const result: Record<string, ConnectedResource> = {};
583
+ for (const r of rows) {
584
+ result[r.provider] = JSON.parse(r.data);
585
+ }
586
+ return result;
587
+ }
588
+
589
+ async deleteResource(userId: string, provider: string): Promise<void> {
590
+ const stmt = this.db.prepare(`DELETE FROM user_resources WHERE user_id = ? AND provider = ?`);
591
+ stmt.run(userId, provider.toLowerCase());
549
592
  }
550
593
  }
594
+
595
+ // Pass to Lixa instance via resourceHandler
596
+ export const lixa = new Lixa({
597
+ resourceHandler: {
598
+ resourceStorage: new SqliteResourceStorage(),
599
+ },
600
+ providers: { /* ... */ },
601
+ });
602
+ ```
603
+
604
+ ### 2. Querying Connected Resources Directly by User ID
605
+
606
+ `ResourceStorage` enables background workers, cron jobs, and webhooks to access third-party API credentials by `userId` without an active HTTP session:
607
+
608
+ ```typescript
609
+ // Background worker querying user's GitHub Repos token
610
+ const githubResource = await lixa.getUserResource(user.id, "github");
611
+
612
+ if (githubResource) {
613
+ // Lixa auto-refreshes expired access tokens transparently!
614
+ const response = await fetch("https://api.github.com/user/repos", {
615
+ headers: { Authorization: `Bearer ${githubResource.accessToken}` },
616
+ });
617
+ }
551
618
  ```
552
619
 
553
620
  ---
@@ -277,15 +277,16 @@ app.get("/connect/github/callback", async (req, res) => {
277
277
  res.redirect("/dashboard");
278
278
  });
279
279
 
280
- // 3. Query Connected Resource Access Token
280
+ // 3. Query Connected Resource Access Token (Auto-Refreshes Expired Tokens)
281
281
  app.get("/api/github/repos", async (req, res) => {
282
+ // Queries by active session OR user ID, auto-refreshing expired access tokens
282
283
  const resource = await lixa.getConnectedResource(req.cookies.session_id, "github");
283
284
 
284
285
  if (!resource) {
285
286
  return res.status(403).json({ error: "GitHub resource not connected" });
286
287
  }
287
288
 
288
- // Call GitHub API with resource access token
289
+ // Call GitHub API with active resource access token
289
290
  const response = await fetch("https://api.github.com/user/repos", {
290
291
  headers: { Authorization: `Bearer ${resource.accessToken}` },
291
292
  });
@@ -294,7 +295,10 @@ app.get("/api/github/repos", async (req, res) => {
294
295
  res.json(repos);
295
296
  });
296
297
 
297
- // 4. Disconnect Resource Provider
298
+ // 4. Query Resource Directly by User ID (e.g. inside background cron jobs or webhooks)
299
+ const githubResource = await lixa.getUserResource(userId, "github");
300
+
301
+ // 5. Disconnect Resource Provider
298
302
  app.delete("/connect/github", async (req, res) => {
299
303
  await lixa.disconnectResource(req.cookies.session_id, "github");
300
304
  res.json({ success: true });
@@ -432,10 +436,8 @@ export const lixa = new Lixa({
432
436
  ```json
433
437
  {
434
438
  "id": "e4a91f82c3b4a07f",
435
- "token": "ya29.a0ARW5m7...",
436
439
  "userId": "1049281048",
437
440
  "email": "alex.developer@example.com",
438
- "provider": "google",
439
441
  "accounts": {
440
442
  "google": {
441
443
  "provider": "google",
@@ -459,11 +461,6 @@ export const lixa = new Lixa({
459
461
  "scopes": ["repo", "read:org"],
460
462
  "connectedAt": 1771657300000
461
463
  }
462
- },
463
- "raw": {
464
- "access_token": "ya29.a0ARW5m7...",
465
- "token_type": "Bearer",
466
- "expires_in": 3599
467
464
  }
468
465
  }
469
466
  ```
@@ -574,10 +571,8 @@ export const lixa = new Lixa({
574
571
  "ttl": 1771743600,
575
572
  "sessionData": {
576
573
  "id": "e4a91f82c3b4a07f",
577
- "token": "ya29.a0ARW5m7...",
578
574
  "userId": "1049281048",
579
575
  "email": "alex.developer@example.com",
580
- "provider": "google",
581
576
  "accounts": {
582
577
  "google": {
583
578
  "provider": "google",
@@ -601,14 +596,115 @@ export const lixa = new Lixa({
601
596
  "scopes": ["repo", "read:org"],
602
597
  "connectedAt": 1771657300000
603
598
  }
604
- },
605
- "raw": {
606
- "access_token": "ya29.a0ARW5m7...",
607
- "token_type": "Bearer",
608
- "expires_in": 3599
609
599
  }
600
+ ---
601
+
602
+ ## Custom Resource Storage Implementation (`ResourceStorage`)
603
+
604
+ While `SessionStorage` manages short-lived user authentication sessions (indexed by `sessionId`), **`ResourceStorage`** manages long-lived third-party API tokens (**AuthZ**) bound directly to a **User ID** (or Email). This ensures that third-party credentials (such as GitHub Repositories or Google Drive) persist across session expirations and logouts.
605
+
606
+ ### `ResourceStorage` Interface
607
+
608
+ ```typescript
609
+ export interface ResourceStorage {
610
+ saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void>;
611
+ getResource(userId: string, provider: string): Promise<ConnectedResource | null>;
612
+ getUserResources(userId: string): Promise<Record<string, ConnectedResource>>;
613
+ deleteResource(userId: string, provider: string): Promise<void>;
614
+ }
615
+ ```
616
+
617
+ ### 1. SQLite Resource Storage (`better-sqlite3`)
618
+
619
+ #### Table Schema (SQL DDL)
620
+
621
+ ```sql
622
+ CREATE TABLE IF NOT EXISTS user_resources (
623
+ user_id TEXT NOT NULL,
624
+ provider TEXT NOT NULL,
625
+ data TEXT NOT NULL,
626
+ updated_at INTEGER NOT NULL,
627
+ PRIMARY KEY (user_id, provider)
628
+ );
629
+ ```
630
+
631
+ #### TypeScript Implementation
632
+
633
+ ```typescript
634
+ import Database from "better-sqlite3";
635
+ import { Lixa, type ResourceStorage, type ConnectedResource } from "@vunexa/lixa";
636
+
637
+ export class SqliteResourceStorage implements ResourceStorage {
638
+ private db = new Database("lixa_resources.db");
639
+
640
+ constructor() {
641
+ this.db.exec(`
642
+ CREATE TABLE IF NOT EXISTS user_resources (
643
+ user_id TEXT NOT NULL,
644
+ provider TEXT NOT NULL,
645
+ data TEXT NOT NULL,
646
+ updated_at INTEGER NOT NULL,
647
+ PRIMARY KEY (user_id, provider)
648
+ );
649
+ `);
650
+ }
651
+
652
+ async saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void> {
653
+ const stmt = this.db.prepare(`
654
+ INSERT INTO user_resources (user_id, provider, data, updated_at)
655
+ VALUES (?, ?, ?, ?)
656
+ ON CONFLICT(user_id, provider) DO UPDATE SET
657
+ data = excluded.data,
658
+ updated_at = excluded.updated_at
659
+ `);
660
+ stmt.run(userId, provider.toLowerCase(), JSON.stringify(resource), Date.now());
661
+ }
662
+
663
+ async getResource(userId: string, provider: string): Promise<ConnectedResource | null> {
664
+ const stmt = this.db.prepare(`SELECT data FROM user_resources WHERE user_id = ? AND provider = ?`);
665
+ const row = stmt.get(userId, provider.toLowerCase()) as { data: string } | undefined;
666
+ return row ? (JSON.parse(row.data) as ConnectedResource) : null;
667
+ }
668
+
669
+ async getUserResources(userId: string): Promise<Record<string, ConnectedResource>> {
670
+ const stmt = this.db.prepare(`SELECT provider, data FROM user_resources WHERE user_id = ?`);
671
+ const rows = stmt.all(userId) as Array<{ provider: string; data: string }>;
672
+ const result: Record<string, ConnectedResource> = {};
673
+ for (const r of rows) {
674
+ result[r.provider] = JSON.parse(r.data);
675
+ }
676
+ return result;
677
+ }
678
+
679
+ async deleteResource(userId: string, provider: string): Promise<void> {
680
+ const stmt = this.db.prepare(`DELETE FROM user_resources WHERE user_id = ? AND provider = ?`);
681
+ stmt.run(userId, provider.toLowerCase());
610
682
  }
611
683
  }
684
+
685
+ // Pass to Lixa instance via resourceHandler
686
+ export const lixa = new Lixa({
687
+ resourceHandler: {
688
+ resourceStorage: new SqliteResourceStorage(),
689
+ },
690
+ providers: { /* ... */ },
691
+ });
692
+ ```
693
+
694
+ ### 2. Querying Connected Resources Directly by User ID
695
+
696
+ `ResourceStorage` enables background workers, cron jobs, and webhooks to access third-party API credentials by `userId` without an active HTTP session:
697
+
698
+ ```typescript
699
+ // Background worker querying user's GitHub Repos token
700
+ const githubResource = await lixa.getUserResource(user.id, "github");
701
+
702
+ if (githubResource) {
703
+ // Lixa auto-refreshes expired access tokens transparently!
704
+ const response = await fetch("https://api.github.com/user/repos", {
705
+ headers: { Authorization: `Bearer ${githubResource.accessToken}` },
706
+ });
707
+ }
612
708
  ```
613
709
 
614
710
  ---
@@ -220,6 +220,53 @@ export interface SessionStorage {
220
220
  session: T;
221
221
  } | null>;
222
222
  }
223
+ /**
224
+ * Resource storage operations interface.
225
+ *
226
+ * @remarks
227
+ * Manages long-lived third-party resource provider tokens (AuthZ) bound to a user account
228
+ * (User ID or Email), independent of short-lived user sessions.
229
+ *
230
+ * @public
231
+ */
232
+ export interface ResourceStorage {
233
+ /**
234
+ * Saves a connected resource token for a user.
235
+ *
236
+ * @param userId - Unique user identifier or email
237
+ * @param provider - Resource provider name (e.g. 'github', 'google')
238
+ * @param resource - Connected resource details including access & refresh tokens
239
+ */
240
+ saveResource(userId: string, provider: string, resource: import("../models/session").ConnectedResource): Promise<void>;
241
+ /**
242
+ * Retrieves a connected resource token for a user.
243
+ *
244
+ * @param userId - Unique user identifier or email
245
+ * @param provider - Resource provider name
246
+ */
247
+ getResource(userId: string, provider: string): Promise<import("../models/session").ConnectedResource | null>;
248
+ /**
249
+ * Retrieves all connected resources for a user.
250
+ *
251
+ * @param userId - Unique user identifier or email
252
+ */
253
+ getUserResources(userId: string): Promise<Record<string, import("../models/session").ConnectedResource>>;
254
+ /**
255
+ * Deletes a connected resource token for a user.
256
+ *
257
+ * @param userId - Unique user identifier or email
258
+ * @param provider - Resource provider name
259
+ */
260
+ deleteResource(userId: string, provider: string): Promise<void>;
261
+ }
262
+ /**
263
+ * Resource handler configuration.
264
+ *
265
+ * @public
266
+ */
267
+ export interface ResourceHandler {
268
+ resourceStorage?: ResourceStorage;
269
+ }
223
270
  /**
224
271
  * Session handler for OAuth authentication.
225
272
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/dao/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnF;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAEnD;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,aAAa,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IAE9E;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,WAAW,CAAC,CAAC,SAAS,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvG;;;;;OAKG;IACH,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAEpE;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;OAIG;IACH,iBAAiB,CAAC,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACzG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,eAAe,CAAC,CAAC,CAAC,SAAS,OAAO,EAChC,SAAS,EAAE,OAAO,mBAAmB,EAAE,kBAAkB,EACzD,gBAAgB,EAAE,OAAO,mBAAmB,EAAE,gBAAgB,GAC7D,OAAO,CAAC,CAAC,CAAC,CAAC;IAEd;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;;;OAKG;IACH,eAAe,CAAC,CAAC,CAAC,SAAS,OAAO,EAChC,SAAS,EAAE,OAAO,mBAAmB,EAAE,kBAAkB,EACzD,gBAAgB,EAAE,OAAO,mBAAmB,EAAE,gBAAgB,GAC7D,OAAO,CAAC,CAAC,CAAC,CAAC;CACf"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/dao/types.ts"],"names":[],"mappings":"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"}
@@ -141,6 +141,8 @@ export declare interface ConnectedResource {
141
141
  accessToken: string;
142
142
  /** Optional refresh token for offline resource access */
143
143
  refreshToken?: string | undefined;
144
+ /** Unix timestamp in milliseconds when the resource access token expires */
145
+ expiresAt?: number | undefined;
144
146
  /** Resource scopes granted by the user */
145
147
  scopes: string[];
146
148
  /** Full raw token response from provider */
@@ -437,6 +439,7 @@ export declare class Lixa<TConfig extends LixaConfig<Record<string, ProviderConf
437
439
  private config;
438
440
  private stateHandler;
439
441
  private sessionHandler;
442
+ private resourceHandler;
440
443
  private debug;
441
444
  /**
442
445
  * Creates a new Lixa instance with the provided configuration.
@@ -749,6 +752,12 @@ export declare class Lixa<TConfig extends LixaConfig<Record<string, ProviderConf
749
752
  * @param params - Object containing sessionId, provider, code, state, and requested scopes
750
753
  * @returns Updated Session containing stored resource tokens under session.resources[provider]
751
754
  */
755
+ private static userResourceStore;
756
+ static LOCAL_RESOURCE_HANDLER: ResourceHandler;
757
+ private getUserKeyFromSession;
758
+ /**
759
+ * Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.
760
+ */
752
761
  handleResourceCallback(params: {
753
762
  sessionId: string;
754
763
  provider: ConfiguredProviderKey<TConfig> | string;
@@ -757,17 +766,41 @@ export declare class Lixa<TConfig extends LixaConfig<Record<string, ProviderConf
757
766
  scopes?: string[];
758
767
  }): Promise<Session>;
759
768
  /**
760
- * Retrieves a connected resource provider token for an active session.
769
+ * Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).
770
+ * Automatically refreshes expired access tokens if a refresh token is present.
771
+ *
772
+ * @param userIdOrEmail - User identifier or email
773
+ * @param provider - Resource provider identifier (e.g. 'github', 'google')
774
+ */
775
+ getUserResource(userIdOrEmail: string, provider: string): Promise<ConnectedResource | null>;
776
+ /**
777
+ * Retrieves all connected resources for a specific User ID / Email.
778
+ */
779
+ getUserResources(userIdOrEmail: string): Promise<Record<string, ConnectedResource>>;
780
+ /**
781
+ * Retrieves a connected resource provider token for an active session, auto-refreshing expired tokens if possible.
761
782
  *
762
783
  * @param sessionId - Active session ID
763
- * @param provider - Provider identifier (e.g. 'github')
784
+ * @param provider - Provider identifier (e.g. 'github', 'google')
764
785
  */
765
786
  getConnectedResource(sessionId: string, provider: string): Promise<ConnectedResource | null>;
766
787
  /**
767
- * Disconnects a resource provider from an active session.
788
+ * Refreshes a user's resource access token using its refresh token.
768
789
  *
769
- * @param sessionId - Active session ID
770
- * @param provider - Provider identifier to disconnect
790
+ * @param userIdOrEmail - User identifier or email
791
+ * @param provider - Provider identifier (e.g. 'google', 'github')
792
+ */
793
+ refreshUserResourceToken(userIdOrEmail: string, provider: string, existingResource?: ConnectedResource): Promise<ConnectedResource>;
794
+ /**
795
+ * Refreshes a connected resource access token for an active session.
796
+ */
797
+ refreshResourceToken(sessionId: string, provider: string): Promise<ConnectedResource>;
798
+ /**
799
+ * Disconnects a resource provider for a specific User ID / Email.
800
+ */
801
+ disconnectUserResource(userIdOrEmail: string, provider: string): Promise<boolean>;
802
+ /**
803
+ * Disconnects a resource provider from an active session and user account.
771
804
  */
772
805
  disconnectResource(sessionId: string, provider: string): Promise<boolean>;
773
806
  fetchSessionInfo(sessionId: string): Promise<Session | null>;
@@ -815,6 +848,14 @@ export declare interface LixaConfig<TProviders extends Record<string, ProviderCo
815
848
  * @see {@link SessionHandler}
816
849
  */
817
850
  sessionHandler?: SessionHandler;
851
+ /**
852
+ * Optional custom resource handler.
853
+ * Handles storage and management of long-lived third-party resource provider tokens (AuthZ)
854
+ * bound directly to user accounts (User ID or Email), independent of transient session IDs.
855
+ *
856
+ * @see {@link ResourceHandler}
857
+ */
858
+ resourceHandler?: ResourceHandler;
818
859
  /**
819
860
  * Enable debug logging.
820
861
  * When enabled, outputs structured logs for initialization, auth flow, and errors.
@@ -945,6 +986,55 @@ export declare interface ProviderMetadata {
945
986
  };
946
987
  }
947
988
 
989
+ /**
990
+ * Resource handler configuration.
991
+ *
992
+ * @public
993
+ */
994
+ export declare interface ResourceHandler {
995
+ resourceStorage?: ResourceStorage;
996
+ }
997
+
998
+ /**
999
+ * Resource storage operations interface.
1000
+ *
1001
+ * @remarks
1002
+ * Manages long-lived third-party resource provider tokens (AuthZ) bound to a user account
1003
+ * (User ID or Email), independent of short-lived user sessions.
1004
+ *
1005
+ * @public
1006
+ */
1007
+ export declare interface ResourceStorage {
1008
+ /**
1009
+ * Saves a connected resource token for a user.
1010
+ *
1011
+ * @param userId - Unique user identifier or email
1012
+ * @param provider - Resource provider name (e.g. 'github', 'google')
1013
+ * @param resource - Connected resource details including access & refresh tokens
1014
+ */
1015
+ saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void>;
1016
+ /**
1017
+ * Retrieves a connected resource token for a user.
1018
+ *
1019
+ * @param userId - Unique user identifier or email
1020
+ * @param provider - Resource provider name
1021
+ */
1022
+ getResource(userId: string, provider: string): Promise<ConnectedResource | null>;
1023
+ /**
1024
+ * Retrieves all connected resources for a user.
1025
+ *
1026
+ * @param userId - Unique user identifier or email
1027
+ */
1028
+ getUserResources(userId: string): Promise<Record<string, ConnectedResource>>;
1029
+ /**
1030
+ * Deletes a connected resource token for a user.
1031
+ *
1032
+ * @param userId - Unique user identifier or email
1033
+ * @param provider - Resource provider name
1034
+ */
1035
+ deleteResource(userId: string, provider: string): Promise<void>;
1036
+ }
1037
+
948
1038
  /**
949
1039
  * Helper type to create a configuration with only registered providers.
950
1040
  * Use this with Lixa.createConfig() for type safety.