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,277 @@
1
+ /**
2
+ * OAuth HTTP Request Handler
3
+ *
4
+ * Handles OAuth-related HTTP requests for GitHub and Google authentication.
5
+ * Provides login initiation and callback handling endpoints.
6
+ */
7
+ import { generateGitHubAuthUrl, handleGitHubCallback } from './github.js';
8
+ import { generateGoogleAuthUrl, handleGoogleCallback } from './google.js';
9
+ import { normalizeAuthPath } from '../utils/normalize-auth-path.js';
10
+ import { upsertOAuthUser, createSession, parseCookies, createCookieHeader, createDeleteCookieHeader, } from './callbacks.js';
11
+ /**
12
+ * Helper to create Headers with multiple Set-Cookie headers
13
+ *
14
+ * HTTP spec requires each cookie to be a separate Set-Cookie header entry,
15
+ * not comma-separated in a single header.
16
+ *
17
+ * @param cookies - Array of cookie header strings
18
+ * @param location - Redirect location URL
19
+ * @returns Headers object with proper Set-Cookie headers
20
+ */
21
+ function createHeadersWithCookies(cookies, location) {
22
+ const headers = new Headers();
23
+ if (location) {
24
+ headers.set('Location', location);
25
+ }
26
+ for (const cookie of cookies) {
27
+ headers.append('Set-Cookie', cookie);
28
+ }
29
+ return headers;
30
+ }
31
+ /**
32
+ * OAuth Request Handler
33
+ *
34
+ * Main handler for OAuth-related requests. Routes requests to appropriate
35
+ * provider handlers based on URL path.
36
+ *
37
+ * @param request - HTTP request
38
+ * @param config - Clear Auth configuration
39
+ * @returns HTTP response
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * export default {
44
+ * async fetch(request: Request, env: Env) {
45
+ * const url = new URL(request.url)
46
+ * if (url.pathname.startsWith('/auth/oauth/')) {
47
+ * return handleOAuthRequest(request, config)
48
+ * }
49
+ * // ... other routes
50
+ * }
51
+ * }
52
+ * ```
53
+ */
54
+ export async function handleOAuthRequest(request, config) {
55
+ const url = new URL(request.url);
56
+ const pathname = normalizeAuthPath(url.pathname);
57
+ // GitHub OAuth routes
58
+ if (pathname === '/auth/oauth/github' || pathname === '/auth/github/login') {
59
+ return handleGitHubLogin(request, config);
60
+ }
61
+ if (pathname === '/auth/callback/github' || pathname === '/auth/github/callback') {
62
+ return handleGitHubCallbackRequest(request, config);
63
+ }
64
+ // Google OAuth routes
65
+ if (pathname === '/auth/oauth/google' || pathname === '/auth/google/login') {
66
+ return handleGoogleLogin(request, config);
67
+ }
68
+ if (pathname === '/auth/callback/google' || pathname === '/auth/google/callback') {
69
+ return handleGoogleCallbackRequest(request, config);
70
+ }
71
+ return new Response('Not Found', { status: 404 });
72
+ }
73
+ /**
74
+ * Handle GitHub login initiation
75
+ *
76
+ * Generates GitHub OAuth URL and redirects user to GitHub for authentication.
77
+ * Stores state parameter in cookie for CSRF protection.
78
+ */
79
+ async function handleGitHubLogin(request, config) {
80
+ try {
81
+ const { url, state } = await generateGitHubAuthUrl(config);
82
+ // Store state in cookie for validation
83
+ const stateCookie = createCookieHeader('oauth_state', state, {
84
+ httpOnly: true,
85
+ secure: config.isProduction ?? true,
86
+ sameSite: 'lax',
87
+ path: '/',
88
+ maxAge: 600, // 10 minutes
89
+ });
90
+ return new Response(null, {
91
+ status: 302,
92
+ headers: {
93
+ Location: url.toString(),
94
+ 'Set-Cookie': stateCookie,
95
+ },
96
+ });
97
+ }
98
+ catch (error) {
99
+ console.error('GitHub login error:', error);
100
+ return new Response('OAuth configuration error', { status: 500 });
101
+ }
102
+ }
103
+ /**
104
+ * Handle GitHub OAuth callback
105
+ *
106
+ * Validates OAuth callback, creates/updates user, and creates session.
107
+ */
108
+ async function handleGitHubCallbackRequest(request, config) {
109
+ try {
110
+ const url = new URL(request.url);
111
+ const code = url.searchParams.get('code');
112
+ const returnedState = url.searchParams.get('state');
113
+ const error = url.searchParams.get('error');
114
+ // Check for OAuth errors
115
+ if (error) {
116
+ return new Response(`OAuth error: ${error}`, { status: 400 });
117
+ }
118
+ if (!code || !returnedState) {
119
+ return new Response('Missing code or state parameter', { status: 400 });
120
+ }
121
+ // Get stored state from cookie
122
+ const cookies = parseCookies(request.headers.get('Cookie') || '');
123
+ const storedState = cookies['oauth_state'];
124
+ if (!storedState) {
125
+ return new Response('Missing state cookie', { status: 400 });
126
+ }
127
+ // Handle OAuth callback
128
+ const result = await handleGitHubCallback(config, code, storedState, returnedState);
129
+ // Upsert user
130
+ const user = await upsertOAuthUser(config.database, 'github', result.profile);
131
+ // Create session
132
+ const context = getRequestContext(request);
133
+ const expiresInSeconds = config.session?.expiresIn ?? 2592000; // 30 days
134
+ const sessionId = await createSession(config.database, user.id, expiresInSeconds, context);
135
+ // Create session cookie
136
+ const cookieName = config.session?.cookie?.name ?? 'session';
137
+ const sessionCookie = createCookieHeader(cookieName, sessionId, {
138
+ httpOnly: config.session?.cookie?.httpOnly ?? true,
139
+ secure: config.session?.cookie?.secure ?? config.isProduction ?? true,
140
+ sameSite: config.session?.cookie?.sameSite ?? 'lax',
141
+ path: config.session?.cookie?.path ?? '/',
142
+ maxAge: expiresInSeconds,
143
+ });
144
+ // Clear state cookie
145
+ const deleteStateCookie = createDeleteCookieHeader('oauth_state', { path: '/' });
146
+ // Redirect to success page or home
147
+ const headers = createHeadersWithCookies([sessionCookie, deleteStateCookie], '/' // TODO: Make this configurable
148
+ );
149
+ return new Response(null, {
150
+ status: 302,
151
+ headers,
152
+ });
153
+ }
154
+ catch (error) {
155
+ console.error('GitHub callback error:', error);
156
+ const message = error instanceof Error ? error.message : 'OAuth callback failed';
157
+ return new Response(message, { status: 400 });
158
+ }
159
+ }
160
+ /**
161
+ * Handle Google login initiation
162
+ *
163
+ * Generates Google OAuth URL and redirects user to Google for authentication.
164
+ * Stores state and code verifier in cookies for CSRF protection and PKCE.
165
+ */
166
+ async function handleGoogleLogin(request, config) {
167
+ try {
168
+ const { url, state, codeVerifier } = await generateGoogleAuthUrl(config);
169
+ // Store state and code verifier in cookies for validation
170
+ const stateCookie = createCookieHeader('oauth_state', state, {
171
+ httpOnly: true,
172
+ secure: config.isProduction ?? true,
173
+ sameSite: 'lax',
174
+ path: '/',
175
+ maxAge: 600, // 10 minutes
176
+ });
177
+ const verifierCookie = createCookieHeader('oauth_code_verifier', codeVerifier, {
178
+ httpOnly: true,
179
+ secure: config.isProduction ?? true,
180
+ sameSite: 'lax',
181
+ path: '/',
182
+ maxAge: 600, // 10 minutes
183
+ });
184
+ const headers = createHeadersWithCookies([stateCookie, verifierCookie], url.toString());
185
+ return new Response(null, {
186
+ status: 302,
187
+ headers,
188
+ });
189
+ }
190
+ catch (error) {
191
+ console.error('Google login error:', error);
192
+ return new Response('OAuth configuration error', { status: 500 });
193
+ }
194
+ }
195
+ /**
196
+ * Handle Google OAuth callback
197
+ *
198
+ * Validates OAuth callback, creates/updates user, and creates session.
199
+ */
200
+ async function handleGoogleCallbackRequest(request, config) {
201
+ try {
202
+ const url = new URL(request.url);
203
+ const code = url.searchParams.get('code');
204
+ const returnedState = url.searchParams.get('state');
205
+ const error = url.searchParams.get('error');
206
+ // Check for OAuth errors
207
+ if (error) {
208
+ return new Response(`OAuth error: ${error}`, { status: 400 });
209
+ }
210
+ if (!code || !returnedState) {
211
+ return new Response('Missing code or state parameter', { status: 400 });
212
+ }
213
+ // Get stored state and code verifier from cookies
214
+ const cookies = parseCookies(request.headers.get('Cookie') || '');
215
+ const storedState = cookies['oauth_state'];
216
+ const codeVerifier = cookies['oauth_code_verifier'];
217
+ if (!storedState) {
218
+ return new Response('Missing state cookie', { status: 400 });
219
+ }
220
+ if (!codeVerifier) {
221
+ return new Response('Missing code verifier cookie', { status: 400 });
222
+ }
223
+ // Handle OAuth callback
224
+ const result = await handleGoogleCallback(config, code, storedState, returnedState, codeVerifier);
225
+ // Upsert user
226
+ const user = await upsertOAuthUser(config.database, 'google', result.profile);
227
+ // Create session
228
+ const context = getRequestContext(request);
229
+ const expiresInSeconds = config.session?.expiresIn ?? 2592000; // 30 days
230
+ const sessionId = await createSession(config.database, user.id, expiresInSeconds, context);
231
+ // Create session cookie
232
+ const cookieName = config.session?.cookie?.name ?? 'session';
233
+ const sessionCookie = createCookieHeader(cookieName, sessionId, {
234
+ httpOnly: config.session?.cookie?.httpOnly ?? true,
235
+ secure: config.session?.cookie?.secure ?? config.isProduction ?? true,
236
+ sameSite: config.session?.cookie?.sameSite ?? 'lax',
237
+ path: config.session?.cookie?.path ?? '/',
238
+ maxAge: expiresInSeconds,
239
+ });
240
+ // Clear state and verifier cookies
241
+ const deleteStateCookie = createDeleteCookieHeader('oauth_state', { path: '/' });
242
+ const deleteVerifierCookie = createDeleteCookieHeader('oauth_code_verifier', { path: '/' });
243
+ // Redirect to success page or home
244
+ const headers = createHeadersWithCookies([sessionCookie, deleteStateCookie, deleteVerifierCookie], '/' // TODO: Make this configurable
245
+ );
246
+ return new Response(null, {
247
+ status: 302,
248
+ headers,
249
+ });
250
+ }
251
+ catch (error) {
252
+ console.error('Google callback error:', error);
253
+ const message = error instanceof Error ? error.message : 'OAuth callback failed';
254
+ return new Response(message, { status: 400 });
255
+ }
256
+ }
257
+ /**
258
+ * Extract request context from HTTP request
259
+ *
260
+ * Extracts IP address and user agent from request headers.
261
+ *
262
+ * @internal
263
+ */
264
+ function getRequestContext(request) {
265
+ // Try various headers for IP address
266
+ const headers = request.headers;
267
+ const ipAddress = headers.get('cf-connecting-ip') || // Cloudflare
268
+ headers.get('x-real-ip') || // Nginx
269
+ headers.get('x-forwarded-for')?.split(',')[0] || // Standard proxy header
270
+ undefined;
271
+ const userAgent = headers.get('user-agent') || undefined;
272
+ return {
273
+ ipAddress,
274
+ userAgent,
275
+ };
276
+ }
277
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/oauth/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { PasswordHasher } from './password-hasher.js';
2
+ export type Argon2idPasswordHasherOptions = {
3
+ memoryCost?: number;
4
+ timeCost?: number;
5
+ parallelism?: number;
6
+ };
7
+ export declare function createArgon2idPasswordHasher(options?: Argon2idPasswordHasherOptions): PasswordHasher;
@@ -0,0 +1,16 @@
1
+ import { hash as argon2Hash, verify as argon2Verify } from '@node-rs/argon2';
2
+ export function createArgon2idPasswordHasher(options = {}) {
3
+ const memoryCost = options.memoryCost ?? 19456;
4
+ const timeCost = options.timeCost ?? 2;
5
+ const parallelism = options.parallelism ?? 1;
6
+ return {
7
+ id: 'argon2id',
8
+ async hash(password) {
9
+ return await argon2Hash(password, { memoryCost, timeCost, parallelism });
10
+ },
11
+ async verify(hash, password) {
12
+ return await argon2Verify(hash, password);
13
+ },
14
+ };
15
+ }
16
+ //# sourceMappingURL=password-hasher-argon2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-hasher-argon2.js","sourceRoot":"","sources":["../src/password-hasher-argon2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAU5E,MAAM,UAAU,4BAA4B,CAAC,UAAyC,EAAE;IACtF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAA;IAC9C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAA;IACtC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAA;IAE5C,OAAO;QACL,EAAE,EAAE,UAAU;QACd,KAAK,CAAC,IAAI,CAAC,QAAgB;YACzB,OAAO,MAAM,UAAU,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAA;QAC1E,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,QAAgB;YACzC,OAAO,MAAM,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC3C,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface PasswordHasher {
2
+ id: string;
3
+ hash(password: string): Promise<string>;
4
+ verify(hash: string, password: string): Promise<boolean>;
5
+ }
6
+ export type Pbkdf2PasswordHasherOptions = {
7
+ iterations?: number;
8
+ hash?: 'SHA-256' | 'SHA-512';
9
+ saltLength?: number;
10
+ keyLength?: number;
11
+ };
12
+ export declare function createPbkdf2PasswordHasher(options?: Pbkdf2PasswordHasherOptions): PasswordHasher;
@@ -0,0 +1,115 @@
1
+ import { base64url } from 'oslo/encoding';
2
+ function encodeBase64UrlNoPadding(bytes) {
3
+ return base64url.encode(bytes, { includePadding: false });
4
+ }
5
+ function decodeBase64UrlNoPadding(value) {
6
+ return base64url.decode(value, { strict: false });
7
+ }
8
+ function timingSafeEqual(a, b) {
9
+ if (a.length !== b.length) {
10
+ return false;
11
+ }
12
+ let diff = 0;
13
+ for (let i = 0; i < a.length; i++) {
14
+ diff |= a[i] ^ b[i];
15
+ }
16
+ return diff === 0;
17
+ }
18
+ async function pbkdf2DeriveKeyBytes(params) {
19
+ const encoder = new TextEncoder();
20
+ const keyMaterial = await crypto.subtle.importKey('raw', encoder.encode(params.password), 'PBKDF2', false, [
21
+ 'deriveBits',
22
+ ]);
23
+ // WebCrypto types can be picky about the exact ArrayBuffer type; Uint8Array is valid at runtime.
24
+ const saltBufferSource = params.salt;
25
+ const bits = await crypto.subtle.deriveBits({
26
+ name: 'PBKDF2',
27
+ salt: saltBufferSource,
28
+ iterations: params.iterations,
29
+ hash: params.hash,
30
+ }, keyMaterial, params.keyLength * 8);
31
+ return new Uint8Array(bits);
32
+ }
33
+ export function createPbkdf2PasswordHasher(options = {}) {
34
+ const iterations = options.iterations ?? 600000;
35
+ const hash = options.hash ?? 'SHA-256';
36
+ const saltLength = options.saltLength ?? 16;
37
+ const keyLength = options.keyLength ?? 32;
38
+ const MIN_ITERATIONS = 100000;
39
+ const MAX_ITERATIONS = 2000000;
40
+ const MIN_KEY_LENGTH = 16;
41
+ const MAX_KEY_LENGTH = 64;
42
+ const MIN_SALT_LENGTH = 8;
43
+ const MAX_SALT_LENGTH = 64;
44
+ return {
45
+ id: 'pbkdf2',
46
+ async hash(password) {
47
+ const salt = new Uint8Array(saltLength);
48
+ crypto.getRandomValues(salt);
49
+ const derivedKey = await pbkdf2DeriveKeyBytes({ password, salt, iterations, hash, keyLength });
50
+ const saltB64 = encodeBase64UrlNoPadding(salt);
51
+ const keyB64 = encodeBase64UrlNoPadding(derivedKey);
52
+ const hashName = hash.toLowerCase().replace('-', '');
53
+ return `$pbkdf2$${hashName}$i=${iterations}$l=${keyLength}$${saltB64}$${keyB64}`;
54
+ },
55
+ async verify(storedHash, password) {
56
+ if (!storedHash.startsWith('$pbkdf2$')) {
57
+ return false;
58
+ }
59
+ const parts = storedHash.split('$');
60
+ if (parts.length !== 7) {
61
+ return false;
62
+ }
63
+ const algorithm = parts[2];
64
+ const iterationsPart = parts[3];
65
+ const keyLengthPart = parts[4];
66
+ const saltPart = parts[5];
67
+ const keyPart = parts[6];
68
+ if (!iterationsPart?.startsWith('i=') || !keyLengthPart?.startsWith('l=')) {
69
+ return false;
70
+ }
71
+ const parsedIterations = Number(iterationsPart.slice(2));
72
+ const parsedKeyLength = Number(keyLengthPart.slice(2));
73
+ if (!Number.isFinite(parsedIterations) || parsedIterations < MIN_ITERATIONS || parsedIterations > MAX_ITERATIONS) {
74
+ return false;
75
+ }
76
+ if (!Number.isFinite(parsedKeyLength) || parsedKeyLength < MIN_KEY_LENGTH || parsedKeyLength > MAX_KEY_LENGTH) {
77
+ return false;
78
+ }
79
+ let parsedHash = null;
80
+ if (algorithm === 'sha256') {
81
+ parsedHash = 'SHA-256';
82
+ }
83
+ else if (algorithm === 'sha512') {
84
+ parsedHash = 'SHA-512';
85
+ }
86
+ if (!parsedHash) {
87
+ return false;
88
+ }
89
+ let salt;
90
+ let expectedKey;
91
+ try {
92
+ salt = decodeBase64UrlNoPadding(saltPart);
93
+ expectedKey = decodeBase64UrlNoPadding(keyPart);
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ if (salt.length < MIN_SALT_LENGTH || salt.length > MAX_SALT_LENGTH) {
99
+ return false;
100
+ }
101
+ if (expectedKey.length !== parsedKeyLength) {
102
+ return false;
103
+ }
104
+ const derivedKey = await pbkdf2DeriveKeyBytes({
105
+ password,
106
+ salt,
107
+ iterations: parsedIterations,
108
+ hash: parsedHash,
109
+ keyLength: parsedKeyLength,
110
+ });
111
+ return timingSafeEqual(derivedKey, expectedKey);
112
+ },
113
+ };
114
+ }
115
+ //# sourceMappingURL=password-hasher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-hasher.js","sourceRoot":"","sources":["../src/password-hasher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * React hooks and utilities for ClearAuth
3
+ *
4
+ * This module provides React hooks for authentication with ClearAuth.
5
+ * Works with any React framework (Next.js, Vite, Create React App, etc.).
6
+ *
7
+ * @module react
8
+ */
9
+ import type { User } from './database/schema.js';
10
+ /**
11
+ * Authentication state returned by useAuth hook
12
+ */
13
+ export interface AuthState {
14
+ /** Current authenticated user, null if not authenticated */
15
+ user: User | null;
16
+ /** True while checking session status */
17
+ loading: boolean;
18
+ /** Error message if authentication failed */
19
+ error: string | null;
20
+ }
21
+ /**
22
+ * Authentication actions provided by useAuth hook
23
+ */
24
+ export interface AuthActions {
25
+ /** Sign in with email and password */
26
+ signIn: (email: string, password: string) => Promise<void>;
27
+ /** Sign up with email and password */
28
+ signUp: (email: string, password: string, name?: string) => Promise<void>;
29
+ /** Sign out the current user */
30
+ signOut: () => Promise<void>;
31
+ /** Initiate GitHub OAuth flow */
32
+ loginWithGitHub: () => void;
33
+ /** Initiate Google OAuth flow */
34
+ loginWithGoogle: () => void;
35
+ /** Request password reset for email */
36
+ requestPasswordReset: (email: string) => Promise<void>;
37
+ /** Reset password with token */
38
+ resetPassword: (token: string, newPassword: string) => Promise<void>;
39
+ /** Verify email with token */
40
+ verifyEmail: (token: string) => Promise<void>;
41
+ /** Resend email verification */
42
+ resendVerification: (email: string) => Promise<void>;
43
+ /** Refresh session to get latest user data */
44
+ refresh: () => Promise<void>;
45
+ }
46
+ /**
47
+ * Combined auth state and actions
48
+ */
49
+ export type AuthContextValue = AuthState & AuthActions;
50
+ /**
51
+ * Configuration for the auth provider
52
+ */
53
+ export interface AuthProviderConfig {
54
+ /** Base URL for auth API (e.g., "https://api.example.com" or "/api/auth") */
55
+ baseUrl?: string;
56
+ /** Custom fetch function (useful for testing or adding auth headers) */
57
+ fetchFn?: typeof fetch;
58
+ }
59
+ /**
60
+ * Authentication provider component
61
+ *
62
+ * Wrap your app with this provider to enable authentication hooks.
63
+ *
64
+ * @param props - Provider props
65
+ * @param props.children - Child components
66
+ * @param props.baseUrl - Base URL for auth API (defaults to "/api/auth")
67
+ * @param props.fetchFn - Custom fetch function (defaults to global fetch)
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * import { AuthProvider } from 'clearauth/react'
72
+ *
73
+ * function App() {
74
+ * return (
75
+ * <AuthProvider baseUrl="/api/auth">
76
+ * <YourApp />
77
+ * </AuthProvider>
78
+ * )
79
+ * }
80
+ * ```
81
+ *
82
+ * @example With custom base URL
83
+ * ```tsx
84
+ * <AuthProvider baseUrl="https://api.example.com/auth">
85
+ * <YourApp />
86
+ * </AuthProvider>
87
+ * ```
88
+ */
89
+ export declare function AuthProvider({ children, baseUrl, fetchFn, }: {
90
+ children: React.ReactNode;
91
+ } & AuthProviderConfig): import("react/jsx-runtime").JSX.Element;
92
+ /**
93
+ * Hook to access authentication state and actions
94
+ *
95
+ * Must be used within an AuthProvider.
96
+ *
97
+ * @returns Auth state and actions
98
+ * @throws Error if used outside AuthProvider
99
+ *
100
+ * @example
101
+ * ```tsx
102
+ * function LoginForm() {
103
+ * const { signIn, loading, error } = useAuth()
104
+ *
105
+ * const handleSubmit = async (e) => {
106
+ * e.preventDefault()
107
+ * await signIn(email, password)
108
+ * }
109
+ *
110
+ * return <form onSubmit={handleSubmit}>...</form>
111
+ * }
112
+ * ```
113
+ */
114
+ export declare function useAuth(): AuthContextValue;
115
+ /**
116
+ * Hook to access current user
117
+ *
118
+ * Convenience hook that only returns the user object.
119
+ *
120
+ * @returns Current user or null
121
+ *
122
+ * @example
123
+ * ```tsx
124
+ * function UserProfile() {
125
+ * const user = useUser()
126
+ *
127
+ * if (!user) return <div>Not logged in</div>
128
+ *
129
+ * return <div>Welcome, {user.name}!</div>
130
+ * }
131
+ * ```
132
+ */
133
+ export declare function useUser(): User | null;
134
+ /**
135
+ * Hook to check if user is authenticated
136
+ *
137
+ * @returns True if user is authenticated
138
+ *
139
+ * @example
140
+ * ```tsx
141
+ * function ProtectedContent() {
142
+ * const isAuthenticated = useIsAuthenticated()
143
+ *
144
+ * if (!isAuthenticated) {
145
+ * return <LoginPrompt />
146
+ * }
147
+ *
148
+ * return <SecretContent />
149
+ * }
150
+ * ```
151
+ */
152
+ export declare function useIsAuthenticated(): boolean;