@alliance-droid/svelte-auth-core 1.0.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/dist/adapter-context.d.ts +19 -0
- package/dist/adapter-context.d.ts.map +1 -0
- package/dist/adapter-context.js +68 -0
- package/dist/adapter-context.js.map +1 -0
- package/dist/adapters/__tests__/adapter-tests.d.ts +7 -0
- package/dist/adapters/__tests__/adapter-tests.d.ts.map +1 -0
- package/dist/adapters/__tests__/adapter-tests.js +206 -0
- package/dist/adapters/__tests__/adapter-tests.js.map +1 -0
- package/dist/adapters/adapter.d.ts +60 -0
- package/dist/adapters/adapter.d.ts.map +1 -0
- package/dist/adapters/adapter.js +2 -0
- package/dist/adapters/adapter.js.map +1 -0
- package/dist/adapters/filesystem-adapter.d.ts +26 -0
- package/dist/adapters/filesystem-adapter.d.ts.map +1 -0
- package/dist/adapters/filesystem-adapter.js +148 -0
- package/dist/adapters/filesystem-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +5 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mongodb-adapter.d.ts +27 -0
- package/dist/adapters/mongodb-adapter.d.ts.map +1 -0
- package/dist/adapters/mongodb-adapter.js +213 -0
- package/dist/adapters/mongodb-adapter.js.map +1 -0
- package/dist/adapters/postgres-adapter.d.ts +30 -0
- package/dist/adapters/postgres-adapter.d.ts.map +1 -0
- package/dist/adapters/postgres-adapter.js +237 -0
- package/dist/adapters/postgres-adapter.js.map +1 -0
- package/dist/adapters/sqlite-adapter.d.ts +26 -0
- package/dist/adapters/sqlite-adapter.d.ts.map +1 -0
- package/dist/adapters/sqlite-adapter.js +261 -0
- package/dist/adapters/sqlite-adapter.js.map +1 -0
- package/dist/auth.d.ts +48 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +205 -0
- package/dist/auth.js.map +1 -0
- package/dist/client-jwt.d.ts +30 -0
- package/dist/client-jwt.d.ts.map +1 -0
- package/dist/client-jwt.js +57 -0
- package/dist/client-jwt.js.map +1 -0
- package/dist/client-store.d.ts +31 -0
- package/dist/client-store.d.ts.map +1 -0
- package/dist/client-store.js +122 -0
- package/dist/client-store.js.map +1 -0
- package/dist/cors.d.ts +48 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +88 -0
- package/dist/cors.js.map +1 -0
- package/dist/csrf.d.ts +57 -0
- package/dist/csrf.d.ts.map +1 -0
- package/dist/csrf.js +95 -0
- package/dist/csrf.js.map +1 -0
- package/dist/db.d.ts +22 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +43 -0
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/input-validation.d.ts +78 -0
- package/dist/input-validation.d.ts.map +1 -0
- package/dist/input-validation.js +238 -0
- package/dist/input-validation.js.map +1 -0
- package/dist/oauth-callback.d.ts +31 -0
- package/dist/oauth-callback.d.ts.map +1 -0
- package/dist/oauth-callback.js +254 -0
- package/dist/oauth-callback.js.map +1 -0
- package/dist/oauth-providers.d.ts +92 -0
- package/dist/oauth-providers.d.ts.map +1 -0
- package/dist/oauth-providers.js +213 -0
- package/dist/oauth-providers.js.map +1 -0
- package/dist/oauth-types.d.ts +77 -0
- package/dist/oauth-types.d.ts.map +1 -0
- package/dist/oauth-types.js +2 -0
- package/dist/oauth-types.js.map +1 -0
- package/dist/password.d.ts +31 -0
- package/dist/password.d.ts.map +1 -0
- package/dist/password.js +54 -0
- package/dist/password.js.map +1 -0
- package/dist/providers/github-oauth.d.ts +58 -0
- package/dist/providers/github-oauth.d.ts.map +1 -0
- package/dist/providers/github-oauth.js +230 -0
- package/dist/providers/github-oauth.js.map +1 -0
- package/dist/providers/google-oauth.d.ts +46 -0
- package/dist/providers/google-oauth.d.ts.map +1 -0
- package/dist/providers/google-oauth.js +177 -0
- package/dist/providers/google-oauth.js.map +1 -0
- package/dist/providers/oidc-oauth.d.ts +85 -0
- package/dist/providers/oidc-oauth.d.ts.map +1 -0
- package/dist/providers/oidc-oauth.js +301 -0
- package/dist/providers/oidc-oauth.js.map +1 -0
- package/dist/rate-limit.d.ts +36 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +88 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/rate-limiting.d.ts +113 -0
- package/dist/rate-limiting.d.ts.map +1 -0
- package/dist/rate-limiting.js +221 -0
- package/dist/rate-limiting.js.map +1 -0
- package/dist/security-headers.d.ts +54 -0
- package/dist/security-headers.d.ts.map +1 -0
- package/dist/security-headers.js +123 -0
- package/dist/security-headers.js.map +1 -0
- package/dist/session.d.ts +13 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +33 -0
- package/dist/session.js.map +1 -0
- package/dist/sql-injection-prevention.d.ts +94 -0
- package/dist/sql-injection-prevention.d.ts.map +1 -0
- package/dist/sql-injection-prevention.js +222 -0
- package/dist/sql-injection-prevention.js.map +1 -0
- package/dist/token.d.ts +22 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +31 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/user.d.ts +33 -0
- package/dist/user.d.ts.map +1 -0
- package/dist/user.js +144 -0
- package/dist/user.js.map +1 -0
- package/package.json +48 -0
- package/src/adapter-context.ts +72 -0
- package/src/adapters/__tests__/adapter-tests.ts +254 -0
- package/src/adapters/__tests__/filesystem-adapter.test.ts +48 -0
- package/src/adapters/__tests__/mongodb-adapter.test.ts +64 -0
- package/src/adapters/__tests__/postgres-adapter.test.ts +62 -0
- package/src/adapters/__tests__/sqlite-adapter.test.ts +103 -0
- package/src/adapters/__tests__/test-fs-adapter.json +4 -0
- package/src/adapters/adapter.ts +72 -0
- package/src/adapters/filesystem-adapter.ts +153 -0
- package/src/adapters/index.ts +5 -0
- package/src/adapters/mongodb-adapter.ts +208 -0
- package/src/adapters/postgres-adapter.ts +261 -0
- package/src/adapters/sqlite-adapter.ts +284 -0
- package/src/auth.ts +239 -0
- package/src/client-jwt.test.ts +137 -0
- package/src/client-jwt.ts +67 -0
- package/src/client-store.test.ts +149 -0
- package/src/client-store.ts +144 -0
- package/src/cors.test.ts +175 -0
- package/src/cors.ts +115 -0
- package/src/csrf.test.ts +226 -0
- package/src/csrf.ts +126 -0
- package/src/db.ts +57 -0
- package/src/index.ts +143 -0
- package/src/input-validation.test.ts +347 -0
- package/src/input-validation.ts +307 -0
- package/src/integration.test.ts +322 -0
- package/src/oauth-callback.test.ts +282 -0
- package/src/oauth-callback.ts +323 -0
- package/src/oauth-providers.ts +232 -0
- package/src/oauth-types.ts +82 -0
- package/src/password.test.ts +89 -0
- package/src/password.ts +62 -0
- package/src/providers/github-oauth.test.ts +290 -0
- package/src/providers/github-oauth.ts +226 -0
- package/src/providers/google-oauth.test.ts +240 -0
- package/src/providers/google-oauth.ts +166 -0
- package/src/providers/oidc-oauth.test.ts +367 -0
- package/src/providers/oidc-oauth.ts +302 -0
- package/src/rate-limit.test.ts +308 -0
- package/src/rate-limit.ts +118 -0
- package/src/rate-limiting.test.ts +390 -0
- package/src/rate-limiting.ts +275 -0
- package/src/security-headers.test.ts +242 -0
- package/src/security-headers.ts +160 -0
- package/src/security-penetration.test.ts +705 -0
- package/src/session.ts +42 -0
- package/src/sql-injection-prevention.test.ts +337 -0
- package/src/sql-injection-prevention.ts +272 -0
- package/src/token.test.ts +67 -0
- package/src/token.ts +34 -0
- package/src/types.ts +87 -0
- package/src/user.ts +165 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Validation and Sanitization
|
|
3
|
+
* Provides secure input validation and sanitization utilities
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validation result
|
|
7
|
+
*/
|
|
8
|
+
export interface ValidationResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
errors: string[];
|
|
11
|
+
value?: unknown;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Sanitize string input
|
|
15
|
+
* - Trim whitespace
|
|
16
|
+
* - Remove potentially harmful characters
|
|
17
|
+
* @param input - Input string
|
|
18
|
+
* @returns Sanitized string
|
|
19
|
+
*/
|
|
20
|
+
export declare function sanitizeString(input: unknown): string;
|
|
21
|
+
/**
|
|
22
|
+
* Validate email address
|
|
23
|
+
* @param email - Email to validate
|
|
24
|
+
* @returns Validation result
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateEmail(email: unknown): ValidationResult;
|
|
27
|
+
/**
|
|
28
|
+
* Validate password
|
|
29
|
+
* - Minimum 8 characters
|
|
30
|
+
* - Must contain uppercase
|
|
31
|
+
* - Must contain lowercase
|
|
32
|
+
* - Must contain number
|
|
33
|
+
* @param password - Password to validate
|
|
34
|
+
* @returns Validation result
|
|
35
|
+
*/
|
|
36
|
+
export declare function validatePassword(password: unknown): ValidationResult;
|
|
37
|
+
/**
|
|
38
|
+
* Validate username
|
|
39
|
+
* - 3-30 characters
|
|
40
|
+
* - Alphanumeric, underscore, hyphen only
|
|
41
|
+
* @param username - Username to validate
|
|
42
|
+
* @returns Validation result
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateUsername(username: unknown): ValidationResult;
|
|
45
|
+
/**
|
|
46
|
+
* Validate URL
|
|
47
|
+
* @param url - URL to validate
|
|
48
|
+
* @returns Validation result
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateUrl(url: unknown): ValidationResult;
|
|
51
|
+
/**
|
|
52
|
+
* Validate number
|
|
53
|
+
* @param value - Value to validate
|
|
54
|
+
* @param min - Minimum value (inclusive)
|
|
55
|
+
* @param max - Maximum value (inclusive)
|
|
56
|
+
* @returns Validation result
|
|
57
|
+
*/
|
|
58
|
+
export declare function validateNumber(value: unknown, min?: number, max?: number): ValidationResult;
|
|
59
|
+
/**
|
|
60
|
+
* Validate UUID
|
|
61
|
+
* @param uuid - UUID to validate
|
|
62
|
+
* @returns Validation result
|
|
63
|
+
*/
|
|
64
|
+
export declare function validateUuid(uuid: unknown): ValidationResult;
|
|
65
|
+
/**
|
|
66
|
+
* Escape HTML entities to prevent XSS
|
|
67
|
+
* @param html - HTML string to escape
|
|
68
|
+
* @returns Escaped string
|
|
69
|
+
*/
|
|
70
|
+
export declare function escapeHtml(html: unknown): string;
|
|
71
|
+
/**
|
|
72
|
+
* Validate object properties
|
|
73
|
+
* @param obj - Object to validate
|
|
74
|
+
* @param schema - Validation schema
|
|
75
|
+
* @returns Validation result
|
|
76
|
+
*/
|
|
77
|
+
export declare function validateObject(obj: unknown, schema: Record<string, (val: unknown) => ValidationResult>): ValidationResult;
|
|
78
|
+
//# sourceMappingURL=input-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validation.d.ts","sourceRoot":"","sources":["../src/input-validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAarD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAkC9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,gBAAgB,CAkCpE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,gBAAgB,CAgCpE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAyB1D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,EACd,GAAG,CAAC,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,GACV,gBAAgB,CAqBlB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAkB5D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAgBhD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC7B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,gBAAgB,CAAC,GACxD,gBAAgB,CAoBlB"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Validation and Sanitization
|
|
3
|
+
* Provides secure input validation and sanitization utilities
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Sanitize string input
|
|
7
|
+
* - Trim whitespace
|
|
8
|
+
* - Remove potentially harmful characters
|
|
9
|
+
* @param input - Input string
|
|
10
|
+
* @returns Sanitized string
|
|
11
|
+
*/
|
|
12
|
+
export function sanitizeString(input) {
|
|
13
|
+
if (typeof input !== 'string') {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
// Trim whitespace
|
|
17
|
+
let sanitized = input.trim();
|
|
18
|
+
// Remove control characters (ASCII 0-31 and 127)
|
|
19
|
+
// eslint-disable-next-line no-control-regex
|
|
20
|
+
sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, '');
|
|
21
|
+
return sanitized;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate email address
|
|
25
|
+
* @param email - Email to validate
|
|
26
|
+
* @returns Validation result
|
|
27
|
+
*/
|
|
28
|
+
export function validateEmail(email) {
|
|
29
|
+
const errors = [];
|
|
30
|
+
if (typeof email !== 'string') {
|
|
31
|
+
errors.push('Email must be a string');
|
|
32
|
+
return { valid: false, errors };
|
|
33
|
+
}
|
|
34
|
+
const sanitized = sanitizeString(email);
|
|
35
|
+
if (!sanitized) {
|
|
36
|
+
errors.push('Email cannot be empty');
|
|
37
|
+
return { valid: false, errors };
|
|
38
|
+
}
|
|
39
|
+
if (sanitized.length > 254) {
|
|
40
|
+
errors.push('Email is too long (max 254 characters)');
|
|
41
|
+
return { valid: false, errors };
|
|
42
|
+
}
|
|
43
|
+
// RFC 5322 simplified email regex (more permissive than strict RFC)
|
|
44
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
45
|
+
if (!emailRegex.test(sanitized)) {
|
|
46
|
+
errors.push('Invalid email format');
|
|
47
|
+
return { valid: false, errors };
|
|
48
|
+
}
|
|
49
|
+
// Check for common injection patterns
|
|
50
|
+
if (sanitized.includes('<') || sanitized.includes('>') || sanitized.includes('"')) {
|
|
51
|
+
errors.push('Email contains invalid characters');
|
|
52
|
+
return { valid: false, errors };
|
|
53
|
+
}
|
|
54
|
+
return { valid: true, errors, value: sanitized };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Validate password
|
|
58
|
+
* - Minimum 8 characters
|
|
59
|
+
* - Must contain uppercase
|
|
60
|
+
* - Must contain lowercase
|
|
61
|
+
* - Must contain number
|
|
62
|
+
* @param password - Password to validate
|
|
63
|
+
* @returns Validation result
|
|
64
|
+
*/
|
|
65
|
+
export function validatePassword(password) {
|
|
66
|
+
const errors = [];
|
|
67
|
+
if (typeof password !== 'string') {
|
|
68
|
+
errors.push('Password must be a string');
|
|
69
|
+
return { valid: false, errors };
|
|
70
|
+
}
|
|
71
|
+
if (!password) {
|
|
72
|
+
errors.push('Password cannot be empty');
|
|
73
|
+
return { valid: false, errors };
|
|
74
|
+
}
|
|
75
|
+
if (password.length < 8) {
|
|
76
|
+
errors.push('Password must be at least 8 characters');
|
|
77
|
+
}
|
|
78
|
+
if (!/[A-Z]/.test(password)) {
|
|
79
|
+
errors.push('Password must contain an uppercase letter');
|
|
80
|
+
}
|
|
81
|
+
if (!/[a-z]/.test(password)) {
|
|
82
|
+
errors.push('Password must contain a lowercase letter');
|
|
83
|
+
}
|
|
84
|
+
if (!/\d/.test(password)) {
|
|
85
|
+
errors.push('Password must contain a number');
|
|
86
|
+
}
|
|
87
|
+
if (errors.length > 0) {
|
|
88
|
+
return { valid: false, errors };
|
|
89
|
+
}
|
|
90
|
+
return { valid: true, errors };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Validate username
|
|
94
|
+
* - 3-30 characters
|
|
95
|
+
* - Alphanumeric, underscore, hyphen only
|
|
96
|
+
* @param username - Username to validate
|
|
97
|
+
* @returns Validation result
|
|
98
|
+
*/
|
|
99
|
+
export function validateUsername(username) {
|
|
100
|
+
const errors = [];
|
|
101
|
+
if (typeof username !== 'string') {
|
|
102
|
+
errors.push('Username must be a string');
|
|
103
|
+
return { valid: false, errors };
|
|
104
|
+
}
|
|
105
|
+
const sanitized = sanitizeString(username);
|
|
106
|
+
if (!sanitized) {
|
|
107
|
+
errors.push('Username cannot be empty');
|
|
108
|
+
return { valid: false, errors };
|
|
109
|
+
}
|
|
110
|
+
if (sanitized.length < 3) {
|
|
111
|
+
errors.push('Username must be at least 3 characters');
|
|
112
|
+
}
|
|
113
|
+
if (sanitized.length > 30) {
|
|
114
|
+
errors.push('Username must be at most 30 characters');
|
|
115
|
+
}
|
|
116
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(sanitized)) {
|
|
117
|
+
errors.push('Username can only contain letters, numbers, underscores, and hyphens');
|
|
118
|
+
}
|
|
119
|
+
if (errors.length > 0) {
|
|
120
|
+
return { valid: false, errors };
|
|
121
|
+
}
|
|
122
|
+
return { valid: true, errors, value: sanitized };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Validate URL
|
|
126
|
+
* @param url - URL to validate
|
|
127
|
+
* @returns Validation result
|
|
128
|
+
*/
|
|
129
|
+
export function validateUrl(url) {
|
|
130
|
+
const errors = [];
|
|
131
|
+
if (typeof url !== 'string') {
|
|
132
|
+
errors.push('URL must be a string');
|
|
133
|
+
return { valid: false, errors };
|
|
134
|
+
}
|
|
135
|
+
const sanitized = sanitizeString(url);
|
|
136
|
+
try {
|
|
137
|
+
const urlObj = new URL(sanitized);
|
|
138
|
+
// Only allow http and https
|
|
139
|
+
if (!['http:', 'https:'].includes(urlObj.protocol)) {
|
|
140
|
+
errors.push('Only HTTP and HTTPS protocols are allowed');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
errors.push('Invalid URL format');
|
|
145
|
+
}
|
|
146
|
+
if (errors.length > 0) {
|
|
147
|
+
return { valid: false, errors };
|
|
148
|
+
}
|
|
149
|
+
return { valid: true, errors, value: sanitized };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Validate number
|
|
153
|
+
* @param value - Value to validate
|
|
154
|
+
* @param min - Minimum value (inclusive)
|
|
155
|
+
* @param max - Maximum value (inclusive)
|
|
156
|
+
* @returns Validation result
|
|
157
|
+
*/
|
|
158
|
+
export function validateNumber(value, min, max) {
|
|
159
|
+
const errors = [];
|
|
160
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
161
|
+
errors.push('Value must be a valid number');
|
|
162
|
+
return { valid: false, errors };
|
|
163
|
+
}
|
|
164
|
+
if (min !== undefined && value < min) {
|
|
165
|
+
errors.push(`Value must be at least ${min}`);
|
|
166
|
+
}
|
|
167
|
+
if (max !== undefined && value > max) {
|
|
168
|
+
errors.push(`Value must be at most ${max}`);
|
|
169
|
+
}
|
|
170
|
+
if (errors.length > 0) {
|
|
171
|
+
return { valid: false, errors };
|
|
172
|
+
}
|
|
173
|
+
return { valid: true, errors, value };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Validate UUID
|
|
177
|
+
* @param uuid - UUID to validate
|
|
178
|
+
* @returns Validation result
|
|
179
|
+
*/
|
|
180
|
+
export function validateUuid(uuid) {
|
|
181
|
+
const errors = [];
|
|
182
|
+
if (typeof uuid !== 'string') {
|
|
183
|
+
errors.push('UUID must be a string');
|
|
184
|
+
return { valid: false, errors };
|
|
185
|
+
}
|
|
186
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
187
|
+
if (!uuidRegex.test(uuid)) {
|
|
188
|
+
errors.push('Invalid UUID format');
|
|
189
|
+
}
|
|
190
|
+
if (errors.length > 0) {
|
|
191
|
+
return { valid: false, errors };
|
|
192
|
+
}
|
|
193
|
+
return { valid: true, errors, value: uuid.toLowerCase() };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Escape HTML entities to prevent XSS
|
|
197
|
+
* @param html - HTML string to escape
|
|
198
|
+
* @returns Escaped string
|
|
199
|
+
*/
|
|
200
|
+
export function escapeHtml(html) {
|
|
201
|
+
if (typeof html !== 'string') {
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
204
|
+
// Entity map for HTML escaping
|
|
205
|
+
const entityMap = {
|
|
206
|
+
'&': '&',
|
|
207
|
+
'<': '<',
|
|
208
|
+
'>': '>',
|
|
209
|
+
'"': '"',
|
|
210
|
+
"'": ''',
|
|
211
|
+
'/': '/'
|
|
212
|
+
};
|
|
213
|
+
return html.replace(/[&<>"'\/]/g, (s) => entityMap[s]);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Validate object properties
|
|
217
|
+
* @param obj - Object to validate
|
|
218
|
+
* @param schema - Validation schema
|
|
219
|
+
* @returns Validation result
|
|
220
|
+
*/
|
|
221
|
+
export function validateObject(obj, schema) {
|
|
222
|
+
const errors = [];
|
|
223
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
224
|
+
errors.push('Value must be an object');
|
|
225
|
+
return { valid: false, errors };
|
|
226
|
+
}
|
|
227
|
+
for (const [key, validator] of Object.entries(schema)) {
|
|
228
|
+
const result = validator(obj[key]);
|
|
229
|
+
if (!result.valid) {
|
|
230
|
+
errors.push(`${key}: ${result.errors.join(', ')}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (errors.length > 0) {
|
|
234
|
+
return { valid: false, errors };
|
|
235
|
+
}
|
|
236
|
+
return { valid: true, errors };
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=input-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validation.js","sourceRoot":"","sources":["../src/input-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { OAuthUserProfile, OAuthCallbackResponse, OAuthSession, OAuthUser } from './oauth-types';
|
|
2
|
+
/**
|
|
3
|
+
* Handle OAuth callback
|
|
4
|
+
* Creates or updates user and session
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleOAuthCallback(profile: OAuthUserProfile, accessToken: string, refreshToken?: string, tokenExpiresIn?: number): Promise<OAuthCallbackResponse>;
|
|
7
|
+
/**
|
|
8
|
+
* Get OAuth session by ID
|
|
9
|
+
*/
|
|
10
|
+
export declare function getOAuthSession(sessionId: string): OAuthSession | null;
|
|
11
|
+
/**
|
|
12
|
+
* Destroy/logout an OAuth session
|
|
13
|
+
*/
|
|
14
|
+
export declare function destroyOAuthSession(sessionId: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Refresh OAuth token
|
|
17
|
+
*/
|
|
18
|
+
export declare function refreshOAuthToken(sessionId: string, newAccessToken: string, newRefreshToken?: string, newTokenExpiresIn?: number): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Get OAuth user by ID
|
|
21
|
+
*/
|
|
22
|
+
export declare function getOAuthUser(userId: string): OAuthUser | null;
|
|
23
|
+
/**
|
|
24
|
+
* Update OAuth user profile
|
|
25
|
+
*/
|
|
26
|
+
export declare function updateOAuthUserProfile(userId: string, updates: Partial<OAuthUser>): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Clean up expired OAuth sessions
|
|
29
|
+
*/
|
|
30
|
+
export declare function cleanupExpiredOAuthSessions(): number;
|
|
31
|
+
//# sourceMappingURL=oauth-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-callback.d.ts","sourceRoot":"","sources":["../src/oauth-callback.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,SAAS,EACT,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,gBAAgB,EACzB,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,EACrB,cAAc,GAAE,MAAa,GAC3B,OAAO,CAAC,qBAAqB,CAAC,CA4FhC;AAsFD;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAUtE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAe9D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,eAAe,CAAC,EAAE,MAAM,EACxB,iBAAiB,GAAE,MAAa,GAC9B,OAAO,CAAC,OAAO,CAAC,CAsBlB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAK7D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAiB3F;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAwBpD"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { getDatabase, saveDatabase } from './db';
|
|
2
|
+
import { generateId } from './token';
|
|
3
|
+
/**
|
|
4
|
+
* Handle OAuth callback
|
|
5
|
+
* Creates or updates user and session
|
|
6
|
+
*/
|
|
7
|
+
export async function handleOAuthCallback(profile, accessToken, refreshToken, tokenExpiresIn = 3600) {
|
|
8
|
+
if (!profile.email || !profile.id || !profile.provider) {
|
|
9
|
+
return {
|
|
10
|
+
success: false,
|
|
11
|
+
message: 'Invalid OAuth profile data',
|
|
12
|
+
error: 'Missing required profile fields'
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const db = getDatabase();
|
|
17
|
+
// Check if user exists by OAuth provider
|
|
18
|
+
let user = findUserByOAuthProvider(profile.provider, profile.id);
|
|
19
|
+
if (!user) {
|
|
20
|
+
// Check if user with this email exists
|
|
21
|
+
user = findUserByEmail(profile.email);
|
|
22
|
+
if (user) {
|
|
23
|
+
// Link OAuth provider to existing user
|
|
24
|
+
if (!user.oauthProviders) {
|
|
25
|
+
user.oauthProviders = [];
|
|
26
|
+
}
|
|
27
|
+
const existingProvider = user.oauthProviders.find((p) => p.provider === profile.provider);
|
|
28
|
+
if (!existingProvider) {
|
|
29
|
+
user.oauthProviders.push({
|
|
30
|
+
provider: profile.provider,
|
|
31
|
+
providerUserId: profile.id,
|
|
32
|
+
connectedAt: Date.now()
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Update user info from OAuth profile
|
|
36
|
+
if (profile.name && !user.name) {
|
|
37
|
+
user.name = profile.name;
|
|
38
|
+
}
|
|
39
|
+
if (profile.avatar && !user.avatar) {
|
|
40
|
+
user.avatar = profile.avatar;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Create new user from OAuth profile
|
|
45
|
+
user = createOAuthUser(profile);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
user.updatedAt = Date.now();
|
|
49
|
+
user.emailVerified = true; // OAuth providers verify email
|
|
50
|
+
// Save updated user
|
|
51
|
+
if (!db.oauthUsers) {
|
|
52
|
+
db.oauthUsers = {};
|
|
53
|
+
}
|
|
54
|
+
db.oauthUsers[user.id] = user;
|
|
55
|
+
saveDatabase();
|
|
56
|
+
// Create OAuth session
|
|
57
|
+
const session = createOAuthSession(user.id, user.email, profile.provider, profile.id, accessToken, refreshToken, tokenExpiresIn);
|
|
58
|
+
if (!db.oauthSessions) {
|
|
59
|
+
db.oauthSessions = {};
|
|
60
|
+
}
|
|
61
|
+
db.oauthSessions[generateId()] = session;
|
|
62
|
+
saveDatabase();
|
|
63
|
+
const { passwordHash: _, ...userWithoutPassword } = user;
|
|
64
|
+
return {
|
|
65
|
+
success: true,
|
|
66
|
+
message: 'OAuth authentication successful',
|
|
67
|
+
user: userWithoutPassword,
|
|
68
|
+
session
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error('OAuth callback error:', error);
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
message: 'OAuth authentication failed',
|
|
76
|
+
error: String(error)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Find user by OAuth provider and provider user ID
|
|
82
|
+
*/
|
|
83
|
+
function findUserByOAuthProvider(provider, providerUserId) {
|
|
84
|
+
const db = getDatabase();
|
|
85
|
+
const oauthUsers = db.oauthUsers || {};
|
|
86
|
+
for (const user of Object.values(oauthUsers)) {
|
|
87
|
+
if (user.oauthProviders?.some((p) => p.provider === provider && p.providerUserId === providerUserId)) {
|
|
88
|
+
return user;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Find user by email
|
|
95
|
+
*/
|
|
96
|
+
function findUserByEmail(email) {
|
|
97
|
+
const db = getDatabase();
|
|
98
|
+
const oauthUsers = db.oauthUsers || {};
|
|
99
|
+
for (const user of Object.values(oauthUsers)) {
|
|
100
|
+
if (user.email === email) {
|
|
101
|
+
return user;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create a new OAuth user
|
|
108
|
+
*/
|
|
109
|
+
function createOAuthUser(profile) {
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
return {
|
|
112
|
+
id: generateId(),
|
|
113
|
+
email: profile.email,
|
|
114
|
+
name: profile.name,
|
|
115
|
+
avatar: profile.avatar,
|
|
116
|
+
emailVerified: true, // OAuth providers verify email
|
|
117
|
+
oauthProviders: [
|
|
118
|
+
{
|
|
119
|
+
provider: profile.provider,
|
|
120
|
+
providerUserId: profile.id,
|
|
121
|
+
connectedAt: now
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
createdAt: now,
|
|
125
|
+
updatedAt: now
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create an OAuth session
|
|
130
|
+
*/
|
|
131
|
+
function createOAuthSession(userId, email, provider, providerUserId, accessToken, refreshToken, tokenExpiresIn) {
|
|
132
|
+
const now = Date.now();
|
|
133
|
+
const sessionDuration = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
134
|
+
return {
|
|
135
|
+
userId,
|
|
136
|
+
email,
|
|
137
|
+
provider,
|
|
138
|
+
providerUserId,
|
|
139
|
+
accessToken,
|
|
140
|
+
refreshToken,
|
|
141
|
+
tokenExpiresAt: now + tokenExpiresIn * 1000,
|
|
142
|
+
createdAt: now,
|
|
143
|
+
expiresAt: now + sessionDuration
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get OAuth session by ID
|
|
148
|
+
*/
|
|
149
|
+
export function getOAuthSession(sessionId) {
|
|
150
|
+
const db = getDatabase();
|
|
151
|
+
const oauthSessions = db.oauthSessions || {};
|
|
152
|
+
const session = oauthSessions[sessionId];
|
|
153
|
+
if (!session || session.expiresAt < Date.now()) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return session;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Destroy/logout an OAuth session
|
|
160
|
+
*/
|
|
161
|
+
export function destroyOAuthSession(sessionId) {
|
|
162
|
+
const db = getDatabase();
|
|
163
|
+
const oauthSessions = db.oauthSessions || {};
|
|
164
|
+
try {
|
|
165
|
+
if (oauthSessions[sessionId]) {
|
|
166
|
+
delete oauthSessions[sessionId];
|
|
167
|
+
saveDatabase();
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error('OAuth session destruction error:', error);
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Refresh OAuth token
|
|
179
|
+
*/
|
|
180
|
+
export async function refreshOAuthToken(sessionId, newAccessToken, newRefreshToken, newTokenExpiresIn = 3600) {
|
|
181
|
+
const db = getDatabase();
|
|
182
|
+
const oauthSessions = db.oauthSessions || {};
|
|
183
|
+
const session = oauthSessions[sessionId];
|
|
184
|
+
if (!session) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
session.accessToken = newAccessToken;
|
|
189
|
+
if (newRefreshToken) {
|
|
190
|
+
session.refreshToken = newRefreshToken;
|
|
191
|
+
}
|
|
192
|
+
session.tokenExpiresAt = Date.now() + newTokenExpiresIn * 1000;
|
|
193
|
+
saveDatabase();
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
console.error('Token refresh error:', error);
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get OAuth user by ID
|
|
203
|
+
*/
|
|
204
|
+
export function getOAuthUser(userId) {
|
|
205
|
+
const db = getDatabase();
|
|
206
|
+
const oauthUsers = db.oauthUsers || {};
|
|
207
|
+
return oauthUsers[userId] || null;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Update OAuth user profile
|
|
211
|
+
*/
|
|
212
|
+
export function updateOAuthUserProfile(userId, updates) {
|
|
213
|
+
const db = getDatabase();
|
|
214
|
+
const oauthUsers = db.oauthUsers || {};
|
|
215
|
+
const user = oauthUsers[userId];
|
|
216
|
+
if (!user) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
Object.assign(user, updates, { updatedAt: Date.now() });
|
|
221
|
+
saveDatabase();
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
console.error('User profile update error:', error);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Clean up expired OAuth sessions
|
|
231
|
+
*/
|
|
232
|
+
export function cleanupExpiredOAuthSessions() {
|
|
233
|
+
const db = getDatabase();
|
|
234
|
+
const oauthSessions = db.oauthSessions || {};
|
|
235
|
+
try {
|
|
236
|
+
let count = 0;
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
for (const [sessionId, session] of Object.entries(oauthSessions)) {
|
|
239
|
+
if (session.expiresAt < now) {
|
|
240
|
+
delete oauthSessions[sessionId];
|
|
241
|
+
count++;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (count > 0) {
|
|
245
|
+
saveDatabase();
|
|
246
|
+
}
|
|
247
|
+
return count;
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
console.error('OAuth session cleanup error:', error);
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=oauth-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-callback.js","sourceRoot":"","sources":["../src/oauth-callback.ts"],"names":[],"mappings":"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"}
|