@vunexa/lixa 0.0.1-alpha.19 → 0.0.1-alpha.21

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.
@@ -4,6 +4,15 @@
4
4
  * @remarks
5
5
  * This package simplifies multi-provider authentication flows (e.g., Google, GitHub), supports extensible session management, and enables custom provider registration.
6
6
  *
7
+ * Key features:
8
+ * - OAuth 2.0 authorization code flow with PKCE (RFC 6749, RFC 7636)
9
+ * - OpenID Connect support
10
+ * - Built-in providers available in \@vunexa/lixa-providers
11
+ * - Custom provider support via IProvider interface
12
+ * - Extensible session management via SessionStrategy
13
+ * - Pluggable state and session storage via StateDao and SessionDao
14
+ * - TypeScript-first with comprehensive type safety
15
+ *
7
16
  * @packageDocumentation
8
17
  */
9
18
 
@@ -30,19 +39,136 @@ export declare class DefaultSessionStrategy implements SessionStrategy {
30
39
  }
31
40
 
32
41
  /**
33
- * Interface for OAuth provider implementations.
42
+ * Interface for OAuth 2.0 and OpenID Connect provider implementations.
34
43
  *
35
44
  * @remarks
36
45
  * Implement this interface to add support for custom OAuth providers.
46
+ * Each provider defines the three core endpoints required for the OAuth 2.0
47
+ * authorization code flow with PKCE.
48
+ *
49
+ * Built-in providers (Google, GitHub) are available in the \@vunexa/lixa-providers package.
50
+ *
51
+ * All implementations must comply with:
52
+ * - RFC 6749 (OAuth 2.0)
53
+ * - RFC 7636 (PKCE)
54
+ * - OpenID Connect Core 1.0 (for OIDC providers)
55
+ *
56
+ * @example
57
+ * Custom provider implementation:
58
+ * ```typescript
59
+ * import { IProvider } from '@vunexa/lixa';
60
+ *
61
+ * class CustomProvider implements IProvider {
62
+ * authorizationEndpoint = 'https://auth.example.com/oauth/authorize';
63
+ * tokenEndpoint = 'https://auth.example.com/oauth/token';
64
+ * userInfoEndpoint = 'https://api.example.com/user';
65
+ * }
66
+ *
67
+ * // Use in configuration
68
+ * const lixa = new Lixa({
69
+ * providers: {
70
+ * custom: {
71
+ * provider: new CustomProvider(),
72
+ * clientId: 'your-client-id',
73
+ * clientSecret: 'your-client-secret',
74
+ * redirectUri: 'https://app.com/callback',
75
+ * scopes: ['read:user']
76
+ * }
77
+ * }
78
+ * });
79
+ * ```
80
+ *
81
+ * @example
82
+ * Object literal provider:
83
+ * ```typescript
84
+ * const customProvider: IProvider = {
85
+ * authorizationEndpoint: 'https://auth.example.com/oauth/authorize',
86
+ * tokenEndpoint: 'https://auth.example.com/oauth/token',
87
+ * userInfoEndpoint: 'https://api.example.com/user'
88
+ * };
89
+ * ```
37
90
  *
38
91
  * @public
39
92
  */
40
93
  export declare interface IProvider {
41
- /** The OAuth authorization endpoint URL */
94
+ /**
95
+ * The OAuth 2.0 authorization endpoint URL.
96
+ *
97
+ * @remarks
98
+ * This is the URL where users are redirected to authenticate and authorize your application.
99
+ * The endpoint must support the OAuth 2.0 authorization code flow with PKCE.
100
+ *
101
+ * Standard query parameters sent to this endpoint:
102
+ * - client_id: Your application's client ID
103
+ * - redirect_uri: Where to redirect after authorization
104
+ * - response_type: Always "code" for authorization code flow
105
+ * - scope: Space-separated list of requested scopes
106
+ * - state: Random string for CSRF protection
107
+ * - code_challenge: PKCE code challenge (SHA-256 hash)
108
+ * - code_challenge_method: Always "S256" for SHA-256
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth'
113
+ * ```
114
+ */
42
115
  authorizationEndpoint: string;
43
- /** The OAuth token exchange endpoint URL */
116
+ /**
117
+ * The OAuth 2.0 token endpoint URL.
118
+ *
119
+ * @remarks
120
+ * This is the URL where authorization codes are exchanged for access tokens.
121
+ * The endpoint must support the OAuth 2.0 token exchange with PKCE.
122
+ *
123
+ * Standard parameters sent to this endpoint (POST request):
124
+ * - grant_type: Always "authorization_code"
125
+ * - code: The authorization code from the callback
126
+ * - redirect_uri: Must match the authorization request
127
+ * - client_id: Your application's client ID
128
+ * - client_secret: Your application's client secret
129
+ * - code_verifier: PKCE code verifier (original random string)
130
+ *
131
+ * Expected response:
132
+ * - access_token: OAuth access token
133
+ * - token_type: Token type (usually "Bearer")
134
+ * - expires_in: Token expiration time in seconds
135
+ * - refresh_token: Refresh token (optional)
136
+ * - id_token: OpenID Connect ID token (for OIDC providers)
137
+ * - scope: Granted scopes
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * tokenEndpoint = 'https://oauth2.googleapis.com/token'
142
+ * ```
143
+ */
44
144
  tokenEndpoint: string;
45
- /** The user information endpoint URL */
145
+ /**
146
+ * The user information endpoint URL.
147
+ *
148
+ * @remarks
149
+ * This is the URL where user profile information can be retrieved using the access token.
150
+ * For OpenID Connect providers, this is the UserInfo endpoint.
151
+ *
152
+ * The endpoint is called with the access token in the Authorization header:
153
+ * ```
154
+ * Authorization: Bearer <access_token>
155
+ * ```
156
+ *
157
+ * Common response fields:
158
+ * - sub: Subject identifier (user ID)
159
+ * - email: User's email address
160
+ * - name: User's full name
161
+ * - picture: User's profile picture URL
162
+ * - email_verified: Whether email is verified
163
+ *
164
+ * Note: This endpoint is not called automatically by Lixa. Your SessionStrategy
165
+ * can call it if needed to fetch user profile information.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * userInfoEndpoint = 'https://www.googleapis.com/oauth2/v2/userinfo'
170
+ * ```
171
+ */
46
172
  userInfoEndpoint: string;
47
173
  }
48
174
 
@@ -51,18 +177,18 @@ export declare interface IProvider {
51
177
  *
52
178
  * @remarks
53
179
  * Lixa simplifies multi-provider authentication flows and supports extensible session management.
180
+ * Providers can be passed inline in the configuration, eliminating the need for pre-registration.
54
181
  *
55
182
  * @example
183
+ * Using built-in providers from @vunexa/lixa-providers:
56
184
  * ```typescript
57
185
  * import { Lixa } from '@vunexa/lixa';
58
- * import { GoogleProvider } from '@vunexa/lixa/providers';
186
+ * import { GoogleProvider } from '@vunexa/lixa-providers';
59
187
  *
60
- * // Register providers before using them
61
- * Lixa.registerProvider({ google: new GoogleProvider() });
62
- *
63
- * const config = Lixa.createConfig({
188
+ * const lixa = new Lixa({
64
189
  * providers: {
65
190
  * google: {
191
+ * provider: new GoogleProvider(),
66
192
  * clientId: 'your-client-id',
67
193
  * clientSecret: 'your-client-secret',
68
194
  * redirectUri: 'https://yourapp.com/auth/google/callback',
@@ -70,13 +196,36 @@ export declare interface IProvider {
70
196
  * }
71
197
  * }
72
198
  * });
199
+ * ```
200
+ *
201
+ * @example
202
+ * Using custom inline providers:
203
+ * ```typescript
204
+ * import { Lixa, IProvider } from '@vunexa/lixa';
205
+ *
206
+ * const customProvider: IProvider = {
207
+ * authorizationEndpoint: 'https://custom.com/oauth/authorize',
208
+ * tokenEndpoint: 'https://custom.com/oauth/token',
209
+ * userInfoEndpoint: 'https://custom.com/api/user'
210
+ * };
73
211
  *
74
- * const lixa = new Lixa(config);
212
+ * const lixa = new Lixa({
213
+ * providers: {
214
+ * custom: {
215
+ * provider: customProvider,
216
+ * clientId: 'your-client-id',
217
+ * clientSecret: 'your-client-secret',
218
+ * redirectUri: 'https://yourapp.com/auth/custom/callback',
219
+ * scopes: ['read:user']
220
+ * }
221
+ * }
222
+ * });
75
223
  * ```
76
224
  *
77
225
  * @public
78
226
  */
79
227
  export declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
228
+ private static DEFAULT_PROVIDERS;
80
229
  private static CONFIGURED_PROVIDERS;
81
230
  private static LOCAL_STATE_CACHE;
82
231
  private static LOCAL_SESSION_CACHE;
@@ -89,17 +238,52 @@ export declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
89
238
  /**
90
239
  * Creates a new Lixa instance with the provided configuration.
91
240
  *
241
+ * @remarks
242
+ * Providers can be passed inline in the configuration using the `provider` field.
243
+ * Provider resolution priority: inline custom provider > default providers > legacy registry.
244
+ *
92
245
  * @param config - The configuration object containing provider settings and optional session strategy
246
+ *
247
+ * @throws Error when provider configuration is missing required fields
248
+ * @throws Error when provider implementation is missing required properties
249
+ * @throws Error when provider is not available and no inline implementation is provided
93
250
  */
94
251
  constructor(config: TConfig);
252
+ /**
253
+ * Validates that a provider configuration has all required credentials.
254
+ *
255
+ * @param name - The provider name
256
+ * @param config - The provider configuration
257
+ * @throws Error when required fields are missing or invalid
258
+ */
259
+ private validateProviderConfig;
260
+ /**
261
+ * Validates that a provider implementation has all required properties.
262
+ *
263
+ * @param name - The provider name
264
+ * @param provider - The provider implementation
265
+ * @throws Error when required properties are missing
266
+ */
267
+ private validateProviderImplementation;
268
+ /**
269
+ * Structured debug logging with standardized format.
270
+ *
271
+ * @param level - Log level (INFO, WARN, ERROR)
272
+ * @param context - Context of the log (Init, Auth, Token, Session, State)
273
+ * @param message - Log message
274
+ * @param data - Optional data to log
275
+ *
276
+ * @remarks
277
+ * Format: [Lixa] [timestamp] [level] [context] message
278
+ * Only logs when debug mode is enabled.
279
+ */
95
280
  private log;
96
- private logError;
97
281
  /**
98
- * Checks if a provider is both registered and configured for this instance.
282
+ * Checks if a provider is configured for this instance.
99
283
  * This is a type guard that narrows the provider type for use with getAuthUrl.
100
284
  *
101
285
  * @param provider - The provider name to check (case-insensitive)
102
- * @returns True if the provider is registered and configured, false otherwise
286
+ * @returns True if the provider is configured, false otherwise
103
287
  *
104
288
  * @example
105
289
  * ```typescript
@@ -110,12 +294,37 @@ export declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
110
294
  * ```
111
295
  */
112
296
  isProviderConfigured<T extends string>(provider: T): provider is T & ConfiguredProviderKey<TConfig>;
297
+ /**
298
+ * Gets a provider implementation by name.
299
+ * Resolution priority: inline custom provider > default providers > legacy registry
300
+ *
301
+ * @param name - The provider name (case-insensitive)
302
+ * @param config - The provider configuration
303
+ * @returns The provider implementation
304
+ * @throws Error when provider is not found
305
+ */
306
+ private getProvider;
113
307
  /**
114
308
  * Registers custom OAuth providers for use with Lixa.
115
309
  *
310
+ * @deprecated This method is maintained for backward compatibility.
311
+ * The recommended approach is to pass providers inline in the configuration:
312
+ * ```typescript
313
+ * const lixa = new Lixa({
314
+ * providers: {
315
+ * custom: {
316
+ * provider: new CustomProvider(),
317
+ * clientId: '...',
318
+ * // ...
319
+ * }
320
+ * }
321
+ * });
322
+ * ```
323
+ *
116
324
  * @param providerMap - A map of provider names to IProvider implementations
117
325
  *
118
326
  * @example
327
+ * Legacy usage (still supported):
119
328
  * ```typescript
120
329
  * class CustomProvider implements IProvider {
121
330
  * authorizationEndpoint = 'https://custom.com/oauth/authorize';
@@ -134,14 +343,31 @@ export declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
134
343
  */
135
344
  static getRegisteredProviders(): string[];
136
345
  /**
137
- * Creates a type-safe configuration that only allows registered providers.
346
+ * Creates a type-safe configuration.
347
+ *
348
+ * @deprecated This method is maintained for backward compatibility.
349
+ * You can now pass configuration directly to the Lixa constructor without this helper.
350
+ *
351
+ * @param config - Configuration object with provider settings
352
+ * @returns The same configuration object with type safety
138
353
  *
139
- * @param config - Configuration object with providers that must be registered
140
- * @returns The same configuration object, but with type safety for registered providers
354
+ * @example
355
+ * New approach (recommended):
356
+ * ```typescript
357
+ * const lixa = new Lixa({
358
+ * providers: {
359
+ * google: {
360
+ * provider: new GoogleProvider(),
361
+ * clientId: '...',
362
+ * // ...
363
+ * }
364
+ * }
365
+ * });
366
+ * ```
141
367
  */
142
- static createConfig<T extends Record<string, ProviderConfig>>(config: LixaConfig<keyof T & string> & {
368
+ static createConfig<T extends Record<string, ProviderConfig>>(config: LixaConfig<T> & {
143
369
  providers: T;
144
- }): LixaConfig<keyof T & string>;
370
+ }): LixaConfig<T>;
145
371
  /**
146
372
  * Generates a cryptographically secure random state parameter for OAuth flows.
147
373
  *
@@ -154,12 +380,81 @@ export declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
154
380
  /**
155
381
  * Generates a cryptographically secure code verifier for PKCE flows.
156
382
  *
157
- * @returns A 64-character hexadecimal string
383
+ * @returns A 64-character hexadecimal string (32 random bytes encoded as hex)
158
384
  *
159
385
  * @remarks
160
- * The code verifier is used in PKCE (Proof Key for Code Exchange) to enhance security.
386
+ * This method implements the code verifier generation as specified in RFC 7636 (PKCE).
387
+ *
388
+ * **PKCE (Proof Key for Code Exchange)** is a security extension to OAuth 2.0 that
389
+ * prevents authorization code interception attacks. It's especially important for
390
+ * public clients (mobile apps, SPAs) but is recommended for all OAuth flows.
391
+ *
392
+ * **Generation methodology:**
393
+ * 1. Generate 32 cryptographically random bytes using Node.js crypto.randomBytes()
394
+ * 2. Encode the bytes as a hexadecimal string (64 characters)
395
+ * 3. The verifier is stored securely and used later in the token exchange
396
+ *
397
+ * **RFC 7636 Requirements:**
398
+ * - Minimum length: 43 characters
399
+ * - Maximum length: 128 characters
400
+ * - Character set: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
401
+ * - This implementation produces 64 hex characters, meeting the requirements
402
+ *
403
+ * The code verifier is:
404
+ * - Generated when creating the authorization URL
405
+ * - Stored in state cache with the state parameter
406
+ * - Retrieved during callback handling
407
+ * - Sent to the token endpoint to prove the client's identity
408
+ *
409
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}
410
+ * @see {@link buildCodeChallenge} for the corresponding challenge generation
411
+ *
412
+ * @internal
161
413
  */
162
414
  private static generateCodeVerifier;
415
+ /**
416
+ * Generates a code challenge from a code verifier for PKCE flows.
417
+ *
418
+ * @param codeVerifier - The code verifier string (64 hex characters)
419
+ * @returns A base64url-encoded SHA-256 hash of the code verifier
420
+ *
421
+ * @remarks
422
+ * This method implements the code challenge generation as specified in RFC 7636 (PKCE)
423
+ * using the S256 (SHA-256) transformation method.
424
+ *
425
+ * **Challenge generation methodology:**
426
+ * 1. Hash the code verifier using SHA-256
427
+ * 2. Encode the hash as base64
428
+ * 3. Convert to base64url format (RFC 4648):
429
+ * - Replace '+' with '-'
430
+ * - Replace '/' with '_'
431
+ * - Remove trailing '=' padding
432
+ *
433
+ * **PKCE Flow:**
434
+ * 1. Client generates code_verifier (random string)
435
+ * 2. Client creates code_challenge = BASE64URL(SHA256(code_verifier))
436
+ * 3. Client sends code_challenge to authorization endpoint
437
+ * 4. Authorization server stores the code_challenge
438
+ * 5. Client sends code_verifier to token endpoint
439
+ * 6. Authorization server verifies: SHA256(code_verifier) == code_challenge
440
+ *
441
+ * **Security Benefits:**
442
+ * - Prevents authorization code interception attacks
443
+ * - Even if an attacker intercepts the authorization code, they cannot
444
+ * exchange it for tokens without the original code_verifier
445
+ * - The challenge is sent in the authorization request (public)
446
+ * - The verifier is sent in the token request (should be kept secret)
447
+ *
448
+ * **RFC 7636 Transformation Methods:**
449
+ * - plain: code_challenge = code_verifier (not recommended)
450
+ * - S256: code_challenge = BASE64URL(SHA256(code_verifier)) (recommended, used here)
451
+ *
452
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7636 | RFC 7636 - PKCE}
453
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc4648#section-5 | RFC 4648 - Base64url Encoding}
454
+ * @see {@link generateCodeVerifier} for the verifier generation
455
+ *
456
+ * @internal
457
+ */
163
458
  private static buildCodeChallenge;
164
459
  /**
165
460
  * Generates the authorization URL for the specified provider.
@@ -209,29 +504,131 @@ export declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
209
504
 
210
505
  /**
211
506
  * Main configuration object for Lixa.
212
- * Only allows providers that have been registered through registerProvider.
507
+ * Provides type-safe provider name inference.
508
+ *
509
+ * @remarks
510
+ * The generic type parameter TProviders enables TypeScript to infer provider names
511
+ * from the configuration object, providing autocomplete and type checking for
512
+ * provider names in methods like getAuthUrl() and handleCallback().
513
+ *
514
+ * @typeParam TProviders - The provider configuration map type, defaults to a generic record
515
+ *
516
+ * @example
517
+ * Basic configuration with built-in providers:
518
+ * ```typescript
519
+ * import { Lixa } from '@vunexa/lixa';
520
+ * import { GoogleProvider } from '@vunexa/lixa-providers';
521
+ *
522
+ * const lixa = new Lixa({
523
+ * providers: {
524
+ * google: {
525
+ * provider: new GoogleProvider(),
526
+ * clientId: process.env.GOOGLE_CLIENT_ID!,
527
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
528
+ * redirectUri: 'https://app.com/auth/google/callback',
529
+ * scopes: ['openid', 'email', 'profile']
530
+ * }
531
+ * }
532
+ * });
533
+ * ```
534
+ *
535
+ * @example
536
+ * Configuration with custom session and state storage:
537
+ * ```typescript
538
+ * const lixa = new Lixa({
539
+ * providers: {
540
+ * google: {
541
+ * provider: new GoogleProvider(),
542
+ * clientId: process.env.GOOGLE_CLIENT_ID!,
543
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
544
+ * redirectUri: 'https://app.com/auth/google/callback',
545
+ * scopes: ['openid', 'email', 'profile']
546
+ * }
547
+ * },
548
+ * sessionStrategy: new CustomSessionStrategy(),
549
+ * stateDao: new RedisStateDao(),
550
+ * sessionDao: new DatabaseSessionDao(),
551
+ * debug: true
552
+ * });
553
+ * ```
213
554
  *
214
555
  * @public
215
556
  */
216
- export declare interface LixaConfig<TRegisteredProviders extends string = string> {
217
- /** Map of registered provider names to their configurations */
218
- providers: Record<TRegisteredProviders, ProviderConfig>;
219
- /** Optional custom session creation strategy */
557
+ export declare interface LixaConfig<TProviders extends Record<string, ProviderConfig> = Record<string, ProviderConfig>> {
558
+ /**
559
+ * Map of provider names to their configurations.
560
+ * Provider names will be available for autocomplete in getAuthUrl() and handleCallback().
561
+ */
562
+ providers: TProviders;
563
+ /**
564
+ * Optional custom session creation strategy.
565
+ * Defines how OAuth tokens are converted into application sessions.
566
+ * Defaults to DefaultSessionStrategy if not provided.
567
+ *
568
+ * @see {@link SessionStrategy}
569
+ */
220
570
  sessionStrategy?: SessionStrategy;
221
- /** Optional custom state storage implementation */
571
+ /**
572
+ * Optional custom state storage implementation.
573
+ * Used for CSRF protection and PKCE code verifier storage during OAuth flow.
574
+ * Defaults to in-memory cache if not provided (not suitable for production).
575
+ *
576
+ * @see {@link StateDao}
577
+ */
222
578
  stateDao?: StateDao;
223
- /** Optiona: custom session storage implementation */
579
+ /**
580
+ * Optional custom session storage implementation.
581
+ * Used for persistent user session storage after authentication.
582
+ * Defaults to in-memory cache if not provided (not suitable for production).
583
+ *
584
+ * @see {@link SessionDao}
585
+ */
224
586
  sessionDao?: SessionDao;
225
- /** Enable debug logging */
587
+ /**
588
+ * Enable debug logging.
589
+ * When enabled, outputs structured logs for initialization, auth flow, and errors.
590
+ * Format: [Lixa] [timestamp] [level] [context] message
591
+ */
226
592
  debug?: boolean;
227
593
  }
228
594
 
229
595
  /**
230
- * Configuration for an OAuth provider.
596
+ * Configuration for an OAuth provider instance.
597
+ *
598
+ * @remarks
599
+ * For built-in providers (google, github), just provide credentials.
600
+ * For custom providers, include the provider implementation.
601
+ *
602
+ * The provider field uses a discriminated union to ensure type safety:
603
+ * - When omitted or undefined: assumes a built-in provider
604
+ * - When provided: must be a valid IProvider implementation
605
+ *
606
+ * @example
607
+ * Built-in provider configuration:
608
+ * ```typescript
609
+ * {
610
+ * clientId: 'your-client-id',
611
+ * clientSecret: 'your-client-secret',
612
+ * redirectUri: 'https://app.com/callback',
613
+ * scopes: ['openid', 'email']
614
+ * }
615
+ * ```
616
+ *
617
+ * @example
618
+ * Custom provider configuration:
619
+ * ```typescript
620
+ * {
621
+ * provider: new CustomProvider(),
622
+ * clientId: 'your-client-id',
623
+ * clientSecret: 'your-client-secret',
624
+ * redirectUri: 'https://app.com/callback',
625
+ * scopes: ['read:user']
626
+ * }
627
+ * ```
231
628
  *
232
629
  * @public
233
630
  */
234
- export declare interface ProviderConfig {
631
+ export declare type ProviderConfig = {
235
632
  /** The OAuth client ID provided by the provider */
236
633
  clientId: string;
237
634
  /** The OAuth client secret provided by the provider */
@@ -242,55 +639,400 @@ export declare interface ProviderConfig {
242
639
  scopes: string[];
243
640
  /** Additional provider-specific configuration parameters */
244
641
  extraConfig?: Record<string, any>;
245
- }
642
+ } & ({
643
+ provider?: never;
644
+ } | {
645
+ provider: IProvider;
646
+ });
246
647
 
247
648
  /**
248
649
  * Helper type to create a configuration with only registered providers.
249
650
  * Use this with Lixa.createConfig() for type safety.
250
651
  *
652
+ * @deprecated This type is maintained for backward compatibility.
653
+ * The new inline provider configuration pattern makes this unnecessary.
654
+ *
251
655
  * @public
252
656
  */
253
- export declare type SafeLixaConfig<TProviders extends Record<string, ProviderConfig>> = LixaConfig<keyof TProviders & string> & {
657
+ export declare type SafeLixaConfig<TProviders extends Record<string, ProviderConfig>> = LixaConfig<TProviders> & {
254
658
  providers: TProviders;
255
659
  };
256
660
 
257
661
  /**
258
662
  * Represents a user session after successful OAuth authentication.
259
663
  *
664
+ * @remarks
665
+ * The Session object is returned by SessionStrategy.createSession() and contains
666
+ * the session identifier and any additional data needed for your application.
667
+ *
668
+ * The structure is intentionally flexible to support various session management
669
+ * approaches (JWT tokens, session IDs, etc.).
670
+ *
260
671
  * @public
261
672
  */
262
673
  export declare interface Session {
263
- /** The session token (typically the access token) */
674
+ /**
675
+ * The session token or identifier.
676
+ * This could be an access token, a session ID, a JWT, or any other identifier
677
+ * that your application uses to track authenticated users.
678
+ */
264
679
  token: string;
265
- /** Raw token data from the OAuth provider */
680
+ /**
681
+ * Raw session data.
682
+ * Contains the complete OAuth token response and any additional data
683
+ * your SessionStrategy adds (user info, database IDs, etc.).
684
+ *
685
+ * Typical OAuth token data includes:
686
+ * - access_token: OAuth access token
687
+ * - refresh_token: OAuth refresh token (if requested)
688
+ * - expires_in: Token expiration time in seconds
689
+ * - token_type: Token type (usually "Bearer")
690
+ * - id_token: OpenID Connect ID token (if using OIDC)
691
+ * - scope: Granted scopes
692
+ */
266
693
  raw: any;
267
694
  }
268
695
 
269
- declare interface SessionDao {
270
- saveSession(session: string, data: any, expiresInSeconds: number): Promise<void>;
271
- getSession(session: string): Promise<any | null>;
272
- deleteSession(session: string): Promise<void>;
696
+ /**
697
+ * Data access object for session storage.
698
+ *
699
+ * @remarks
700
+ * Session storage is used to persist user sessions after successful OAuth authentication.
701
+ * Sessions must persist across HTTP requests and support TTL (time-to-live).
702
+ *
703
+ * The default implementation uses in-memory cache, which is not suitable for production
704
+ * environments with multiple server instances or server restarts.
705
+ *
706
+ * For production, implement this interface with a distributed cache like Redis,
707
+ * a database, or a session store like express-session.
708
+ *
709
+ * @example
710
+ * Database implementation:
711
+ * ```typescript
712
+ * class DatabaseSessionDao implements SessionDao {
713
+ * constructor(private db: Database) {}
714
+ *
715
+ * async saveSession(sessionId: string, session: Session, expiresInSeconds: number): Promise<void> {
716
+ * const expiresAt = new Date(Date.now() + expiresInSeconds * 1000);
717
+ * await this.db.sessions.create({
718
+ * id: sessionId,
719
+ * token: session.token,
720
+ * data: session.raw,
721
+ * expiresAt
722
+ * });
723
+ * }
724
+ *
725
+ * async getSession(sessionId: string): Promise<Session | null> {
726
+ * const record = await this.db.sessions.findOne({
727
+ * id: sessionId,
728
+ * expiresAt: { $gt: new Date() }
729
+ * });
730
+ * return record ? { token: record.token, raw: record.data } : null;
731
+ * }
732
+ *
733
+ * async deleteSession(sessionId: string): Promise<void> {
734
+ * await this.db.sessions.delete({ id: sessionId });
735
+ * }
736
+ * }
737
+ * ```
738
+ *
739
+ * @public
740
+ */
741
+ export declare interface SessionDao {
742
+ /**
743
+ * Saves a session with expiration.
744
+ *
745
+ * @param sessionId - Unique session identifier
746
+ * @param data - Session data from SessionStrategy.createSession()
747
+ * @param expiresInSeconds - TTL in seconds (typically 86400 for 24 hours)
748
+ *
749
+ * @remarks
750
+ * The session data structure is defined by your SessionStrategy implementation.
751
+ * The default strategy returns \{ token: string, raw: any \}.
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * await sessionDao.saveSession('session-123', \{
756
+ * token: 'access-token',
757
+ * raw: \{ userId: '456', email: 'user\@example.com' \}
758
+ * \}, 86400);
759
+ * ```
760
+ */
761
+ saveSession(sessionId: string, data: any, expiresInSeconds: number): Promise<void>;
762
+ /**
763
+ * Retrieves a session by ID.
764
+ *
765
+ * @param sessionId - Unique session identifier
766
+ * @returns Session data or null if not found or expired
767
+ *
768
+ * @remarks
769
+ * This method is called to retrieve user session data for authenticated requests.
770
+ *
771
+ * @example
772
+ * ```typescript
773
+ * const session = await sessionDao.getSession('session-123');
774
+ * if (!session) {
775
+ * throw new Error('Session not found or expired');
776
+ * }
777
+ * ```
778
+ */
779
+ getSession(sessionId: string): Promise<any | null>;
780
+ /**
781
+ * Deletes a session (e.g., on logout).
782
+ *
783
+ * @param sessionId - Unique session identifier
784
+ *
785
+ * @remarks
786
+ * This method should be called when a user logs out to invalidate their session.
787
+ *
788
+ * @example
789
+ * ```typescript
790
+ * await sessionDao.deleteSession('session-123');
791
+ * ```
792
+ */
793
+ deleteSession(sessionId: string): Promise<void>;
273
794
  }
274
795
 
275
796
  /**
276
797
  * Strategy interface for custom session creation.
277
798
  *
799
+ * @remarks
800
+ * Implement this interface to customize how OAuth tokens are converted into
801
+ * application sessions. This is where you typically:
802
+ * - Decode ID tokens (for OpenID Connect)
803
+ * - Look up or create users in your database
804
+ * - Generate session identifiers
805
+ * - Store session data
806
+ * - Add custom claims or metadata
807
+ *
808
+ * The default implementation (DefaultSessionStrategy) simply extracts the
809
+ * access token and returns it as the session token.
810
+ *
811
+ * @example
812
+ * Custom session strategy with database integration:
813
+ * ```typescript
814
+ * class DatabaseSessionStrategy implements SessionStrategy {
815
+ * constructor(private db: Database) {}
816
+ *
817
+ * async createSession(tokenData: any): Promise<Session> {
818
+ * // Decode ID token for OIDC providers
819
+ * const idToken = tokenData.id_token;
820
+ * const payload = decodeJwt(idToken);
821
+ *
822
+ * // Create or update user in database
823
+ * const user = await this.db.users.upsert({
824
+ * email: payload.email,
825
+ * name: payload.name,
826
+ * picture: payload.picture
827
+ * });
828
+ *
829
+ * // Generate session ID
830
+ * const sessionId = generateSecureId();
831
+ *
832
+ * // Store session with tokens
833
+ * await this.db.sessions.create({
834
+ * id: sessionId,
835
+ * userId: user.id,
836
+ * accessToken: tokenData.access_token,
837
+ * refreshToken: tokenData.refresh_token,
838
+ * expiresAt: new Date(Date.now() + tokenData.expires_in * 1000)
839
+ * });
840
+ *
841
+ * return {
842
+ * token: sessionId,
843
+ * raw: {
844
+ * userId: user.id,
845
+ * email: user.email,
846
+ * ...tokenData
847
+ * }
848
+ * };
849
+ * }
850
+ * }
851
+ * ```
852
+ *
278
853
  * @public
279
854
  */
280
855
  export declare interface SessionStrategy {
281
856
  /**
282
857
  * Creates a session from OAuth token data.
283
858
  *
284
- * @param userInfo - The token data received from the OAuth provider
859
+ * @param tokenData - The token data received from the OAuth provider's token endpoint
285
860
  * @returns A Promise that resolves to a Session object
861
+ *
862
+ * @remarks
863
+ * This method is called after successfully exchanging the authorization code
864
+ * for tokens. The tokenData parameter contains the raw response from the
865
+ * provider's token endpoint.
866
+ *
867
+ * Common token data fields:
868
+ * - access_token: OAuth access token
869
+ * - refresh_token: OAuth refresh token (optional)
870
+ * - expires_in: Token expiration time in seconds
871
+ * - token_type: Token type (usually "Bearer")
872
+ * - id_token: OpenID Connect ID token (for OIDC providers)
873
+ * - scope: Granted scopes
874
+ *
875
+ * @throws \{Error\} If session creation fails (e.g., database error, invalid token)
876
+ *
877
+ * @example
878
+ * Simple implementation:
879
+ * ```typescript
880
+ * async createSession(tokenData: any): Promise<Session> {
881
+ * return {
882
+ * token: tokenData.access_token,
883
+ * raw: tokenData
884
+ * };
885
+ * }
886
+ * ```
286
887
  */
287
- createSession(userInfo: any): Promise<Session>;
888
+ createSession(tokenData: any): Promise<Session>;
288
889
  }
289
890
 
290
- declare interface StateDao {
291
- saveState(state: string, data: any, expiresInSeconds: number): Promise<void>;
292
- getState(state: string): Promise<any | null>;
891
+ /**
892
+ * Data access object for OAuth state storage.
893
+ *
894
+ * @remarks
895
+ * State storage is used during the OAuth authorization flow to:
896
+ * - Prevent CSRF attacks by validating the state parameter
897
+ * - Store PKCE code verifiers for secure token exchange
898
+ * - Maintain OAuth flow context across HTTP requests
899
+ *
900
+ * State data must persist across HTTP requests and support TTL (time-to-live).
901
+ * The default implementation uses in-memory cache, which is not suitable for production
902
+ * environments with multiple server instances or server restarts.
903
+ *
904
+ * For production, implement this interface with a distributed cache like Redis,
905
+ * or a database with TTL support.
906
+ *
907
+ * @example
908
+ * Redis implementation:
909
+ * ```typescript
910
+ * class RedisStateDao implements StateDao {
911
+ * constructor(private redis: RedisClient) {}
912
+ *
913
+ * async saveState(state: string, data: any, expiresInSeconds: number): Promise<void> {
914
+ * await this.redis.setex(`oauth:state:${state}`, expiresInSeconds, JSON.stringify(data));
915
+ * }
916
+ *
917
+ * async getState(state: string): Promise<any | null> {
918
+ * const data = await this.redis.get(`oauth:state:${state}`);
919
+ * return data ? JSON.parse(data) : null;
920
+ * }
921
+ *
922
+ * async deleteState(state: string): Promise<void> {
923
+ * await this.redis.del(`oauth:state:${state}`);
924
+ * }
925
+ * }
926
+ * ```
927
+ *
928
+ * @public
929
+ */
930
+ export declare interface StateDao {
931
+ /**
932
+ * Saves OAuth state with expiration.
933
+ *
934
+ * @param state - The state parameter value (random string for CSRF protection)
935
+ * @param data - State data including provider name and PKCE code verifier
936
+ * @param expiresInSeconds - TTL in seconds (typically 300 for 5 minutes)
937
+ *
938
+ * @remarks
939
+ * The data object MUST include the following required fields:
940
+ * - **provider** (string): Provider name for callback routing (e.g., 'google', 'github')
941
+ * - **codeVerifier** (string): PKCE code verifier for secure token exchange (64 hex characters)
942
+ * - **createdAt** (number): Unix timestamp in milliseconds for debugging and validation
943
+ *
944
+ * These fields are automatically populated by Lixa during the authorization flow.
945
+ * The state is stored when generating the authorization URL and retrieved during
946
+ * the OAuth callback to complete the PKCE flow.
947
+ *
948
+ * @see {@link StateData} for the complete state data structure
949
+ *
950
+ * @example
951
+ * ```typescript
952
+ * await stateDao.saveState('random-state-123', {
953
+ * provider: 'google',
954
+ * codeVerifier: 'abc123...',
955
+ * createdAt: Date.now()
956
+ * }, 300);
957
+ * ```
958
+ */
959
+ saveState(state: string, data: StateData, expiresInSeconds: number): Promise<void>;
960
+ /**
961
+ * Retrieves OAuth state data.
962
+ *
963
+ * @param state - The state parameter value
964
+ * @returns State data or null if not found or expired
965
+ *
966
+ * @remarks
967
+ * This method is called during the OAuth callback to validate the state
968
+ * parameter and retrieve the PKCE code verifier for token exchange.
969
+ *
970
+ * The returned data will include:
971
+ * - provider: Provider name for routing
972
+ * - codeVerifier: PKCE code verifier for token exchange
973
+ * - createdAt: Timestamp when state was created
974
+ *
975
+ * @see {@link StateData} for the complete state data structure
976
+ *
977
+ * @example
978
+ * ```typescript
979
+ * const stateData = await stateDao.getState('random-state-123');
980
+ * if (!stateData) {
981
+ * throw new Error('Invalid or expired state');
982
+ * }
983
+ * console.log(stateData.provider); // 'google'
984
+ * console.log(stateData.codeVerifier); // 'abc123...'
985
+ * ```
986
+ */
987
+ getState(state: string): Promise<StateData | null>;
988
+ /**
989
+ * Deletes OAuth state (called after successful validation).
990
+ *
991
+ * @param state - The state parameter value
992
+ *
993
+ * @remarks
994
+ * This method should be called after successfully validating the state
995
+ * to prevent replay attacks. State should only be usable once.
996
+ *
997
+ * @example
998
+ * ```typescript
999
+ * await stateDao.deleteState('random-state-123');
1000
+ * ```
1001
+ */
293
1002
  deleteState(state: string): Promise<void>;
294
1003
  }
295
1004
 
1005
+ /**
1006
+ * OAuth state data structure.
1007
+ *
1008
+ * @remarks
1009
+ * This structure is used internally by Lixa to store OAuth flow state
1010
+ * during the authorization process. It contains the information needed
1011
+ * to complete the PKCE flow and route callbacks to the correct provider.
1012
+ *
1013
+ * @public
1014
+ */
1015
+ export declare interface StateData {
1016
+ /**
1017
+ * Provider name for callback routing.
1018
+ * Used to identify which provider configuration to use when handling the callback.
1019
+ *
1020
+ * @example 'google', 'github', 'custom'
1021
+ */
1022
+ provider: string;
1023
+ /**
1024
+ * PKCE code verifier for secure token exchange.
1025
+ * A cryptographically random string (64 hex characters) used in the PKCE flow
1026
+ * to prevent authorization code interception attacks.
1027
+ *
1028
+ * @see RFC 7636 - Proof Key for Code Exchange
1029
+ */
1030
+ codeVerifier: string;
1031
+ /**
1032
+ * Unix timestamp in milliseconds when the state was created.
1033
+ * Used for debugging and validation purposes.
1034
+ */
1035
+ createdAt: number;
1036
+ }
1037
+
296
1038
  export { }