@vunexa/lixa 0.0.1-alpha.16 → 0.0.1-alpha.18

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.
Files changed (46) hide show
  1. package/dist/IProvider-2tDwRAnH.d.cts +18 -0
  2. package/dist/IProvider-2tDwRAnH.d.ts +18 -0
  3. package/dist/index.cjs +357 -15
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +261 -0
  6. package/dist/index.js +319 -10
  7. package/dist/index.js.map +1 -1
  8. package/dist/providers-entry.cjs +47 -26
  9. package/dist/providers-entry.cjs.map +1 -0
  10. package/dist/providers-entry.d.cts +25 -0
  11. package/dist/providers-entry.js +19 -21
  12. package/dist/providers-entry.js.map +1 -1
  13. package/package.json +20 -10
  14. package/dist/dao/session-cache.cjs +0 -24
  15. package/dist/dao/session-cache.js +0 -18
  16. package/dist/dao/session-cache.js.map +0 -1
  17. package/dist/dao/state-cache.cjs +0 -24
  18. package/dist/dao/state-cache.js +0 -18
  19. package/dist/dao/state-cache.js.map +0 -1
  20. package/dist/dao/types.cjs +0 -3
  21. package/dist/dao/types.js +0 -2
  22. package/dist/dao/types.js.map +0 -1
  23. package/dist/lixa.cjs +0 -295
  24. package/dist/lixa.js +0 -289
  25. package/dist/lixa.js.map +0 -1
  26. package/dist/models/session.cjs +0 -34
  27. package/dist/models/session.js +0 -30
  28. package/dist/models/session.js.map +0 -1
  29. package/dist/providers/IProvider.cjs +0 -3
  30. package/dist/providers/IProvider.js +0 -2
  31. package/dist/providers/IProvider.js.map +0 -1
  32. package/dist/providers/github.cjs +0 -15
  33. package/dist/providers/github.js +0 -12
  34. package/dist/providers/github.js.map +0 -1
  35. package/dist/providers/google.cjs +0 -15
  36. package/dist/providers/google.js +0 -12
  37. package/dist/providers/google.js.map +0 -1
  38. package/dist/providers/index.cjs +0 -8
  39. package/dist/providers/index.js +0 -3
  40. package/dist/providers/index.js.map +0 -1
  41. package/dist/types.cjs +0 -3
  42. package/dist/types.js +0 -2
  43. package/dist/types.js.map +0 -1
  44. package/dist/utils/constants.cjs +0 -8
  45. package/dist/utils/constants.js +0 -4
  46. package/dist/utils/constants.js.map +0 -1
@@ -0,0 +1,261 @@
1
+ import { I as IProvider } from './IProvider-2tDwRAnH.cjs';
2
+
3
+ interface StateDao {
4
+ saveState(state: string, data: any, expiresInSeconds: number): Promise<void>;
5
+ getState(state: string): Promise<any | null>;
6
+ deleteState(state: string): Promise<void>;
7
+ }
8
+ interface SessionDao {
9
+ saveSession(session: string, data: any, expiresInSeconds: number): Promise<void>;
10
+ getSession(session: string): Promise<any | null>;
11
+ deleteSession(session: string): Promise<void>;
12
+ }
13
+
14
+ /**
15
+ * Represents a user session after successful OAuth authentication.
16
+ *
17
+ * @public
18
+ */
19
+ interface Session {
20
+ /** The session token (typically the access token) */
21
+ token: string;
22
+ /** Raw token data from the OAuth provider */
23
+ raw: any;
24
+ }
25
+ /**
26
+ * Strategy interface for custom session creation.
27
+ *
28
+ * @public
29
+ */
30
+ interface SessionStrategy {
31
+ /**
32
+ * Creates a session from OAuth token data.
33
+ *
34
+ * @param userInfo - The token data received from the OAuth provider
35
+ * @returns A Promise that resolves to a Session object
36
+ */
37
+ createSession(userInfo: any): Promise<Session>;
38
+ }
39
+ /**
40
+ * Default session strategy that works with any OAuth provider.
41
+ * Extracts common token information and creates a standardized session.
42
+ *
43
+ * @public
44
+ */
45
+ declare class DefaultSessionStrategy implements SessionStrategy {
46
+ /**
47
+ * Creates a session from OAuth token data.
48
+ * Handles common OAuth token formats and extracts the access token.
49
+ *
50
+ * @param tokenData - The token data received from the OAuth provider
51
+ * @returns A Promise that resolves to a Session object
52
+ */
53
+ createSession(tokenData: any): Promise<Session>;
54
+ }
55
+
56
+ /**
57
+ * Configuration for an OAuth provider.
58
+ *
59
+ * @public
60
+ */
61
+ interface ProviderConfig {
62
+ /** The OAuth client ID provided by the provider */
63
+ clientId: string;
64
+ /** The OAuth client secret provided by the provider */
65
+ clientSecret: string;
66
+ /** The redirect URI registered with the provider */
67
+ redirectUri: string;
68
+ /** Array of OAuth scopes to request */
69
+ scopes: string[];
70
+ /** Additional provider-specific configuration parameters */
71
+ extraConfig?: Record<string, any>;
72
+ }
73
+ /**
74
+ * Main configuration object for Lixa.
75
+ * Only allows providers that have been registered through registerProvider.
76
+ *
77
+ * @public
78
+ */
79
+ interface LixaConfig<TRegisteredProviders extends string = string> {
80
+ /** Map of registered provider names to their configurations */
81
+ providers: Record<TRegisteredProviders, ProviderConfig>;
82
+ /** Optional custom session creation strategy */
83
+ sessionStrategy?: SessionStrategy;
84
+ /** Optional custom state storage implementation */
85
+ stateDao?: StateDao;
86
+ /** Optiona: custom session storage implementation */
87
+ sessionDao?: SessionDao;
88
+ }
89
+ /**
90
+ * Helper type to create a configuration with only registered providers.
91
+ * Use this with Lixa.createConfig() for type safety.
92
+ *
93
+ * @public
94
+ */
95
+ type SafeLixaConfig<TProviders extends Record<string, ProviderConfig>> = LixaConfig<keyof TProviders & string> & {
96
+ providers: TProviders;
97
+ };
98
+
99
+ /**
100
+ * Type representing the keys of configured providers
101
+ */
102
+ type ConfiguredProviderKey<T extends LixaConfig<any>> = keyof T['providers'];
103
+ /**
104
+ * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library.
105
+ *
106
+ * @remarks
107
+ * Lixa simplifies multi-provider authentication flows and supports extensible session management.
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * import { Lixa } from '@vunexa/lixa';
112
+ * import { GoogleProvider } from '@vunexa/lixa/providers';
113
+ *
114
+ * // Register providers before using them
115
+ * Lixa.registerProvider({ google: new GoogleProvider() });
116
+ *
117
+ * const config = Lixa.createConfig({
118
+ * providers: {
119
+ * google: {
120
+ * clientId: 'your-client-id',
121
+ * clientSecret: 'your-client-secret',
122
+ * redirectUri: 'https://yourapp.com/auth/google/callback',
123
+ * scopes: ['openid', 'email', 'profile']
124
+ * }
125
+ * }
126
+ * });
127
+ *
128
+ * const lixa = new Lixa(config);
129
+ * ```
130
+ *
131
+ * @public
132
+ */
133
+ declare class Lixa<TConfig extends LixaConfig<any> = LixaConfig> {
134
+ private static CONFIGURED_PROVIDERS;
135
+ private static LOCAL_STATE_CACHE;
136
+ private static LOCAL_SESSION_CACHE;
137
+ private static DEFAULT_SESSION_STRATEGY;
138
+ private config;
139
+ private stateDao;
140
+ private sesionDao;
141
+ private sessionStrategy;
142
+ /**
143
+ * Creates a new Lixa instance with the provided configuration.
144
+ *
145
+ * @param config - The configuration object containing provider settings and optional session strategy
146
+ */
147
+ constructor(config: TConfig);
148
+ /**
149
+ * Checks if a provider is both registered and configured for this instance.
150
+ * This is a type guard that narrows the provider type for use with getAuthUrl.
151
+ *
152
+ * @param provider - The provider name to check (case-insensitive)
153
+ * @returns True if the provider is registered and configured, false otherwise
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * if (lixa.isProviderConfigured(provider)) {
158
+ * // TypeScript now knows provider is a valid ConfiguredProviderKey
159
+ * const authUrl = lixa.getAuthUrl(provider, state);
160
+ * }
161
+ * ```
162
+ */
163
+ isProviderConfigured<T extends string>(provider: T): provider is T & ConfiguredProviderKey<TConfig>;
164
+ /**
165
+ * Registers custom OAuth providers for use with Lixa.
166
+ *
167
+ * @param providerMap - A map of provider names to IProvider implementations
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * class CustomProvider implements IProvider {
172
+ * authorizationEndpoint = 'https://custom.com/oauth/authorize';
173
+ * tokenEndpoint = 'https://custom.com/oauth/token';
174
+ * userInfoEndpoint = 'https://custom.com/api/user';
175
+ * }
176
+ *
177
+ * Lixa.registerProvider({ custom: new CustomProvider() });
178
+ * ```
179
+ */
180
+ static registerProvider<T extends Record<string, IProvider>>(providerMap: T): void;
181
+ /**
182
+ * Gets the list of registered provider names.
183
+ *
184
+ * @returns Array of registered provider names
185
+ */
186
+ static getRegisteredProviders(): string[];
187
+ /**
188
+ * Creates a type-safe configuration that only allows registered providers.
189
+ *
190
+ * @param config - Configuration object with providers that must be registered
191
+ * @returns The same configuration object, but with type safety for registered providers
192
+ */
193
+ static createConfig<T extends Record<string, ProviderConfig>>(config: LixaConfig<keyof T & string> & {
194
+ providers: T;
195
+ }): LixaConfig<keyof T & string>;
196
+ /**
197
+ * Generates a cryptographically secure random state parameter for OAuth flows.
198
+ *
199
+ * @returns A 32-character hexadecimal string
200
+ *
201
+ * @remarks
202
+ * The state parameter is used to prevent CSRF attacks in OAuth flows.
203
+ */
204
+ static generateRandomState(): string;
205
+ /**
206
+ * Generates a cryptographically secure code verifier for PKCE flows.
207
+ *
208
+ * @returns A 64-character hexadecimal string
209
+ *
210
+ * @remarks
211
+ * The code verifier is used in PKCE (Proof Key for Code Exchange) to enhance security.
212
+ */
213
+ private static generateCodeVerifier;
214
+ private static buildCodeChallenge;
215
+ /**
216
+ * Generates the authorization URL for the specified provider.
217
+ *
218
+ * @param provider - The provider name (must be a configured provider key)
219
+ * @param state - The state parameter for CSRF protection
220
+ * @returns The complete authorization URL to redirect users to
221
+ *
222
+ * @throws Error when the provider is not configured
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * const state = Lixa.generateRandomState();
227
+ * const authUrl = lixa.getAuthUrl('google', state);
228
+ * res.redirect(authUrl);
229
+ * ```
230
+ */
231
+ getAuthUrl(provider: ConfiguredProviderKey<TConfig> | string, state: string): string;
232
+ /**
233
+ * Handles the OAuth callback and creates a user session.
234
+ *
235
+ * @param provider - The provider name (must be a configured provider key)
236
+ * @param code - The authorization code from the provider
237
+ * @param state - The state parameter for validation
238
+ * @returns A Promise that resolves to the session ID
239
+ *
240
+ * @throws Error when code or state is missing/invalid, or provider is not configured
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * const sessionId = await lixa.handleCallback({
245
+ * provider: 'google',
246
+ * code: req.query.code,
247
+ * state: req.query.state
248
+ * });
249
+ * ```
250
+ */
251
+ handleCallback({ provider, code, state, }: {
252
+ provider: ConfiguredProviderKey<TConfig> | string;
253
+ code: string;
254
+ state?: string;
255
+ }): Promise<string>;
256
+ fetchSessionInfo(sessionId: string): Promise<Session | null>;
257
+ private exchangeCodeForToken;
258
+ private findProviderByType;
259
+ }
260
+
261
+ export { DefaultSessionStrategy, IProvider, Lixa, type LixaConfig, type ProviderConfig, type SafeLixaConfig, type Session, type SessionStrategy };
package/dist/index.js CHANGED
@@ -1,11 +1,320 @@
1
- /**
2
- * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library for backend applications.
3
- *
4
- * @remarks
5
- * This package simplifies multi-provider authentication flows (e.g., Google, GitHub), supports extensible session management, and enables custom provider registration.
6
- *
7
- * @packageDocumentation
8
- */
9
- export { Lixa } from "./lixa";
10
- export { DefaultSessionStrategy } from "./models/session";
1
+ // src/lixa.ts
2
+ import { randomBytes } from "crypto";
3
+
4
+ // src/dao/state-cache.ts
5
+ import NodeCache from "node-cache";
6
+ var LocalStateCache = class {
7
+ cache;
8
+ constructor(defaultTtlSeconds = 600) {
9
+ this.cache = new NodeCache({ stdTTL: defaultTtlSeconds });
10
+ }
11
+ async saveState(state, data, expiresInSeconds) {
12
+ this.cache.set(state, data, expiresInSeconds);
13
+ }
14
+ async getState(state) {
15
+ return this.cache.get(state) || null;
16
+ }
17
+ async deleteState(state) {
18
+ this.cache.del(state);
19
+ }
20
+ };
21
+
22
+ // src/lixa.ts
23
+ import crypto from "crypto";
24
+
25
+ // src/dao/session-cache.ts
26
+ import NodeCache2 from "node-cache";
27
+ var LocalSessionCache = class {
28
+ cache;
29
+ constructor(defaultTtlSeconds = 600) {
30
+ this.cache = new NodeCache2({ stdTTL: defaultTtlSeconds });
31
+ }
32
+ async saveSession(state, data, expiresInSeconds) {
33
+ this.cache.set(state, data, expiresInSeconds);
34
+ }
35
+ async getSession(state) {
36
+ return this.cache.get(state) || null;
37
+ }
38
+ async deleteSession(state) {
39
+ this.cache.del(state);
40
+ }
41
+ };
42
+
43
+ // src/models/session.ts
44
+ var DefaultSessionStrategy = class {
45
+ /**
46
+ * Creates a session from OAuth token data.
47
+ * Handles common OAuth token formats and extracts the access token.
48
+ *
49
+ * @param tokenData - The token data received from the OAuth provider
50
+ * @returns A Promise that resolves to a Session object
51
+ */
52
+ async createSession(tokenData) {
53
+ const accessToken = tokenData.access_token || tokenData.accessToken || tokenData.token || tokenData;
54
+ if (!accessToken || typeof accessToken !== "string") {
55
+ throw new Error("No valid access token found in OAuth response");
56
+ }
57
+ return {
58
+ token: accessToken,
59
+ raw: tokenData
60
+ };
61
+ }
62
+ };
63
+
64
+ // src/lixa.ts
65
+ var Lixa = class _Lixa {
66
+ static CONFIGURED_PROVIDERS = /* @__PURE__ */ new Map();
67
+ static LOCAL_STATE_CACHE = new LocalStateCache();
68
+ static LOCAL_SESSION_CACHE = new LocalSessionCache();
69
+ static DEFAULT_SESSION_STRATEGY = new DefaultSessionStrategy();
70
+ config;
71
+ stateDao;
72
+ sesionDao;
73
+ sessionStrategy;
74
+ /**
75
+ * Creates a new Lixa instance with the provided configuration.
76
+ *
77
+ * @param config - The configuration object containing provider settings and optional session strategy
78
+ */
79
+ constructor(config) {
80
+ const configuredProviders = Object.keys(config.providers);
81
+ const registeredProviders = _Lixa.getRegisteredProviders();
82
+ for (const provider of configuredProviders) {
83
+ if (!registeredProviders.includes(provider.toLowerCase())) {
84
+ throw new Error(`Provider '${provider}' is not registered. Please register it first using Lixa.registerProvider()`);
85
+ }
86
+ }
87
+ this.config = config;
88
+ this.stateDao = config.stateDao || _Lixa.LOCAL_STATE_CACHE;
89
+ this.sesionDao = config.sessionDao || _Lixa.LOCAL_SESSION_CACHE;
90
+ this.sessionStrategy = config.sessionStrategy || _Lixa.DEFAULT_SESSION_STRATEGY;
91
+ }
92
+ /**
93
+ * Checks if a provider is both registered and configured for this instance.
94
+ * This is a type guard that narrows the provider type for use with getAuthUrl.
95
+ *
96
+ * @param provider - The provider name to check (case-insensitive)
97
+ * @returns True if the provider is registered and configured, false otherwise
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * if (lixa.isProviderConfigured(provider)) {
102
+ * // TypeScript now knows provider is a valid ConfiguredProviderKey
103
+ * const authUrl = lixa.getAuthUrl(provider, state);
104
+ * }
105
+ * ```
106
+ */
107
+ isProviderConfigured(provider) {
108
+ const providerType = provider.toLowerCase();
109
+ return _Lixa.CONFIGURED_PROVIDERS.has(providerType) && this.config.providers.hasOwnProperty(providerType);
110
+ }
111
+ /**
112
+ * Registers custom OAuth providers for use with Lixa.
113
+ *
114
+ * @param providerMap - A map of provider names to IProvider implementations
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * class CustomProvider implements IProvider {
119
+ * authorizationEndpoint = 'https://custom.com/oauth/authorize';
120
+ * tokenEndpoint = 'https://custom.com/oauth/token';
121
+ * userInfoEndpoint = 'https://custom.com/api/user';
122
+ * }
123
+ *
124
+ * Lixa.registerProvider({ custom: new CustomProvider() });
125
+ * ```
126
+ */
127
+ static registerProvider(providerMap) {
128
+ Object.entries(providerMap).forEach(([key, providerImpl]) => {
129
+ _Lixa.CONFIGURED_PROVIDERS.set(key.toLowerCase(), providerImpl);
130
+ });
131
+ }
132
+ /**
133
+ * Gets the list of registered provider names.
134
+ *
135
+ * @returns Array of registered provider names
136
+ */
137
+ static getRegisteredProviders() {
138
+ return Array.from(_Lixa.CONFIGURED_PROVIDERS.keys());
139
+ }
140
+ /**
141
+ * Creates a type-safe configuration that only allows registered providers.
142
+ *
143
+ * @param config - Configuration object with providers that must be registered
144
+ * @returns The same configuration object, but with type safety for registered providers
145
+ */
146
+ static createConfig(config) {
147
+ const configuredProviders = Object.keys(config.providers);
148
+ const registeredProviders = _Lixa.getRegisteredProviders();
149
+ for (const provider of configuredProviders) {
150
+ if (!registeredProviders.includes(provider.toLowerCase())) {
151
+ throw new Error(`Provider '${provider}' is not registered. Please register it first using Lixa.registerProvider()`);
152
+ }
153
+ }
154
+ return config;
155
+ }
156
+ /**
157
+ * Generates a cryptographically secure random state parameter for OAuth flows.
158
+ *
159
+ * @returns A 32-character hexadecimal string
160
+ *
161
+ * @remarks
162
+ * The state parameter is used to prevent CSRF attacks in OAuth flows.
163
+ */
164
+ static generateRandomState() {
165
+ return randomBytes(16).toString("hex");
166
+ }
167
+ /**
168
+ * Generates a cryptographically secure code verifier for PKCE flows.
169
+ *
170
+ * @returns A 64-character hexadecimal string
171
+ *
172
+ * @remarks
173
+ * The code verifier is used in PKCE (Proof Key for Code Exchange) to enhance security.
174
+ */
175
+ static generateCodeVerifier() {
176
+ return randomBytes(32).toString("hex");
177
+ }
178
+ static buildCodeChallenge(codeVerifier) {
179
+ const hash = crypto.createHash("sha256").update(codeVerifier).digest("base64");
180
+ return hash.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
181
+ }
182
+ /**
183
+ * Generates the authorization URL for the specified provider.
184
+ *
185
+ * @param provider - The provider name (must be a configured provider key)
186
+ * @param state - The state parameter for CSRF protection
187
+ * @returns The complete authorization URL to redirect users to
188
+ *
189
+ * @throws Error when the provider is not configured
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const state = Lixa.generateRandomState();
194
+ * const authUrl = lixa.getAuthUrl('google', state);
195
+ * res.redirect(authUrl);
196
+ * ```
197
+ */
198
+ getAuthUrl(provider, state) {
199
+ const providerType = String(provider).toLowerCase();
200
+ const providerConfig = this.findProviderByType(providerType);
201
+ const providerImpl = _Lixa.CONFIGURED_PROVIDERS.get(providerType);
202
+ if (!providerConfig || !providerImpl) {
203
+ throw new Error(`Provider ${providerType} not configured`);
204
+ }
205
+ const codeVerifier = _Lixa.generateCodeVerifier();
206
+ const codeChallenge = _Lixa.buildCodeChallenge(codeVerifier);
207
+ this.stateDao.saveState(
208
+ state,
209
+ {
210
+ createdAt: Date.now(),
211
+ provider: providerType,
212
+ codeVerifier
213
+ },
214
+ 300
215
+ // 5 minutes in seconds
216
+ );
217
+ const params = new URLSearchParams({
218
+ client_id: providerConfig.clientId,
219
+ redirect_uri: providerConfig.redirectUri,
220
+ scope: providerConfig.scopes.join(" "),
221
+ state,
222
+ response_type: "code",
223
+ code_challenge: codeChallenge,
224
+ code_challenge_method: "S256",
225
+ ...providerConfig.extraConfig
226
+ });
227
+ return `${providerImpl.authorizationEndpoint}?${params.toString()}`;
228
+ }
229
+ /**
230
+ * Handles the OAuth callback and creates a user session.
231
+ *
232
+ * @param provider - The provider name (must be a configured provider key)
233
+ * @param code - The authorization code from the provider
234
+ * @param state - The state parameter for validation
235
+ * @returns A Promise that resolves to the session ID
236
+ *
237
+ * @throws Error when code or state is missing/invalid, or provider is not configured
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const sessionId = await lixa.handleCallback({
242
+ * provider: 'google',
243
+ * code: req.query.code,
244
+ * state: req.query.state
245
+ * });
246
+ * ```
247
+ */
248
+ async handleCallback({
249
+ provider,
250
+ code,
251
+ state
252
+ }) {
253
+ if (!code || code.trim() === "") {
254
+ throw new Error("Invalid or missing code in callback");
255
+ }
256
+ if (!state || state.trim() === "") {
257
+ throw new Error("Invalid or missing state in callback");
258
+ }
259
+ const cachedState = await this.stateDao.getState(state);
260
+ if (!cachedState) {
261
+ throw new Error("Invalid or expired state");
262
+ }
263
+ await this.stateDao.deleteState(state);
264
+ const codeVerifier = cachedState.codeVerifier;
265
+ const providerType = String(provider).toLowerCase();
266
+ const providerConfig = this.findProviderByType(providerType);
267
+ const providerImpl = _Lixa.CONFIGURED_PROVIDERS.get(providerType);
268
+ if (!providerConfig || !providerImpl) {
269
+ throw new Error(`Provider ${String(provider)} not configured`);
270
+ }
271
+ const tokens = await this.exchangeCodeForToken(
272
+ code,
273
+ providerConfig,
274
+ providerImpl,
275
+ codeVerifier
276
+ );
277
+ const session = await this.sessionStrategy.createSession(tokens);
278
+ const sessionId = randomBytes(32).toString("hex");
279
+ await this.sesionDao.saveSession(sessionId, session, 86400);
280
+ return sessionId;
281
+ }
282
+ fetchSessionInfo(sessionId) {
283
+ return this.sesionDao.getSession(sessionId);
284
+ }
285
+ async exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier) {
286
+ const body = {
287
+ client_id: providerConfig.clientId,
288
+ client_secret: providerConfig.clientSecret,
289
+ code,
290
+ redirect_uri: providerConfig.redirectUri,
291
+ grant_type: "authorization_code"
292
+ };
293
+ if (codeVerifier) {
294
+ body.code_verifier = codeVerifier;
295
+ }
296
+ const params = new URLSearchParams(body);
297
+ const response = await fetch(providerImpl.tokenEndpoint, {
298
+ method: "POST",
299
+ headers: {
300
+ "Content-Type": "application/x-www-form-urlencoded",
301
+ Accept: "application/json"
302
+ },
303
+ body: params.toString()
304
+ });
305
+ if (!response.ok) {
306
+ throw new Error(
307
+ `Token exchange failed: ${response.status} ${response.statusText}`
308
+ );
309
+ }
310
+ return response.json();
311
+ }
312
+ findProviderByType(providerType) {
313
+ return this.config.providers[providerType];
314
+ }
315
+ };
316
+ export {
317
+ DefaultSessionStrategy,
318
+ Lixa
319
+ };
11
320
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAM9B,OAAO,EAAsC,sBAAsB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"sources":["../src/lixa.ts","../src/dao/state-cache.ts","../src/dao/session-cache.ts","../src/models/session.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport { type LixaConfig, type ProviderConfig } from \"./types\";\nimport { IProvider } from \"./providers\";\nimport { LocalStateCache } from \"./dao/state-cache\";\nimport { SessionDao, StateDao } from \"./dao/types\";\nimport crypto from \"crypto\";\nimport { LocalSessionCache } from \"./dao/session-cache\";\nimport { type Session, type SessionStrategy, DefaultSessionStrategy } from \"./models/session\";\n\n/**\n * Global registry of registered provider names\n */\ntype RegisteredProviders = string;\n\n/**\n * Type representing the keys of configured providers\n */\ntype ConfiguredProviderKey<T extends LixaConfig<any>> = keyof T['providers'];\n\n/**\n * A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library.\n *\n * @remarks\n * Lixa simplifies multi-provider authentication flows and supports extensible session management.\n *\n * @example\n * ```typescript\n * import { Lixa } from '@vunexa/lixa';\n * import { GoogleProvider } from '@vunexa/lixa/providers';\n * \n * // Register providers before using them\n * Lixa.registerProvider({ google: new GoogleProvider() });\n * \n * const config = Lixa.createConfig({\n * providers: {\n * google: {\n * clientId: 'your-client-id',\n * clientSecret: 'your-client-secret',\n * redirectUri: 'https://yourapp.com/auth/google/callback',\n * scopes: ['openid', 'email', 'profile']\n * }\n * }\n * });\n * \n * const lixa = new Lixa(config);\n * ```\n *\n * @public\n */\nclass Lixa<TConfig extends LixaConfig<any> = LixaConfig> {\n private static CONFIGURED_PROVIDERS: Map<string, IProvider> = new Map();\n private static LOCAL_STATE_CACHE = new LocalStateCache();\n private static LOCAL_SESSION_CACHE = new LocalSessionCache();\n private static DEFAULT_SESSION_STRATEGY = new DefaultSessionStrategy();\n private config: TConfig;\n private stateDao: StateDao;\n private sesionDao: SessionDao;\n private sessionStrategy: SessionStrategy;\n\n /**\n * Creates a new Lixa instance with the provided configuration.\n *\n * @param config - The configuration object containing provider settings and optional session strategy\n */\n constructor(config: TConfig) {\n // Validate that all providers in config are registered\n const configuredProviders = Object.keys(config.providers);\n const registeredProviders = Lixa.getRegisteredProviders();\n \n for (const provider of configuredProviders) {\n if (!registeredProviders.includes(provider.toLowerCase())) {\n throw new Error(`Provider '${provider}' is not registered. Please register it first using Lixa.registerProvider()`);\n }\n }\n \n this.config = config;\n this.stateDao = config.stateDao || Lixa.LOCAL_STATE_CACHE;\n this.sesionDao = config.sessionDao || Lixa.LOCAL_SESSION_CACHE;\n this.sessionStrategy = config.sessionStrategy || Lixa.DEFAULT_SESSION_STRATEGY;\n }\n\n /**\n * Checks if a provider is both registered and configured for this instance.\n * This is a type guard that narrows the provider type for use with getAuthUrl.\n *\n * @param provider - The provider name to check (case-insensitive)\n * @returns True if the provider is registered and configured, false otherwise\n *\n * @example\n * ```typescript\n * if (lixa.isProviderConfigured(provider)) {\n * // TypeScript now knows provider is a valid ConfiguredProviderKey\n * const authUrl = lixa.getAuthUrl(provider, state);\n * }\n * ```\n */\n public isProviderConfigured<T extends string>(provider: T): provider is T & ConfiguredProviderKey<TConfig> {\n const providerType = provider.toLowerCase();\n return Lixa.CONFIGURED_PROVIDERS.has(providerType) &&\n this.config.providers.hasOwnProperty(providerType);\n }\n\n /**\n * Registers custom OAuth providers for use with Lixa.\n *\n * @param providerMap - A map of provider names to IProvider implementations\n *\n * @example\n * ```typescript\n * class CustomProvider implements IProvider {\n * authorizationEndpoint = 'https://custom.com/oauth/authorize';\n * tokenEndpoint = 'https://custom.com/oauth/token';\n * userInfoEndpoint = 'https://custom.com/api/user';\n * }\n *\n * Lixa.registerProvider({ custom: new CustomProvider() });\n * ```\n */\n public static registerProvider<T extends Record<string, IProvider>>(providerMap: T): void {\n Object.entries(providerMap).forEach(([key, providerImpl]) => {\n Lixa.CONFIGURED_PROVIDERS.set(key.toLowerCase(), providerImpl);\n });\n }\n\n /**\n * Gets the list of registered provider names.\n * \n * @returns Array of registered provider names\n */\n public static getRegisteredProviders(): string[] {\n return Array.from(Lixa.CONFIGURED_PROVIDERS.keys());\n }\n\n /**\n * Creates a type-safe configuration that only allows registered providers.\n * \n * @param config - Configuration object with providers that must be registered\n * @returns The same configuration object, but with type safety for registered providers\n */\n public static createConfig<T extends Record<string, ProviderConfig>>(\n config: LixaConfig<keyof T & string> & { providers: T }\n ): LixaConfig<keyof T & string> {\n // Validate that all providers in config are registered\n const configuredProviders = Object.keys(config.providers);\n const registeredProviders = Lixa.getRegisteredProviders();\n \n for (const provider of configuredProviders) {\n if (!registeredProviders.includes(provider.toLowerCase())) {\n throw new Error(`Provider '${provider}' is not registered. Please register it first using Lixa.registerProvider()`);\n }\n }\n \n return config;\n }\n\n /**\n * Generates a cryptographically secure random state parameter for OAuth flows.\n *\n * @returns A 32-character hexadecimal string\n *\n * @remarks\n * The state parameter is used to prevent CSRF attacks in OAuth flows.\n */\n public static generateRandomState(): string {\n return randomBytes(16).toString(\"hex\");\n }\n\n /**\n * Generates a cryptographically secure code verifier for PKCE flows.\n *\n * @returns A 64-character hexadecimal string\n *\n * @remarks\n * The code verifier is used in PKCE (Proof Key for Code Exchange) to enhance security.\n */\n private static generateCodeVerifier(): string {\n return randomBytes(32).toString(\"hex\");\n }\n\n private static buildCodeChallenge(codeVerifier: string): string {\n const hash = crypto\n .createHash(\"sha256\")\n .update(codeVerifier)\n .digest(\"base64\");\n\n // Convert to base64url\n return hash.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n }\n\n /**\n * Generates the authorization URL for the specified provider.\n *\n * @param provider - The provider name (must be a configured provider key)\n * @param state - The state parameter for CSRF protection\n * @returns The complete authorization URL to redirect users to\n *\n * @throws Error when the provider is not configured\n *\n * @example\n * ```typescript\n * const state = Lixa.generateRandomState();\n * const authUrl = lixa.getAuthUrl('google', state);\n * res.redirect(authUrl);\n * ```\n */\n public getAuthUrl(provider: ConfiguredProviderKey<TConfig> | string, state: string): string {\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n const providerImpl = Lixa.CONFIGURED_PROVIDERS.get(providerType);\n\n if (!providerConfig || !providerImpl) {\n throw new Error(`Provider ${providerType} not configured`);\n }\n\n const codeVerifier = Lixa.generateCodeVerifier();\n const codeChallenge = Lixa.buildCodeChallenge(codeVerifier);\n\n // Cache the state paramaeter with TTL of 5 minutes (300 seconds)\n // We dont care about value. we are onl interested in key existence\n this.stateDao.saveState(\n state,\n {\n createdAt: Date.now(),\n provider: providerType,\n codeVerifier,\n },\n 300 // 5 minutes in seconds\n );\n\n const params = new URLSearchParams({\n client_id: providerConfig.clientId,\n redirect_uri: providerConfig.redirectUri,\n scope: providerConfig.scopes.join(\" \"),\n state,\n response_type: \"code\",\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n ...providerConfig.extraConfig,\n });\n\n return `${providerImpl.authorizationEndpoint}?${params.toString()}`;\n }\n\n /**\n * Handles the OAuth callback and creates a user session.\n *\n * @param provider - The provider name (must be a configured provider key)\n * @param code - The authorization code from the provider\n * @param state - The state parameter for validation\n * @returns A Promise that resolves to the session ID\n *\n * @throws Error when code or state is missing/invalid, or provider is not configured\n *\n * @example\n * ```typescript\n * const sessionId = await lixa.handleCallback({\n * provider: 'google',\n * code: req.query.code,\n * state: req.query.state\n * });\n * ```\n */\n public async handleCallback({\n provider,\n code,\n state,\n }: {\n provider: ConfiguredProviderKey<TConfig> | string;\n code: string;\n state?: string;\n }): Promise<string> {\n if (!code || code.trim() === \"\") {\n throw new Error(\"Invalid or missing code in callback\");\n }\n\n if (!state || state.trim() === \"\") {\n throw new Error(\"Invalid or missing state in callback\");\n }\n\n //Validate state here\n const cachedState = await this.stateDao.getState(state);\n if (!cachedState) {\n throw new Error(\"Invalid or expired state\");\n }\n // State is valid, remove it from cache to prevent reuse\n await this.stateDao.deleteState(state);\n\n //Get code verifier from cached state\n const codeVerifier = cachedState.codeVerifier;\n\n const providerType = String(provider).toLowerCase();\n const providerConfig = this.findProviderByType(providerType);\n const providerImpl = Lixa.CONFIGURED_PROVIDERS.get(providerType);\n\n if (!providerConfig || !providerImpl) {\n throw new Error(`Provider ${String(provider)} not configured`);\n }\n\n // Exchange code for tokens and fetch user info here.\n const tokens = await this.exchangeCodeForToken(\n code,\n providerConfig,\n providerImpl,\n codeVerifier\n );\n\n\n const session = await this.sessionStrategy.createSession(tokens);\n\n // Generate unique session ID\n const sessionId = randomBytes(32).toString(\"hex\");\n\n // Store session with 24 hour TTL (86400 seconds)\n await this.sesionDao.saveSession(sessionId, session, 86400);\n\n return sessionId;\n }\n\n public fetchSessionInfo(sessionId: string): Promise<Session | null> {\n return this.sesionDao.getSession(sessionId);\n }\n\n private async exchangeCodeForToken(\n code: string,\n providerConfig: ProviderConfig,\n providerImpl: IProvider,\n codeVerifier: string\n ): Promise<any> {\n // Build the request body\n const body: Record<string, string> = {\n client_id: providerConfig.clientId,\n client_secret: providerConfig.clientSecret,\n code,\n redirect_uri: providerConfig.redirectUri,\n grant_type: \"authorization_code\",\n };\n\n if (codeVerifier) {\n body.code_verifier = codeVerifier;\n }\n const params = new URLSearchParams(body);\n\n const response = await fetch(providerImpl.tokenEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n throw new Error(\n `Token exchange failed: ${response.status} ${response.statusText}`\n );\n }\n\n return response.json();\n }\n\n private findProviderByType(providerType: string): ProviderConfig | undefined {\n return this.config.providers[providerType];\n }\n}\n\nexport { Lixa };\n","import NodeCache from 'node-cache';\nimport { StateDao } from \"./types\";\n\nclass LocalStateCache implements StateDao {\n private cache: NodeCache;\n\n constructor(defaultTtlSeconds: number = 600) {\n this.cache = new NodeCache({ stdTTL: defaultTtlSeconds });\n }\n\n async saveState(state: string, data: any, expiresInSeconds: number): Promise<void> {\n this.cache.set(state, data, expiresInSeconds);\n }\n\n async getState(state: string): Promise<any | null> {\n return this.cache.get(state) || null;\n }\n\n async deleteState(state: string): Promise<void> {\n this.cache.del(state);\n }\n}\n\nexport { LocalStateCache };\n","import NodeCache from 'node-cache';\nimport { SessionDao } from \"./types\";\n\nclass LocalSessionCache implements SessionDao {\n private cache: NodeCache;\n\n constructor(defaultTtlSeconds: number = 600) {\n this.cache = new NodeCache({ stdTTL: defaultTtlSeconds });\n }\n\n async saveSession(state: string, data: any, expiresInSeconds: number): Promise<void> {\n this.cache.set(state, data, expiresInSeconds);\n }\n\n async getSession(state: string): Promise<any | null> {\n return this.cache.get(state) || null;\n }\n\n async deleteSession(state: string): Promise<void> {\n this.cache.del(state);\n }\n}\n\nexport { LocalSessionCache };\n","/**\n * Represents a user session after successful OAuth authentication.\n *\n * @public\n */\nexport interface Session {\n /** The session token (typically the access token) */\n token: string;\n /** Raw token data from the OAuth provider */\n raw: any;\n}\n\n/**\n * Strategy interface for custom session creation.\n *\n * @public\n */\nexport interface SessionStrategy {\n /**\n * Creates a session from OAuth token data.\n *\n * @param userInfo - The token data received from the OAuth provider\n * @returns A Promise that resolves to a Session object\n */\n createSession(userInfo: any): Promise<Session>;\n}\n\n/**\n * Default session strategy that works with any OAuth provider.\n * Extracts common token information and creates a standardized session.\n *\n * @public\n */\nexport class DefaultSessionStrategy implements SessionStrategy {\n /**\n * Creates a session from OAuth token data.\n * Handles common OAuth token formats and extracts the access token.\n *\n * @param tokenData - The token data received from the OAuth provider\n * @returns A Promise that resolves to a Session object\n */\n async createSession(tokenData: any): Promise<Session> {\n // Extract access token from various possible formats\n const accessToken = tokenData.access_token || \n tokenData.accessToken || \n tokenData.token ||\n tokenData;\n\n if (!accessToken || typeof accessToken !== 'string') {\n throw new Error('No valid access token found in OAuth response');\n }\n\n return {\n token: accessToken,\n raw: tokenData,\n };\n }\n}"],"mappings":"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","names":["NodeCache"]}