clearauth 0.3.0

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 (88) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/LICENSE +21 -0
  3. package/README.md +417 -0
  4. package/dist/auth/handler.d.ts +38 -0
  5. package/dist/auth/handler.js +483 -0
  6. package/dist/auth/handler.js.map +1 -0
  7. package/dist/auth/login.d.ts +69 -0
  8. package/dist/auth/login.js +103 -0
  9. package/dist/auth/login.js.map +1 -0
  10. package/dist/auth/register.d.ts +72 -0
  11. package/dist/auth/register.js +122 -0
  12. package/dist/auth/register.js.map +1 -0
  13. package/dist/auth/reset-password.d.ts +106 -0
  14. package/dist/auth/reset-password.js +213 -0
  15. package/dist/auth/reset-password.js.map +1 -0
  16. package/dist/auth/utils.d.ts +58 -0
  17. package/dist/auth/utils.js +121 -0
  18. package/dist/auth/utils.js.map +1 -0
  19. package/dist/auth/verify-email.d.ts +70 -0
  20. package/dist/auth/verify-email.js +137 -0
  21. package/dist/auth/verify-email.js.map +1 -0
  22. package/dist/createMechAuth.d.ts +178 -0
  23. package/dist/createMechAuth.js +215 -0
  24. package/dist/createMechAuth.js.map +1 -0
  25. package/dist/database/schema.d.ts +135 -0
  26. package/dist/database/schema.js +37 -0
  27. package/dist/database/schema.js.map +1 -0
  28. package/dist/edge.d.ts +4 -0
  29. package/dist/edge.js +6 -0
  30. package/dist/edge.js.map +1 -0
  31. package/dist/errors.d.ts +25 -0
  32. package/dist/errors.js +44 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/handler.d.ts +100 -0
  35. package/dist/handler.js +213 -0
  36. package/dist/handler.js.map +1 -0
  37. package/dist/index.d.ts +22 -0
  38. package/dist/index.js +28 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/logger.d.ts +22 -0
  41. package/dist/logger.js +40 -0
  42. package/dist/logger.js.map +1 -0
  43. package/dist/mech-kysely.d.ts +22 -0
  44. package/dist/mech-kysely.js +88 -0
  45. package/dist/mech-kysely.js.map +1 -0
  46. package/dist/mech-sql-client.d.ts +85 -0
  47. package/dist/mech-sql-client.js +155 -0
  48. package/dist/mech-sql-client.js.map +1 -0
  49. package/dist/node.d.ts +4 -0
  50. package/dist/node.js +10 -0
  51. package/dist/node.js.map +1 -0
  52. package/dist/oauth/arctic-providers.d.ts +60 -0
  53. package/dist/oauth/arctic-providers.js +94 -0
  54. package/dist/oauth/arctic-providers.js.map +1 -0
  55. package/dist/oauth/callbacks.d.ts +155 -0
  56. package/dist/oauth/callbacks.js +286 -0
  57. package/dist/oauth/callbacks.js.map +1 -0
  58. package/dist/oauth/github.d.ts +47 -0
  59. package/dist/oauth/github.js +136 -0
  60. package/dist/oauth/github.js.map +1 -0
  61. package/dist/oauth/google.d.ts +49 -0
  62. package/dist/oauth/google.js +104 -0
  63. package/dist/oauth/google.js.map +1 -0
  64. package/dist/oauth/handler.d.ts +31 -0
  65. package/dist/oauth/handler.js +277 -0
  66. package/dist/oauth/handler.js.map +1 -0
  67. package/dist/password-hasher-argon2.d.ts +7 -0
  68. package/dist/password-hasher-argon2.js +16 -0
  69. package/dist/password-hasher-argon2.js.map +1 -0
  70. package/dist/password-hasher.d.ts +12 -0
  71. package/dist/password-hasher.js +115 -0
  72. package/dist/password-hasher.js.map +1 -0
  73. package/dist/react.d.ts +152 -0
  74. package/dist/react.js +296 -0
  75. package/dist/react.js.map +1 -0
  76. package/dist/types.d.ts +190 -0
  77. package/dist/types.js +7 -0
  78. package/dist/types.js.map +1 -0
  79. package/dist/utils/cors.d.ts +65 -0
  80. package/dist/utils/cors.js +152 -0
  81. package/dist/utils/cors.js.map +1 -0
  82. package/dist/utils/normalize-auth-path.d.ts +1 -0
  83. package/dist/utils/normalize-auth-path.js +8 -0
  84. package/dist/utils/normalize-auth-path.js.map +1 -0
  85. package/dist/validation.d.ts +23 -0
  86. package/dist/validation.js +70 -0
  87. package/dist/validation.js.map +1 -0
  88. package/package.json +93 -0
@@ -0,0 +1,483 @@
1
+ /**
2
+ * Email/Password Authentication HTTP Handler
3
+ *
4
+ * Handles HTTP requests for email/password authentication:
5
+ * - POST /auth/register - User registration
6
+ * - POST /auth/verify-email - Email verification
7
+ * - POST /auth/login - User login
8
+ * - POST /auth/logout - Session deletion
9
+ * - POST /auth/request-reset - Request password reset
10
+ * - POST /auth/reset-password - Reset password with token
11
+ */
12
+ import { registerUser, toPublicRegisterResult } from './register.js';
13
+ import { verifyEmail, resendVerificationEmail } from './verify-email.js';
14
+ import { loginUser, toPublicLoginResult } from './login.js';
15
+ import { requestPasswordReset, resetPassword } from './reset-password.js';
16
+ import { deleteSession, validateSession, parseCookies, createDeleteCookieHeader, createCookieHeader, } from '../oauth/callbacks.js';
17
+ import { AuthError } from './utils.js';
18
+ import { toPublicUser } from '../database/schema.js';
19
+ /**
20
+ * Parse JSON request body
21
+ *
22
+ * @param request - HTTP request
23
+ * @returns Parsed JSON body
24
+ * @internal
25
+ */
26
+ async function parseJsonBody(request) {
27
+ try {
28
+ return await request.json();
29
+ }
30
+ catch (error) {
31
+ throw new AuthError('Invalid JSON body', 'INVALID_JSON', 400);
32
+ }
33
+ }
34
+ /**
35
+ * Extract request context from headers
36
+ *
37
+ * @param request - HTTP request
38
+ * @returns Request context (IP address, user agent)
39
+ * @internal
40
+ */
41
+ function getRequestContext(request) {
42
+ return {
43
+ ipAddress: request.headers.get('x-forwarded-for') || request.headers.get('cf-connecting-ip') || undefined,
44
+ userAgent: request.headers.get('user-agent') || undefined,
45
+ };
46
+ }
47
+ /**
48
+ * Create JSON response
49
+ *
50
+ * @param data - Response data
51
+ * @param status - HTTP status code
52
+ * @returns JSON response
53
+ * @internal
54
+ */
55
+ function jsonResponse(data, status = 200) {
56
+ return new Response(JSON.stringify(data), {
57
+ status,
58
+ headers: {
59
+ 'Content-Type': 'application/json',
60
+ },
61
+ });
62
+ }
63
+ /**
64
+ * Create error response
65
+ *
66
+ * @param error - Error object
67
+ * @returns JSON error response
68
+ * @internal
69
+ */
70
+ function errorResponse(error) {
71
+ if (error instanceof AuthError) {
72
+ return jsonResponse({
73
+ error: error.message,
74
+ code: error.code,
75
+ }, error.statusCode);
76
+ }
77
+ // Unknown error
78
+ console.error('Unexpected error:', error);
79
+ return jsonResponse({
80
+ error: 'Internal server error',
81
+ code: 'INTERNAL_ERROR',
82
+ }, 500);
83
+ }
84
+ /**
85
+ * Handle POST /auth/register
86
+ *
87
+ * Register a new user with email and password.
88
+ *
89
+ * Request body:
90
+ * ```json
91
+ * {
92
+ * "email": "user@example.com",
93
+ * "password": "SecurePass123!"
94
+ * }
95
+ * ```
96
+ *
97
+ * Response:
98
+ * ```json
99
+ * {
100
+ * "user": {
101
+ * "id": "uuid",
102
+ * "email": "user@example.com",
103
+ * "email_verified": false,
104
+ * "name": null,
105
+ * "avatar_url": null,
106
+ * "created_at": "2025-01-01T00:00:00.000Z"
107
+ * },
108
+ * "sessionId": "session_id",
109
+ * "verificationToken": "token"
110
+ * }
111
+ * ```
112
+ */
113
+ async function handleRegister(request, config) {
114
+ const body = await parseJsonBody(request);
115
+ const { email, password } = body;
116
+ if (!email || !password) {
117
+ throw new AuthError('Email and password are required', 'MISSING_FIELDS', 400);
118
+ }
119
+ const context = getRequestContext(request);
120
+ const result = await registerUser(config.database, email, password, context, config.passwordHasher);
121
+ const publicResult = toPublicRegisterResult(result);
122
+ const cookieName = config.session?.cookie?.name ?? 'session';
123
+ const expiresInSeconds = config.session?.expiresIn ?? 2592000;
124
+ const sessionCookie = createCookieHeader(cookieName, result.sessionId, {
125
+ httpOnly: config.session?.cookie?.httpOnly ?? true,
126
+ secure: config.session?.cookie?.secure ?? config.isProduction ?? true,
127
+ sameSite: config.session?.cookie?.sameSite ?? 'lax',
128
+ path: config.session?.cookie?.path ?? '/',
129
+ maxAge: expiresInSeconds,
130
+ });
131
+ return new Response(JSON.stringify(publicResult), {
132
+ status: 201,
133
+ headers: {
134
+ 'Content-Type': 'application/json',
135
+ 'Set-Cookie': sessionCookie,
136
+ },
137
+ });
138
+ }
139
+ /**
140
+ * Handle POST /auth/verify-email
141
+ *
142
+ * Verify user's email address with a token.
143
+ *
144
+ * Request body:
145
+ * ```json
146
+ * {
147
+ * "token": "verification_token"
148
+ * }
149
+ * ```
150
+ *
151
+ * Response:
152
+ * ```json
153
+ * {
154
+ * "success": true,
155
+ * "userId": "uuid"
156
+ * }
157
+ * ```
158
+ */
159
+ async function handleVerifyEmail(request, config) {
160
+ const body = await parseJsonBody(request);
161
+ const { token } = body;
162
+ if (!token) {
163
+ throw new AuthError('Verification token is required', 'MISSING_TOKEN', 400);
164
+ }
165
+ const result = await verifyEmail(config.database, token);
166
+ return jsonResponse(result);
167
+ }
168
+ /**
169
+ * Handle POST /auth/resend-verification
170
+ *
171
+ * Resend verification email.
172
+ *
173
+ * Request body:
174
+ * ```json
175
+ * {
176
+ * "email": "user@example.com"
177
+ * }
178
+ * ```
179
+ *
180
+ * Response:
181
+ * ```json
182
+ * {
183
+ * "token": "new_verification_token"
184
+ * }
185
+ * ```
186
+ */
187
+ async function handleResendVerification(request, config) {
188
+ const body = await parseJsonBody(request);
189
+ const { email } = body;
190
+ if (!email) {
191
+ throw new AuthError('Email is required', 'MISSING_EMAIL', 400);
192
+ }
193
+ const result = await resendVerificationEmail(config.database, email);
194
+ return jsonResponse(result);
195
+ }
196
+ /**
197
+ * Handle POST /auth/login
198
+ *
199
+ * Login user with email and password.
200
+ *
201
+ * Request body:
202
+ * ```json
203
+ * {
204
+ * "email": "user@example.com",
205
+ * "password": "SecurePass123!"
206
+ * }
207
+ * ```
208
+ *
209
+ * Response:
210
+ * ```json
211
+ * {
212
+ * "user": {
213
+ * "id": "uuid",
214
+ * "email": "user@example.com",
215
+ * "email_verified": true,
216
+ * "name": null,
217
+ * "avatar_url": null,
218
+ * "created_at": "2025-01-01T00:00:00.000Z"
219
+ * },
220
+ * "sessionId": "session_id"
221
+ * }
222
+ * ```
223
+ */
224
+ async function handleLogin(request, config) {
225
+ const body = await parseJsonBody(request);
226
+ const { email, password } = body;
227
+ if (!email || !password) {
228
+ throw new AuthError('Email and password are required', 'MISSING_FIELDS', 400);
229
+ }
230
+ const context = getRequestContext(request);
231
+ const result = await loginUser(config.database, email, password, context, config.passwordHasher);
232
+ const publicResult = toPublicLoginResult(result);
233
+ const cookieName = config.session?.cookie?.name ?? 'session';
234
+ const expiresInSeconds = config.session?.expiresIn ?? 2592000;
235
+ const sessionCookie = createCookieHeader(cookieName, result.sessionId, {
236
+ httpOnly: config.session?.cookie?.httpOnly ?? true,
237
+ secure: config.session?.cookie?.secure ?? config.isProduction ?? true,
238
+ sameSite: config.session?.cookie?.sameSite ?? 'lax',
239
+ path: config.session?.cookie?.path ?? '/',
240
+ maxAge: expiresInSeconds,
241
+ });
242
+ return new Response(JSON.stringify(publicResult), {
243
+ status: 200,
244
+ headers: {
245
+ 'Content-Type': 'application/json',
246
+ 'Set-Cookie': sessionCookie,
247
+ },
248
+ });
249
+ }
250
+ /**
251
+ * Handle POST /auth/logout
252
+ *
253
+ * Logout user by deleting their session.
254
+ *
255
+ * Request body:
256
+ * ```json
257
+ * {
258
+ * "sessionId": "session_id"
259
+ * }
260
+ * ```
261
+ *
262
+ * Response:
263
+ * ```json
264
+ * {
265
+ * "success": true
266
+ * }
267
+ * ```
268
+ */
269
+ async function handleLogout(request, config) {
270
+ let sessionId;
271
+ let usedCookieFallback = false;
272
+ try {
273
+ const body = await request.json();
274
+ sessionId = body?.sessionId;
275
+ }
276
+ catch {
277
+ // Allow empty/invalid JSON body for cookie-based logout
278
+ }
279
+ if (!sessionId) {
280
+ const cookieHeader = request.headers.get('cookie');
281
+ if (cookieHeader) {
282
+ const cookies = parseCookies(cookieHeader);
283
+ const cookieName = config.session?.cookie?.name || 'session';
284
+ sessionId = cookies[cookieName];
285
+ usedCookieFallback = Boolean(sessionId);
286
+ }
287
+ }
288
+ if (usedCookieFallback) {
289
+ const origin = request.headers.get('origin');
290
+ if (origin) {
291
+ const requestOrigin = new URL(request.url).origin;
292
+ if (origin !== requestOrigin) {
293
+ throw new AuthError('Forbidden', 'FORBIDDEN', 403);
294
+ }
295
+ }
296
+ }
297
+ const cookieName = config.session?.cookie?.name || 'session';
298
+ const cookiePath = config.session?.cookie?.path ?? '/';
299
+ if (sessionId) {
300
+ await deleteSession(config.database, sessionId);
301
+ }
302
+ const deleteSessionCookie = createDeleteCookieHeader(cookieName, { path: cookiePath });
303
+ return new Response(JSON.stringify({ success: true }), {
304
+ status: 200,
305
+ headers: {
306
+ 'Content-Type': 'application/json',
307
+ 'Set-Cookie': deleteSessionCookie,
308
+ },
309
+ });
310
+ }
311
+ /**
312
+ * Handle POST /auth/request-reset
313
+ *
314
+ * Request password reset.
315
+ *
316
+ * **Security Note:** This endpoint always returns success, even if the email doesn't exist.
317
+ * This prevents email enumeration attacks. The token is never returned in the response.
318
+ *
319
+ * Request body:
320
+ * ```json
321
+ * {
322
+ * "email": "user@example.com"
323
+ * }
324
+ * ```
325
+ *
326
+ * Response (always):
327
+ * ```json
328
+ * {
329
+ * "success": true,
330
+ * "message": "If your email is registered, you will receive a password reset link."
331
+ * }
332
+ * ```
333
+ */
334
+ async function handleRequestReset(request, config) {
335
+ const body = await parseJsonBody(request);
336
+ const { email } = body;
337
+ if (!email) {
338
+ throw new AuthError('Email is required', 'MISSING_EMAIL', 400);
339
+ }
340
+ // Note: onTokenGenerated callback should be provided by config for email sending
341
+ // For now, we just store the token - users need to implement email sending
342
+ await requestPasswordReset(config.database, email);
343
+ // Always return success to prevent email enumeration
344
+ return jsonResponse({
345
+ success: true,
346
+ message: 'If your email is registered, you will receive a password reset link.',
347
+ });
348
+ }
349
+ /**
350
+ * Handle POST /auth/reset-password
351
+ *
352
+ * Reset password with token.
353
+ *
354
+ * Request body:
355
+ * ```json
356
+ * {
357
+ * "token": "reset_token",
358
+ * "password": "NewSecurePass123!"
359
+ * }
360
+ * ```
361
+ *
362
+ * Response:
363
+ * ```json
364
+ * {
365
+ * "success": true
366
+ * }
367
+ * ```
368
+ */
369
+ async function handleResetPassword(request, config) {
370
+ const body = await parseJsonBody(request);
371
+ const { token, password } = body;
372
+ if (!token || !password) {
373
+ throw new AuthError('Token and password are required', 'MISSING_FIELDS', 400);
374
+ }
375
+ const result = await resetPassword(config.database, token, password, config.passwordHasher);
376
+ return jsonResponse(result);
377
+ }
378
+ /**
379
+ * Handle GET /auth/session
380
+ *
381
+ * Get current session/user from cookie.
382
+ *
383
+ * Response (authenticated):
384
+ * ```json
385
+ * {
386
+ * "user": {
387
+ * "id": "uuid",
388
+ * "email": "user@example.com",
389
+ * "email_verified": true,
390
+ * "name": "User Name",
391
+ * "avatar_url": "https://...",
392
+ * "created_at": "2025-01-01T00:00:00.000Z"
393
+ * }
394
+ * }
395
+ * ```
396
+ *
397
+ * Response (not authenticated):
398
+ * ```json
399
+ * {
400
+ * "user": null
401
+ * }
402
+ * ```
403
+ */
404
+ async function handleSession(request, config) {
405
+ // Get session ID from cookie
406
+ const cookieHeader = request.headers.get('cookie');
407
+ if (!cookieHeader) {
408
+ return jsonResponse({ user: null });
409
+ }
410
+ const cookies = parseCookies(cookieHeader);
411
+ const sessionId = cookies[config.session?.cookie?.name || 'session'];
412
+ if (!sessionId) {
413
+ return jsonResponse({ user: null });
414
+ }
415
+ // Validate session
416
+ const user = await validateSession(config.database, sessionId);
417
+ if (!user) {
418
+ return jsonResponse({ user: null });
419
+ }
420
+ return jsonResponse({ user: toPublicUser(user) });
421
+ }
422
+ /**
423
+ * Main authentication request handler
424
+ *
425
+ * Routes incoming requests to the appropriate handler based on the URL path.
426
+ *
427
+ * Supported routes:
428
+ * - GET /auth/session - Get current user session
429
+ * - POST /auth/register - Register new user
430
+ * - POST /auth/verify-email - Verify email with token
431
+ * - POST /auth/resend-verification - Resend verification email
432
+ * - POST /auth/login - Login with email/password
433
+ * - POST /auth/logout - Logout user
434
+ * - POST /auth/request-reset - Request password reset
435
+ * - POST /auth/reset-password - Reset password with token
436
+ *
437
+ * @param request - HTTP request
438
+ * @param config - Clear Auth configuration
439
+ * @returns HTTP response
440
+ *
441
+ * @example
442
+ * ```ts
443
+ * const response = await handleAuthRequest(request, config)
444
+ * return response
445
+ * ```
446
+ */
447
+ export async function handleAuthRequest(request, config) {
448
+ try {
449
+ const url = new URL(request.url);
450
+ const path = url.pathname.startsWith('/api/auth') ? url.pathname.replace(/^\/api/, '') : url.pathname;
451
+ // Handle GET /auth/session
452
+ if (request.method === 'GET' && path === '/auth/session') {
453
+ return await handleSession(request, config);
454
+ }
455
+ // Only handle POST requests for other routes
456
+ if (request.method !== 'POST') {
457
+ return jsonResponse({ error: 'Method not allowed' }, 405);
458
+ }
459
+ // Route to appropriate handler
460
+ switch (path) {
461
+ case '/auth/register':
462
+ return await handleRegister(request, config);
463
+ case '/auth/verify-email':
464
+ return await handleVerifyEmail(request, config);
465
+ case '/auth/resend-verification':
466
+ return await handleResendVerification(request, config);
467
+ case '/auth/login':
468
+ return await handleLogin(request, config);
469
+ case '/auth/logout':
470
+ return await handleLogout(request, config);
471
+ case '/auth/request-reset':
472
+ return await handleRequestReset(request, config);
473
+ case '/auth/reset-password':
474
+ return await handleResetPassword(request, config);
475
+ default:
476
+ return jsonResponse({ error: 'Not found' }, 404);
477
+ }
478
+ }
479
+ catch (error) {
480
+ return errorResponse(error);
481
+ }
482
+ }
483
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/auth/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * User Login
3
+ *
4
+ * Handles user login with email and password:
5
+ * - User lookup by email
6
+ * - Password verification using Argon2id
7
+ * - Session creation on successful login
8
+ */
9
+ import type { Kysely } from 'kysely';
10
+ import type { Database, User } from '../database/schema.js';
11
+ import type { RequestContext } from '../types.js';
12
+ import type { PasswordHasher } from '../password-hasher.js';
13
+ /**
14
+ * Login user with email and password
15
+ *
16
+ * Authenticates a user by verifying their email and password.
17
+ * Creates a new session on successful login.
18
+ *
19
+ * @param db - Kysely database instance
20
+ * @param email - User's email address
21
+ * @param password - User's password (plain text)
22
+ * @param context - Optional request context (IP address, user agent)
23
+ * @returns User record and session ID if successful
24
+ * @throws {AuthError} If credentials are invalid
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * try {
29
+ * const result = await loginUser(db, 'user@example.com', 'SecurePass123!', {
30
+ * ipAddress: '192.168.1.1',
31
+ * userAgent: 'Mozilla/5.0...'
32
+ * })
33
+ * console.log('Login successful:', result.user.id, result.sessionId)
34
+ * } catch (error) {
35
+ * console.error('Login failed:', error.message)
36
+ * }
37
+ * ```
38
+ */
39
+ export declare function loginUser(db: Kysely<Database>, email: string, password: string, context?: RequestContext, passwordHasher?: PasswordHasher): Promise<{
40
+ user: User;
41
+ sessionId: string;
42
+ }>;
43
+ /**
44
+ * Login result type (for HTTP responses)
45
+ */
46
+ export interface LoginUserResult {
47
+ user: {
48
+ id: string;
49
+ email: string;
50
+ email_verified: boolean;
51
+ name: string | null;
52
+ avatar_url: string | null;
53
+ created_at: Date;
54
+ };
55
+ sessionId: string;
56
+ }
57
+ /**
58
+ * Convert login result to public format
59
+ *
60
+ * Removes sensitive fields like password_hash and provider IDs.
61
+ *
62
+ * @param result - Login result
63
+ * @returns Public login result
64
+ * @internal
65
+ */
66
+ export declare function toPublicLoginResult(result: {
67
+ user: User;
68
+ sessionId: string;
69
+ }): LoginUserResult;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * User Login
3
+ *
4
+ * Handles user login with email and password:
5
+ * - User lookup by email
6
+ * - Password verification using Argon2id
7
+ * - Session creation on successful login
8
+ */
9
+ import { createSession } from '../oauth/callbacks.js';
10
+ import { createPbkdf2PasswordHasher } from '../password-hasher.js';
11
+ import { normalizeEmail, createAuthError } from './utils.js';
12
+ const defaultPasswordHasher = createPbkdf2PasswordHasher();
13
+ /**
14
+ * Login user with email and password
15
+ *
16
+ * Authenticates a user by verifying their email and password.
17
+ * Creates a new session on successful login.
18
+ *
19
+ * @param db - Kysely database instance
20
+ * @param email - User's email address
21
+ * @param password - User's password (plain text)
22
+ * @param context - Optional request context (IP address, user agent)
23
+ * @returns User record and session ID if successful
24
+ * @throws {AuthError} If credentials are invalid
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * try {
29
+ * const result = await loginUser(db, 'user@example.com', 'SecurePass123!', {
30
+ * ipAddress: '192.168.1.1',
31
+ * userAgent: 'Mozilla/5.0...'
32
+ * })
33
+ * console.log('Login successful:', result.user.id, result.sessionId)
34
+ * } catch (error) {
35
+ * console.error('Login failed:', error.message)
36
+ * }
37
+ * ```
38
+ */
39
+ export async function loginUser(db, email, password, context, passwordHasher) {
40
+ const hasher = passwordHasher ?? defaultPasswordHasher;
41
+ if (!email || !password) {
42
+ throw createAuthError('Email and password are required', 'INVALID_CREDENTIALS', 401);
43
+ }
44
+ const normalizedEmail = normalizeEmail(email);
45
+ // Look up user by email
46
+ const user = await db
47
+ .selectFrom('users')
48
+ .selectAll()
49
+ .where('email', '=', normalizedEmail)
50
+ .executeTakeFirst();
51
+ if (!user) {
52
+ // Generic error message to prevent email enumeration
53
+ throw createAuthError('Invalid email or password', 'INVALID_CREDENTIALS', 401);
54
+ }
55
+ // Check if user has a password set (OAuth-only users don't)
56
+ if (!user.password_hash) {
57
+ throw createAuthError('This account uses social login. Please sign in with your social provider.', 'NO_PASSWORD_SET', 400);
58
+ }
59
+ // Verify password
60
+ let isValidPassword;
61
+ try {
62
+ isValidPassword = await hasher.verify(user.password_hash, password);
63
+ }
64
+ catch (error) {
65
+ console.error('Password verification failed:', error instanceof Error ? error.message : 'unknown');
66
+ throw createAuthError('Invalid email or password', 'INVALID_CREDENTIALS', 401);
67
+ }
68
+ if (!isValidPassword) {
69
+ throw createAuthError('Invalid email or password', 'INVALID_CREDENTIALS', 401);
70
+ }
71
+ // Create new session
72
+ const sessionId = await createSession(db, user.id, 2592000, {
73
+ ipAddress: context?.ipAddress,
74
+ userAgent: context?.userAgent,
75
+ });
76
+ return {
77
+ user,
78
+ sessionId,
79
+ };
80
+ }
81
+ /**
82
+ * Convert login result to public format
83
+ *
84
+ * Removes sensitive fields like password_hash and provider IDs.
85
+ *
86
+ * @param result - Login result
87
+ * @returns Public login result
88
+ * @internal
89
+ */
90
+ export function toPublicLoginResult(result) {
91
+ return {
92
+ user: {
93
+ id: result.user.id,
94
+ email: result.user.email,
95
+ email_verified: result.user.email_verified,
96
+ name: result.user.name,
97
+ avatar_url: result.user.avatar_url,
98
+ created_at: result.user.created_at,
99
+ },
100
+ sessionId: result.sessionId,
101
+ };
102
+ }
103
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAGrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5D,MAAM,qBAAqB,GAAG,0BAA0B,EAAE,CAAA;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAoB,EACpB,KAAa,EACb,QAAgB,EAChB,OAAwB,EACxB,cAA+B;IAE/B,MAAM,MAAM,GAAG,cAAc,IAAI,qBAAqB,CAAA;IAEtD,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,MAAM,eAAe,CAAC,iCAAiC,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAA;IACtF,CAAC;IAED,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;IAE7C,wBAAwB;IACxB,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,UAAU,CAAC,OAAO,CAAC;SACnB,SAAS,EAAE;SACX,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,eAAe,CAAC;SACpC,gBAAgB,EAAE,CAAA;IAErB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,qDAAqD;QACrD,MAAM,eAAe,CAAC,2BAA2B,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAA;IAChF,CAAC;IAED,4DAA4D;IAC5D,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QACxB,MAAM,eAAe,CACnB,2EAA2E,EAC3E,iBAAiB,EACjB,GAAG,CACJ,CAAA;IACH,CAAC;IAED,kBAAkB;IAClB,IAAI,eAAwB,CAAA;IAC5B,IAAI,CAAC;QACH,eAAe,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,+BAA+B,EAC/B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACnD,CAAA;QACD,MAAM,eAAe,CAAC,2BAA2B,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAA;IAChF,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,eAAe,CAAC,2BAA2B,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAA;IAChF,CAAC;IAED,qBAAqB;IACrB,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE;QAC1D,SAAS,EAAE,OAAO,EAAE,SAAS;QAC7B,SAAS,EAAE,OAAO,EAAE,SAAS;KAC9B,CAAC,CAAA;IAEF,OAAO;QACL,IAAI;QACJ,SAAS;KACV,CAAA;AACH,CAAC;AAiBD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAyC;IAC3E,OAAO;QACL,IAAI,EAAE;YACJ,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE;YAClB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK;YACxB,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,cAAc;YAC1C,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;YACtB,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU;YAClC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU;SACnC;QACD,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAA;AACH,CAAC"}