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