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