@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.
- package/README.md +717 -29
- package/dist/dao/types.d.ts +239 -5
- package/dist/dao/types.d.ts.map +1 -1
- package/dist/export-types/index.d.ts +785 -43
- package/dist/index.cjs +277 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +787 -40
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +277 -41
- package/dist/index.js.map +1 -1
- package/dist/lixa.d.ts +185 -16
- package/dist/lixa.d.ts.map +1 -1
- package/dist/models/session.d.ts +107 -4
- package/dist/models/session.d.ts.map +1 -1
- package/dist/providers/IProvider.d.ts +121 -4
- package/dist/providers/IProvider.d.ts.map +1 -1
- package/dist/providers/index.d.ts +0 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/types.d.ts +129 -14
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -16
- package/dist/IProvider-2tDwRAnH.d.cts +0 -18
- package/dist/IProvider-2tDwRAnH.d.ts +0 -18
- package/dist/export-types/providers.d.ts +0 -61
- package/dist/providers/github.d.ts +0 -13
- package/dist/providers/github.d.ts.map +0 -1
- package/dist/providers/google.d.ts +0 -13
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers-entry.cjs +0 -48
- package/dist/providers-entry.cjs.map +0 -1
- package/dist/providers-entry.d.cts +0 -25
- package/dist/providers-entry.d.ts +0 -22
- package/dist/providers-entry.d.ts.map +0 -1
- package/dist/providers-entry.js +0 -20
- package/dist/providers-entry.js.map +0 -1
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
186
|
+
* import { GoogleProvider } from '@vunexa/lixa-providers';
|
|
59
187
|
*
|
|
60
|
-
*
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
-
* @
|
|
140
|
-
*
|
|
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<
|
|
368
|
+
static createConfig<T extends Record<string, ProviderConfig>>(config: LixaConfig<T> & {
|
|
143
369
|
providers: T;
|
|
144
|
-
}): LixaConfig<
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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<
|
|
217
|
-
/**
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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<
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
|
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(
|
|
888
|
+
createSession(tokenData: any): Promise<Session>;
|
|
288
889
|
}
|
|
289
890
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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 { }
|