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,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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"}
|
package/dist/react.d.ts
ADDED
|
@@ -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;
|