@vunexa/lixa 0.1.4 → 0.1.6-alpha.10

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.
@@ -17,8 +17,7 @@ A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library
17
17
  - **Multi-SSO Account Linking**: Seamlessly merge accounts sharing the same verified email address under a single unified user session.
18
18
  - **Post-Login Resource Connection API**: Connect third-party API providers (GitHub Repositories, Google Drive, Slack) post-authentication and manage resource tokens on the user session.
19
19
  - **Multi-provider OAuth/OIDC support** with unified API.
20
- - **Built-in providers** for Google, GitHub, and more via `@vunexa/lixa-providers`.
21
- - **Custom provider support** with extensible provider interface.
20
+ - **Pre-built Database Storage Adapters**: Official Prisma and Drizzle ORM adapters via `@vunexa/lixa-adapters` with support for PostgreSQL, MySQL, and SQLite.
22
21
  - **Unified session management** via `SessionHandler` (generation + storage).
23
22
  - **Unified state management** via `StateHandler` (PKCE + CSRF protection).
24
23
  - **Automatic PKCE** (Proof Key for Code Exchange) for all OAuth flows.
@@ -277,15 +276,16 @@ app.get("/connect/github/callback", async (req, res) => {
277
276
  res.redirect("/dashboard");
278
277
  });
279
278
 
280
- // 3. Query Connected Resource Access Token
279
+ // 3. Query Connected Resource Access Token (Auto-Refreshes Expired Tokens)
281
280
  app.get("/api/github/repos", async (req, res) => {
281
+ // Queries by active session OR user ID, auto-refreshing expired access tokens
282
282
  const resource = await lixa.getConnectedResource(req.cookies.session_id, "github");
283
283
 
284
284
  if (!resource) {
285
285
  return res.status(403).json({ error: "GitHub resource not connected" });
286
286
  }
287
287
 
288
- // Call GitHub API with resource access token
288
+ // Call GitHub API with active resource access token
289
289
  const response = await fetch("https://api.github.com/user/repos", {
290
290
  headers: { Authorization: `Bearer ${resource.accessToken}` },
291
291
  });
@@ -294,7 +294,10 @@ app.get("/api/github/repos", async (req, res) => {
294
294
  res.json(repos);
295
295
  });
296
296
 
297
- // 4. Disconnect Resource Provider
297
+ // 4. Query Resource Directly by User ID (e.g. inside background cron jobs or webhooks)
298
+ const githubResource = await lixa.getUserResource(userId, "github");
299
+
300
+ // 5. Disconnect Resource Provider
298
301
  app.delete("/connect/github", async (req, res) => {
299
302
  await lixa.disconnectResource(req.cookies.session_id, "github");
300
303
  res.json({ success: true });
@@ -310,26 +313,26 @@ export interface Session<TRaw = OAuthTokenResponse> {
310
313
  /** Unique session ID generated by Lixa */
311
314
  id?: string;
312
315
 
313
- /** Primary access token or session token */
314
- token: string;
315
-
316
316
  /** Unified user ID across linked accounts */
317
317
  userId?: string;
318
318
 
319
319
  /** Primary user email */
320
320
  email?: string;
321
321
 
322
- /** Current active auth provider */
323
- provider?: string;
324
-
325
- /** Linked SSO provider accounts (AuthN) */
322
+ /** Linked SSO provider accounts (AuthN) - Single Source of Truth */
326
323
  accounts?: Record<string, LinkedAccount>;
327
324
 
328
- /** Connected third-party resource provider tokens (AuthZ) */
325
+ /** Connected third-party resource provider tokens (AuthZ) - Single Source of Truth */
329
326
  resources?: Record<string, ConnectedResource>;
330
327
 
331
- /** Full raw token response from provider */
332
- raw: TRaw;
328
+ /** Optional primary access token or session token */
329
+ token?: string;
330
+
331
+ /** Optional current active auth provider */
332
+ provider?: string;
333
+
334
+ /** Optional raw token response from provider */
335
+ raw?: TRaw;
333
336
  }
334
337
  ```
335
338
 
@@ -432,10 +435,8 @@ export const lixa = new Lixa({
432
435
  ```json
433
436
  {
434
437
  "id": "e4a91f82c3b4a07f",
435
- "token": "ya29.a0ARW5m7...",
436
438
  "userId": "1049281048",
437
439
  "email": "alex.developer@example.com",
438
- "provider": "google",
439
440
  "accounts": {
440
441
  "google": {
441
442
  "provider": "google",
@@ -451,19 +452,6 @@ export const lixa = new Lixa({
451
452
  "accessToken": "gho_8f7b2a9e1c3...",
452
453
  "linkedAt": 1771657250000
453
454
  }
454
- },
455
- "resources": {
456
- "github": {
457
- "provider": "github",
458
- "accessToken": "gho_resource_repo_9a8b7c...",
459
- "scopes": ["repo", "read:org"],
460
- "connectedAt": 1771657300000
461
- }
462
- },
463
- "raw": {
464
- "access_token": "ya29.a0ARW5m7...",
465
- "token_type": "Bearer",
466
- "expires_in": 3599
467
455
  }
468
456
  }
469
457
  ```
@@ -574,10 +562,8 @@ export const lixa = new Lixa({
574
562
  "ttl": 1771743600,
575
563
  "sessionData": {
576
564
  "id": "e4a91f82c3b4a07f",
577
- "token": "ya29.a0ARW5m7...",
578
565
  "userId": "1049281048",
579
566
  "email": "alex.developer@example.com",
580
- "provider": "google",
581
567
  "accounts": {
582
568
  "google": {
583
569
  "provider": "google",
@@ -593,22 +579,280 @@ export const lixa = new Lixa({
593
579
  "accessToken": "gho_8f7b2a9e1c3...",
594
580
  "linkedAt": 1771657250000
595
581
  }
596
- },
597
- "resources": {
598
- "github": {
599
- "provider": "github",
600
- "accessToken": "gho_resource_repo_9a8b7c...",
601
- "scopes": ["repo", "read:org"],
602
- "connectedAt": 1771657300000
582
+ }
583
+ }
584
+ }
585
+ ```
586
+
587
+ ---
588
+
589
+ ## Custom Resource Storage Implementation (`ResourceStorage`)
590
+
591
+ 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.
592
+
593
+ ### `ResourceStorage` Interface
594
+
595
+ ```typescript
596
+ export interface ResourceStorage {
597
+ saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void>;
598
+ getResource(userId: string, provider: string): Promise<ConnectedResource | null>;
599
+ getUserResources(userId: string): Promise<Record<string, ConnectedResource>>;
600
+ deleteResource(userId: string, provider: string): Promise<void>;
601
+ }
602
+ ```
603
+
604
+ ### 1. SQLite Resource Storage (`better-sqlite3`)
605
+
606
+ #### Table Schema (SQL DDL)
607
+
608
+ ```sql
609
+ CREATE TABLE IF NOT EXISTS user_resources (
610
+ user_id TEXT NOT NULL,
611
+ provider TEXT NOT NULL,
612
+ data TEXT NOT NULL,
613
+ updated_at INTEGER NOT NULL,
614
+ PRIMARY KEY (user_id, provider)
615
+ );
616
+ ```
617
+
618
+ #### TypeScript Implementation
619
+
620
+ ```typescript
621
+ import Database from "better-sqlite3";
622
+ import { Lixa, type ResourceStorage, type ConnectedResource } from "@vunexa/lixa";
623
+
624
+ export class SqliteResourceStorage implements ResourceStorage {
625
+ private db = new Database("lixa_resources.db");
626
+
627
+ constructor() {
628
+ this.db.exec(`
629
+ CREATE TABLE IF NOT EXISTS user_resources (
630
+ user_id TEXT NOT NULL,
631
+ provider TEXT NOT NULL,
632
+ data TEXT NOT NULL,
633
+ updated_at INTEGER NOT NULL,
634
+ PRIMARY KEY (user_id, provider)
635
+ );
636
+ `);
637
+ }
638
+
639
+ async saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void> {
640
+ const stmt = this.db.prepare(`
641
+ INSERT INTO user_resources (user_id, provider, data, updated_at)
642
+ VALUES (?, ?, ?, ?)
643
+ ON CONFLICT(user_id, provider) DO UPDATE SET
644
+ data = excluded.data,
645
+ updated_at = excluded.updated_at
646
+ `);
647
+ stmt.run(userId, provider.toLowerCase(), JSON.stringify(resource), Date.now());
648
+ }
649
+
650
+ async getResource(userId: string, provider: string): Promise<ConnectedResource | null> {
651
+ const stmt = this.db.prepare(`SELECT data FROM user_resources WHERE user_id = ? AND provider = ?`);
652
+ const row = stmt.get(userId, provider.toLowerCase()) as { data: string } | undefined;
653
+ return row ? (JSON.parse(row.data) as ConnectedResource) : null;
654
+ }
655
+
656
+ async getUserResources(userId: string): Promise<Record<string, ConnectedResource>> {
657
+ const stmt = this.db.prepare(`SELECT provider, data FROM user_resources WHERE user_id = ?`);
658
+ const rows = stmt.all(userId) as Array<{ provider: string; data: string }>;
659
+ const result: Record<string, ConnectedResource> = {};
660
+ for (const r of rows) {
661
+ result[r.provider] = JSON.parse(r.data);
662
+ }
663
+ return result;
664
+ }
665
+
666
+ async deleteResource(userId: string, provider: string): Promise<void> {
667
+ const stmt = this.db.prepare(`DELETE FROM user_resources WHERE user_id = ? AND provider = ?`);
668
+ stmt.run(userId, provider.toLowerCase());
669
+ }
670
+ }
671
+
672
+ // Pass to Lixa instance via resourceHandler
673
+ export const lixa = new Lixa({
674
+ resourceHandler: {
675
+ resourceStorage: new SqliteResourceStorage(),
676
+ },
677
+ providers: { /* ... */ },
678
+ });
679
+ ```
680
+
681
+ #### Saved JSON Records Example in SQLite (`user_resources` Table)
682
+
683
+ **Row 1 (GitHub Repositories)**:
684
+ ```json
685
+ {
686
+ "user_id": "1049281048",
687
+ "provider": "github",
688
+ "data": {
689
+ "accessToken": "gho_resource_repo_9a8b7c...",
690
+ "refreshToken": "ghr_refresh_token_123...",
691
+ "scopes": ["repo", "read:org"],
692
+ "expiresAt": 1771743600000,
693
+ "connectedAt": 1771657300000
694
+ }
695
+ }
696
+ ```
697
+
698
+ **Row 2 (Google Drive)**:
699
+ ```json
700
+ {
701
+ "user_id": "1049281048",
702
+ "provider": "google",
703
+ "data": {
704
+ "accessToken": "ya29.drive_resource_token_456...",
705
+ "refreshToken": "1//09abc_google_refresh_token...",
706
+ "scopes": ["https://www.googleapis.com/auth/drive.readonly"],
707
+ "expiresAt": 1771660800000,
708
+ "connectedAt": 1771657400000
709
+ }
710
+ }
711
+ ```
712
+
713
+ ---
714
+
715
+ ### 2. AWS DynamoDB Resource Storage (`@aws-sdk/lib-dynamodb`)
716
+
717
+ For serverless AWS deployments storing user API credentials in DynamoDB:
718
+
719
+ #### Table Configuration
720
+
721
+ - **Table Name**: `LixaUserResources`
722
+ - **Partition Key (PK)**: `userId` (String)
723
+ - **Sort Key (SK)**: `provider` (String)
724
+
725
+ #### TypeScript Implementation
726
+
727
+ ```typescript
728
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
729
+ import { DynamoDBDocumentClient, PutCommand, GetCommand, DeleteCommand, QueryCommand } from "@aws-sdk/lib-dynamodb";
730
+ import { Lixa, type ResourceStorage, type ConnectedResource } from "@vunexa/lixa";
731
+
732
+ export class DynamoDbResourceStorage implements ResourceStorage {
733
+ private docClient: DynamoDBDocumentClient;
734
+ private tableName = "LixaUserResources";
735
+
736
+ constructor() {
737
+ const client = new DynamoDBClient({ region: process.env.AWS_REGION || "us-east-1" });
738
+ this.docClient = DynamoDBDocumentClient.from(client);
739
+ }
740
+
741
+ async saveResource(userId: string, provider: string, resource: ConnectedResource): Promise<void> {
742
+ await this.docClient.send(
743
+ new PutCommand({
744
+ TableName: this.tableName,
745
+ Item: {
746
+ userId,
747
+ provider: provider.toLowerCase(),
748
+ resourceData: resource,
749
+ updatedAt: Date.now(),
750
+ },
751
+ })
752
+ );
753
+ }
754
+
755
+ async getResource(userId: string, provider: string): Promise<ConnectedResource | null> {
756
+ const res = await this.docClient.send(
757
+ new GetCommand({
758
+ TableName: this.tableName,
759
+ Key: {
760
+ userId,
761
+ provider: provider.toLowerCase(),
762
+ },
763
+ })
764
+ );
765
+
766
+ return res.Item ? (res.Item.resourceData as ConnectedResource) : null;
767
+ }
768
+
769
+ async getUserResources(userId: string): Promise<Record<string, ConnectedResource>> {
770
+ const res = await this.docClient.send(
771
+ new QueryCommand({
772
+ TableName: this.tableName,
773
+ KeyConditionExpression: "userId = :userId",
774
+ ExpressionAttributeValues: { ":userId": userId },
775
+ })
776
+ );
777
+
778
+ const result: Record<string, ConnectedResource> = {};
779
+ if (res.Items) {
780
+ for (const item of res.Items) {
781
+ result[item.provider] = item.resourceData as ConnectedResource;
603
782
  }
604
- },
605
- "raw": {
606
- "access_token": "ya29.a0ARW5m7...",
607
- "token_type": "Bearer",
608
- "expires_in": 3599
609
783
  }
784
+ return result;
785
+ }
786
+
787
+ async deleteResource(userId: string, provider: string): Promise<void> {
788
+ await this.docClient.send(
789
+ new DeleteCommand({
790
+ TableName: this.tableName,
791
+ Key: {
792
+ userId,
793
+ provider: provider.toLowerCase(),
794
+ },
795
+ })
796
+ );
610
797
  }
611
798
  }
799
+
800
+ // Pass to Lixa instance via resourceHandler
801
+ export const lixa = new Lixa({
802
+ resourceHandler: {
803
+ resourceStorage: new DynamoDbResourceStorage(),
804
+ },
805
+ providers: { /* ... */ },
806
+ });
807
+ ```
808
+
809
+ #### Saved DynamoDB Resource Items Example (`LixaUserResources` Table)
810
+
811
+ ```json
812
+ [
813
+ {
814
+ "userId": "1049281048",
815
+ "provider": "github",
816
+ "updatedAt": 1771657300000,
817
+ "resourceData": {
818
+ "accessToken": "gho_resource_repo_9a8b7c...",
819
+ "refreshToken": "ghr_refresh_token_123...",
820
+ "scopes": ["repo", "read:org"],
821
+ "expiresAt": 1771743600000,
822
+ "connectedAt": 1771657300000
823
+ }
824
+ },
825
+ {
826
+ "userId": "1049281048",
827
+ "provider": "google",
828
+ "updatedAt": 1771657400000,
829
+ "resourceData": {
830
+ "accessToken": "ya29.drive_resource_token_456...",
831
+ "refreshToken": "1//09abc_google_refresh_token...",
832
+ "scopes": ["https://www.googleapis.com/auth/drive.readonly"],
833
+ "expiresAt": 1771660800000,
834
+ "connectedAt": 1771657400000
835
+ }
836
+ }
837
+ ]
838
+ ```
839
+
840
+ ---
841
+
842
+ ### 3. Querying Connected Resources Directly by User ID
843
+
844
+ `ResourceStorage` enables background workers, cron jobs, and webhooks to access third-party API credentials by `userId` without an active HTTP session:
845
+
846
+ ```typescript
847
+ // Background worker querying user's GitHub Repos token
848
+ const githubResource = await lixa.getUserResource(user.id, "github");
849
+
850
+ if (githubResource) {
851
+ // Lixa auto-refreshes expired access tokens transparently!
852
+ const response = await fetch("https://api.github.com/user/repos", {
853
+ headers: { Authorization: `Bearer ${githubResource.accessToken}` },
854
+ });
855
+ }
612
856
  ```
613
857
 
614
858
  ---
@@ -5,7 +5,8 @@ declare class LocalSessionHandler implements SessionHandler {
5
5
  private emailToSessionMap;
6
6
  sessionStorage: SessionStorage;
7
7
  constructor(defaultTtlSeconds?: number);
8
- GenerateSession<T extends Session>(tokenData: OAuthTokenResponse, providerMetadata: ProviderMetadata): Promise<T>;
8
+ generateSession<T extends Session>(tokenData: OAuthTokenResponse, _providerMetadata?: ProviderMetadata): Promise<T>;
9
+ GenerateSession<T extends Session>(tokenData: OAuthTokenResponse, providerMetadata?: ProviderMetadata): Promise<T>;
9
10
  }
10
11
  export { LocalSessionHandler };
11
12
  //# sourceMappingURL=session-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-cache.d.ts","sourceRoot":"","sources":["../../src/dao/session-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEvF,cAAM,mBAAoB,YAAW,cAAc;IACjD,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,iBAAiB,CAAkC;IACpD,cAAc,EAAE,cAAc,CAAC;gBAE1B,iBAAiB,GAAE,MAAY;IAuCrC,eAAe,CAAC,CAAC,SAAS,OAAO,EACrC,SAAS,EAAE,kBAAkB,EAC7B,gBAAgB,EAAE,gBAAgB,GACjC,OAAO,CAAC,CAAC,CAAC;CAad;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"session-cache.d.ts","sourceRoot":"","sources":["../../src/dao/session-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEvF,cAAM,mBAAoB,YAAW,cAAc;IACjD,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,iBAAiB,CAAkC;IACpD,cAAc,EAAE,cAAc,CAAC;gBAE1B,iBAAiB,GAAE,MAAY;IAwDrC,eAAe,CAAC,CAAC,SAAS,OAAO,EACrC,SAAS,EAAE,kBAAkB,EAC7B,iBAAiB,CAAC,EAAE,gBAAgB,GACnC,OAAO,CAAC,CAAC,CAAC;IAcP,eAAe,CAAC,CAAC,SAAS,OAAO,EACrC,SAAS,EAAE,kBAAkB,EAC7B,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,CAAC,CAAC;CAGd;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
@@ -7,6 +7,10 @@ declare class LocalStateHandler implements StateHandler {
7
7
  state: string;
8
8
  data: StateData;
9
9
  }>;
10
+ GenerateState(provider: string): Promise<{
11
+ state: string;
12
+ data: StateData;
13
+ }>;
10
14
  }
11
15
  export { LocalStateHandler };
12
16
  //# sourceMappingURL=state-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/dao/state-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhE,cAAM,iBAAkB,YAAW,YAAY;IAC7C,OAAO,CAAC,KAAK,CAAY;IAClB,YAAY,EAAE,YAAY,CAAC;gBAEtB,iBAAiB,GAAE,MAAY;IAoBrC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;CAanF;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/dao/state-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhE,cAAM,iBAAkB,YAAW,YAAY;IAC7C,OAAO,CAAC,KAAK,CAAY;IAClB,YAAY,EAAE,YAAY,CAAC;gBAEtB,iBAAiB,GAAE,MAAY;IAoBrC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;IAe5E,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;CAGnF;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -164,10 +164,20 @@ export interface StateHandler {
164
164
  * }
165
165
  * ```
166
166
  */
167
+ /**
168
+ * Generates OAuth state parameter and associated data (camelCase).
169
+ */
167
170
  generateState?(provider: string): Promise<{
168
171
  state: string;
169
172
  data: StateData;
170
173
  }>;
174
+ /**
175
+ * Generates OAuth state parameter and associated data (PascalCase alias for backward compatibility).
176
+ */
177
+ GenerateState?(provider: string): Promise<{
178
+ state: string;
179
+ data: StateData;
180
+ }>;
171
181
  /**
172
182
  * State storage operations.
173
183
  *
@@ -193,7 +203,7 @@ export interface SessionStorage {
193
203
  * Saves a session with expiration.
194
204
  *
195
205
  * @param sessionId - Unique session identifier
196
- * @param session - Session data from GenerateSession()
206
+ * @param session - Session data from generateSession()
197
207
  * @param expiresInSeconds - TTL in seconds (typically 86400 for 24 hours)
198
208
  */
199
209
  saveSession<T extends Session>(sessionId: string, session: T, expiresInSeconds: number): Promise<void>;
@@ -220,19 +230,66 @@ export interface SessionStorage {
220
230
  session: T;
221
231
  } | null>;
222
232
  }
233
+ /**
234
+ * Resource storage operations interface.
235
+ *
236
+ * @remarks
237
+ * Manages long-lived third-party resource provider tokens (AuthZ) bound to a user account
238
+ * (User ID or Email), independent of short-lived user sessions.
239
+ *
240
+ * @public
241
+ */
242
+ export interface ResourceStorage {
243
+ /**
244
+ * Saves a connected resource token for a user.
245
+ *
246
+ * @param userId - Unique user identifier or email
247
+ * @param provider - Resource provider name (e.g. 'github', 'google')
248
+ * @param resource - Connected resource details including access & refresh tokens
249
+ */
250
+ saveResource(userId: string, provider: string, resource: import("../models/session").ConnectedResource): Promise<void>;
251
+ /**
252
+ * Retrieves a connected resource token for a user.
253
+ *
254
+ * @param userId - Unique user identifier or email
255
+ * @param provider - Resource provider name
256
+ */
257
+ getResource(userId: string, provider: string): Promise<import("../models/session").ConnectedResource | null>;
258
+ /**
259
+ * Retrieves all connected resources for a user.
260
+ *
261
+ * @param userId - Unique user identifier or email
262
+ */
263
+ getUserResources(userId: string): Promise<Record<string, import("../models/session").ConnectedResource>>;
264
+ /**
265
+ * Deletes a connected resource token for a user.
266
+ *
267
+ * @param userId - Unique user identifier or email
268
+ * @param provider - Resource provider name
269
+ */
270
+ deleteResource(userId: string, provider: string): Promise<void>;
271
+ }
272
+ /**
273
+ * Resource handler configuration.
274
+ *
275
+ * @public
276
+ */
277
+ export interface ResourceHandler {
278
+ resourceStorage?: ResourceStorage;
279
+ }
223
280
  /**
224
281
  * Session handler for OAuth authentication.
225
282
  *
226
283
  * @remarks
227
284
  * The SessionHandler manages session generation and storage after successful OAuth authentication.
228
285
  *
229
- * - GenerateSession: Optional. Customizes how OAuth tokens are converted into session data.
286
+ * - generateSession: Optional. Customizes how OAuth tokens are converted into session data.
230
287
  * If not provided, uses default implementation (access token as session token).
231
288
  *
232
- * - storage: Optional. Provides custom session storage (save/get/delete operations).
289
+ * - sessionStorage: Optional. Provides custom session storage (save/get/delete operations).
233
290
  * If not provided, uses in-memory cache (not suitable for production).
234
291
  *
235
- * For production, implement both GenerateSession (for user creation/lookup) and storage
292
+ * For production, implement both generateSession (for user creation/lookup) and sessionStorage
236
293
  * (for persistent session storage with Redis, database, etc.).
237
294
  *
238
295
  * @example
@@ -241,7 +298,7 @@ export interface SessionStorage {
241
298
  * import { SessionHandler, Session, OAuthTokenResponse, ProviderMetadata, extractUserInfo } from '@vunexa/lixa';
242
299
  *
243
300
  * const sessionHandler: SessionHandler = {
244
- * GenerateSession: async (tokenData, providerMetadata) => {
301
+ * generateSession: async (tokenData, providerMetadata) => {
245
302
  * // Extract user info and create/retrieve user
246
303
  * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);
247
304
  * const user = await db.users.upsert({
@@ -260,7 +317,7 @@ export interface SessionStorage {
260
317
  * };
261
318
  * },
262
319
  *
263
- * storage: {
320
+ * sessionStorage: {
264
321
  * saveSession: async (sessionId, session, expiresInSeconds) => {
265
322
  * const expiresAt = new Date(Date.now() + expiresInSeconds * 1000);
266
323
  * await db.sessions.create({
@@ -286,78 +343,21 @@ export interface SessionStorage {
286
343
  * };
287
344
  * ```
288
345
  *
289
- * @example
290
- * Minimal implementation (uses defaults):
291
- * ```typescript
292
- * const sessionHandler: SessionHandler = {
293
- * storage: {
294
- * saveSession: async (sessionId, session, expiresInSeconds) => {
295
- * await redis.setex(sessionId, expiresInSeconds, JSON.stringify(session));
296
- * },
297
- * getSession: async (sessionId) => {
298
- * const data = await redis.get(sessionId);
299
- * return data ? JSON.parse(data) : null;
300
- * },
301
- * deleteSession: async (sessionId) => {
302
- * await redis.del(sessionId);
303
- * }
304
- * }
305
- * };
306
- * ```
307
- *
308
346
  * @public
309
347
  */
310
348
  export interface SessionHandler {
311
349
  /**
312
- * Generates session data from OAuth token data.
350
+ * Generates session data from OAuth token data (camelCase).
313
351
  *
314
352
  * @param tokenData - The token data received from the OAuth provider's token endpoint
315
353
  * @param providerMetadata - Provider metadata including name and endpoints
316
354
  * @returns A Promise that resolves to session data
317
- *
318
- * @remarks
319
- * This method is responsible for creating session data from OAuth tokens.
320
- * It is called after successfully exchanging the authorization code for tokens.
321
- *
322
- * Token Data:
323
- * - access_token: OAuth access token
324
- * - refresh_token: OAuth refresh token (optional)
325
- * - expires_in: Token expiration time in seconds
326
- * - token_type: Token type (usually "Bearer")
327
- * - id_token: OpenID Connect ID token (for OIDC providers)
328
- * - scope: Granted scopes
329
- *
330
- * Provider Metadata:
331
- * - name: The provider name (e.g., 'google', 'github')
332
- * - endpoints: Provider endpoints (authorization, token, userInfo)
333
- *
334
- * Your implementation should:
335
- * 1. Extract user info (using extractUserInfo or decode ID token)
336
- * 2. Create or lookup users in your database
337
- * 3. Build and return session data with any custom fields
338
- *
339
- * Note: This method should NOT store the session. Storage is handled by the storage object.
340
- *
341
- * If not provided, defaults to using the access token as the session token.
342
- *
343
- * @example
344
- * ```typescript
345
- * GenerateSession: async (tokenData, providerMetadata) => {
346
- * const { userInfo } = await extractUserInfo(tokenData, providerMetadata);
347
- * const user = await db.users.upsert({ email: userInfo.email });
348
- *
349
- * return {
350
- * token: tokenData.access_token,
351
- * raw: {
352
- * ...tokenData,
353
- * userId: user.id,
354
- * provider: providerMetadata.name
355
- * }
356
- * };
357
- * }
358
- * ```
359
355
  */
360
356
  generateSession?<T extends Session>(tokenData: import('../models/session').OAuthTokenResponse, providerMetadata: import('../models/session').ProviderMetadata): Promise<T>;
357
+ /**
358
+ * Generates session data from OAuth token data (PascalCase alias for backward compatibility).
359
+ */
360
+ GenerateSession?<T extends Session>(tokenData: import('../models/session').OAuthTokenResponse, providerMetadata: import('../models/session').ProviderMetadata): Promise<T>;
361
361
  /**
362
362
  * Session storage operations.
363
363
  *
@@ -368,12 +368,5 @@ export interface SessionHandler {
368
368
  * If not provided, uses in-memory cache (not suitable for production).
369
369
  */
370
370
  sessionStorage?: SessionStorage;
371
- /**
372
- * Optional method to generate session data from OAuth tokens.
373
- *
374
- * @remarks
375
- * If not provided, uses default implementation from LocalSessionHandler.
376
- */
377
- generateSession?<T extends Session>(tokenData: import('../models/session').OAuthTokenResponse, providerMetadata: import('../models/session').ProviderMetadata): Promise<T>;
378
371
  }
379
372
  //# sourceMappingURL=types.d.ts.map
@@ -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":"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;;OAEG;IACH,aAAa,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IAE9E;;OAEG;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;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,mBAAmB,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvH;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,mBAAmB,EAAE,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAE7G;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,mBAAmB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEzG;;;;;OAKG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;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;;OAEG;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;CACjC"}