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.
- package/CHANGELOG.md +235 -0
- package/LICENSE +21 -0
- package/README.md +417 -0
- package/dist/auth/handler.d.ts +38 -0
- package/dist/auth/handler.js +483 -0
- package/dist/auth/handler.js.map +1 -0
- package/dist/auth/login.d.ts +69 -0
- package/dist/auth/login.js +103 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/register.d.ts +72 -0
- package/dist/auth/register.js +122 -0
- package/dist/auth/register.js.map +1 -0
- package/dist/auth/reset-password.d.ts +106 -0
- package/dist/auth/reset-password.js +213 -0
- package/dist/auth/reset-password.js.map +1 -0
- package/dist/auth/utils.d.ts +58 -0
- package/dist/auth/utils.js +121 -0
- package/dist/auth/utils.js.map +1 -0
- package/dist/auth/verify-email.d.ts +70 -0
- package/dist/auth/verify-email.js +137 -0
- package/dist/auth/verify-email.js.map +1 -0
- package/dist/createMechAuth.d.ts +178 -0
- package/dist/createMechAuth.js +215 -0
- package/dist/createMechAuth.js.map +1 -0
- package/dist/database/schema.d.ts +135 -0
- package/dist/database/schema.js +37 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/edge.d.ts +4 -0
- package/dist/edge.js +6 -0
- package/dist/edge.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +44 -0
- package/dist/errors.js.map +1 -0
- package/dist/handler.d.ts +100 -0
- package/dist/handler.js +213 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +22 -0
- package/dist/logger.js +40 -0
- package/dist/logger.js.map +1 -0
- package/dist/mech-kysely.d.ts +22 -0
- package/dist/mech-kysely.js +88 -0
- package/dist/mech-kysely.js.map +1 -0
- package/dist/mech-sql-client.d.ts +85 -0
- package/dist/mech-sql-client.js +155 -0
- package/dist/mech-sql-client.js.map +1 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/dist/oauth/arctic-providers.d.ts +60 -0
- package/dist/oauth/arctic-providers.js +94 -0
- package/dist/oauth/arctic-providers.js.map +1 -0
- package/dist/oauth/callbacks.d.ts +155 -0
- package/dist/oauth/callbacks.js +286 -0
- package/dist/oauth/callbacks.js.map +1 -0
- package/dist/oauth/github.d.ts +47 -0
- package/dist/oauth/github.js +136 -0
- package/dist/oauth/github.js.map +1 -0
- package/dist/oauth/google.d.ts +49 -0
- package/dist/oauth/google.js +104 -0
- package/dist/oauth/google.js.map +1 -0
- package/dist/oauth/handler.d.ts +31 -0
- package/dist/oauth/handler.js +277 -0
- package/dist/oauth/handler.js.map +1 -0
- package/dist/password-hasher-argon2.d.ts +7 -0
- package/dist/password-hasher-argon2.js +16 -0
- package/dist/password-hasher-argon2.js.map +1 -0
- package/dist/password-hasher.d.ts +12 -0
- package/dist/password-hasher.js +115 -0
- package/dist/password-hasher.js.map +1 -0
- package/dist/react.d.ts +152 -0
- package/dist/react.js +296 -0
- package/dist/react.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cors.d.ts +65 -0
- package/dist/utils/cors.js +152 -0
- package/dist/utils/cors.js.map +1 -0
- package/dist/utils/normalize-auth-path.d.ts +1 -0
- package/dist/utils/normalize-auth-path.js +8 -0
- package/dist/utils/normalize-auth-path.js.map +1 -0
- package/dist/validation.d.ts +23 -0
- package/dist/validation.js +70 -0
- package/dist/validation.js.map +1 -0
- 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"}
|