@nauth-toolkit/core 0.1.0 → 0.1.3

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 (184) hide show
  1. package/LICENSE +90 -0
  2. package/README.md +30 -0
  3. package/package.json +7 -2
  4. package/jest.config.js +0 -15
  5. package/jest.setup.ts +0 -6
  6. package/src/adapters/database-columns.ts +0 -165
  7. package/src/adapters/express.adapter.ts +0 -385
  8. package/src/adapters/fastify.adapter.ts +0 -416
  9. package/src/adapters/index.ts +0 -16
  10. package/src/adapters/storage.factory.ts +0 -143
  11. package/src/bootstrap.ts +0 -374
  12. package/src/dto/auth-challenge.dto.ts +0 -231
  13. package/src/dto/auth-response.dto.ts +0 -253
  14. package/src/dto/challenge-response.dto.ts +0 -234
  15. package/src/dto/change-password-request.dto.ts +0 -50
  16. package/src/dto/change-password-response.dto.ts +0 -29
  17. package/src/dto/change-password.dto.ts +0 -57
  18. package/src/dto/error-response.dto.ts +0 -136
  19. package/src/dto/get-available-methods.dto.ts +0 -55
  20. package/src/dto/get-challenge-data-response.dto.ts +0 -28
  21. package/src/dto/get-challenge-data.dto.ts +0 -69
  22. package/src/dto/get-client-info.dto.ts +0 -104
  23. package/src/dto/get-device-token-response.dto.ts +0 -25
  24. package/src/dto/get-events-by-type.dto.ts +0 -76
  25. package/src/dto/get-ip-address-response.dto.ts +0 -24
  26. package/src/dto/get-mfa-status.dto.ts +0 -94
  27. package/src/dto/get-risk-assessment-history.dto.ts +0 -39
  28. package/src/dto/get-session-id-response.dto.ts +0 -25
  29. package/src/dto/get-setup-data-response.dto.ts +0 -31
  30. package/src/dto/get-setup-data.dto.ts +0 -75
  31. package/src/dto/get-suspicious-activity.dto.ts +0 -42
  32. package/src/dto/get-user-agent-response.dto.ts +0 -23
  33. package/src/dto/get-user-auth-history.dto.ts +0 -95
  34. package/src/dto/get-user-by-email.dto.ts +0 -61
  35. package/src/dto/get-user-by-id.dto.ts +0 -46
  36. package/src/dto/get-user-devices.dto.ts +0 -53
  37. package/src/dto/get-user-response.dto.ts +0 -17
  38. package/src/dto/has-provider.dto.ts +0 -56
  39. package/src/dto/index.ts +0 -57
  40. package/src/dto/is-trusted-device-response.dto.ts +0 -34
  41. package/src/dto/list-providers-response.dto.ts +0 -23
  42. package/src/dto/login.dto.ts +0 -95
  43. package/src/dto/logout-all-response.dto.ts +0 -24
  44. package/src/dto/logout-all.dto.ts +0 -65
  45. package/src/dto/logout-response.dto.ts +0 -25
  46. package/src/dto/logout.dto.ts +0 -64
  47. package/src/dto/refresh-token.dto.ts +0 -36
  48. package/src/dto/remove-devices.dto.ts +0 -85
  49. package/src/dto/resend-code-response.dto.ts +0 -32
  50. package/src/dto/resend-code.dto.ts +0 -51
  51. package/src/dto/reset-password.dto.ts +0 -115
  52. package/src/dto/respond-challenge.dto.ts +0 -272
  53. package/src/dto/set-mfa-exemption.dto.ts +0 -112
  54. package/src/dto/set-must-change-password-response.dto.ts +0 -27
  55. package/src/dto/set-must-change-password.dto.ts +0 -46
  56. package/src/dto/set-preferred-method.dto.ts +0 -80
  57. package/src/dto/setup-mfa.dto.ts +0 -98
  58. package/src/dto/signup.dto.ts +0 -174
  59. package/src/dto/social-auth.dto.ts +0 -422
  60. package/src/dto/trust-device-response.dto.ts +0 -30
  61. package/src/dto/trust-device.dto.ts +0 -9
  62. package/src/dto/update-user-attributes-request.dto.ts +0 -51
  63. package/src/dto/user-response.dto.ts +0 -138
  64. package/src/dto/user-update.dto.ts +0 -222
  65. package/src/dto/verify-email.dto.ts +0 -313
  66. package/src/dto/verify-mfa-code.dto.ts +0 -103
  67. package/src/dto/verify-phone-by-sub.dto.ts +0 -78
  68. package/src/dto/verify-phone.dto.ts +0 -245
  69. package/src/entities/auth-audit.entity.ts +0 -232
  70. package/src/entities/challenge-session.entity.ts +0 -116
  71. package/src/entities/index.ts +0 -29
  72. package/src/entities/login-attempt.entity.ts +0 -64
  73. package/src/entities/mfa-device.entity.ts +0 -151
  74. package/src/entities/rate-limit.entity.ts +0 -44
  75. package/src/entities/session.entity.ts +0 -180
  76. package/src/entities/social-account.entity.ts +0 -96
  77. package/src/entities/storage-lock.entity.ts +0 -39
  78. package/src/entities/trusted-device.entity.ts +0 -112
  79. package/src/entities/user.entity.ts +0 -243
  80. package/src/entities/verification-token.entity.ts +0 -141
  81. package/src/enums/auth-audit-event-type.enum.ts +0 -360
  82. package/src/enums/error-codes.enum.ts +0 -420
  83. package/src/enums/mfa-method.enum.ts +0 -97
  84. package/src/enums/risk-factor.enum.ts +0 -111
  85. package/src/exceptions/nauth.exception.ts +0 -231
  86. package/src/handlers/auth.handler.ts +0 -260
  87. package/src/handlers/client-info.handler.ts +0 -101
  88. package/src/handlers/csrf.handler.ts +0 -156
  89. package/src/handlers/token-delivery.handler.ts +0 -118
  90. package/src/index.ts +0 -118
  91. package/src/interfaces/client-info.interface.ts +0 -85
  92. package/src/interfaces/config.interface.ts +0 -2135
  93. package/src/interfaces/entities.interface.ts +0 -226
  94. package/src/interfaces/index.ts +0 -15
  95. package/src/interfaces/logger.interface.ts +0 -283
  96. package/src/interfaces/mfa-provider.interface.ts +0 -154
  97. package/src/interfaces/oauth.interface.ts +0 -148
  98. package/src/interfaces/provider.interface.ts +0 -47
  99. package/src/interfaces/social-auth-provider.interface.ts +0 -131
  100. package/src/interfaces/storage-adapter.interface.ts +0 -82
  101. package/src/interfaces/template.interface.ts +0 -510
  102. package/src/interfaces/token-verifier.interface.ts +0 -110
  103. package/src/internal.ts +0 -178
  104. package/src/platform/interfaces.ts +0 -299
  105. package/src/schemas/auth-config.schema.ts +0 -646
  106. package/src/services/adaptive-mfa-decision.service.spec.ts +0 -1058
  107. package/src/services/adaptive-mfa-decision.service.ts +0 -457
  108. package/src/services/auth-audit.service.spec.ts +0 -675
  109. package/src/services/auth-audit.service.ts +0 -558
  110. package/src/services/auth-challenge-helper.service.spec.ts +0 -3227
  111. package/src/services/auth-challenge-helper.service.ts +0 -825
  112. package/src/services/auth-flow-context-builder.service.ts +0 -520
  113. package/src/services/auth-flow-rules.ts +0 -202
  114. package/src/services/auth-flow-state-definitions.ts +0 -190
  115. package/src/services/auth-flow-state-machine.service.ts +0 -207
  116. package/src/services/auth-flow-state-machine.types.ts +0 -316
  117. package/src/services/auth.service.spec.ts +0 -4195
  118. package/src/services/auth.service.ts +0 -3727
  119. package/src/services/challenge.service.spec.ts +0 -1363
  120. package/src/services/challenge.service.ts +0 -696
  121. package/src/services/client-info.service.spec.ts +0 -572
  122. package/src/services/client-info.service.ts +0 -374
  123. package/src/services/csrf.service.ts +0 -54
  124. package/src/services/email-verification.service.spec.ts +0 -1229
  125. package/src/services/email-verification.service.ts +0 -578
  126. package/src/services/geo-location.service.spec.ts +0 -603
  127. package/src/services/geo-location.service.ts +0 -599
  128. package/src/services/index.ts +0 -13
  129. package/src/services/jwt.service.spec.ts +0 -882
  130. package/src/services/jwt.service.ts +0 -621
  131. package/src/services/mfa-base.service.spec.ts +0 -246
  132. package/src/services/mfa-base.service.ts +0 -611
  133. package/src/services/mfa.service.spec.ts +0 -693
  134. package/src/services/mfa.service.ts +0 -960
  135. package/src/services/password.service.spec.ts +0 -166
  136. package/src/services/password.service.ts +0 -309
  137. package/src/services/phone-verification.service.spec.ts +0 -1120
  138. package/src/services/phone-verification.service.ts +0 -751
  139. package/src/services/risk-detection.service.spec.ts +0 -1292
  140. package/src/services/risk-detection.service.ts +0 -1012
  141. package/src/services/risk-scoring.service.spec.ts +0 -204
  142. package/src/services/risk-scoring.service.ts +0 -131
  143. package/src/services/session.service.spec.ts +0 -1293
  144. package/src/services/session.service.ts +0 -803
  145. package/src/services/social-account.service.spec.ts +0 -725
  146. package/src/services/social-auth-base.service.spec.ts +0 -418
  147. package/src/services/social-auth-base.service.ts +0 -581
  148. package/src/services/social-auth.service.spec.ts +0 -238
  149. package/src/services/social-auth.service.ts +0 -436
  150. package/src/services/social-provider-registry.service.spec.ts +0 -238
  151. package/src/services/social-provider-registry.service.ts +0 -122
  152. package/src/services/trusted-device.service.spec.ts +0 -505
  153. package/src/services/trusted-device.service.ts +0 -339
  154. package/src/storage/account-lockout-storage.service.spec.ts +0 -310
  155. package/src/storage/account-lockout-storage.service.ts +0 -89
  156. package/src/storage/index.ts +0 -3
  157. package/src/storage/memory-storage.adapter.ts +0 -443
  158. package/src/storage/rate-limit-storage.service.spec.ts +0 -247
  159. package/src/storage/rate-limit-storage.service.ts +0 -38
  160. package/src/templates/html-template.engine.spec.ts +0 -161
  161. package/src/templates/html-template.engine.ts +0 -688
  162. package/src/templates/index.ts +0 -7
  163. package/src/utils/common-passwords.spec.ts +0 -230
  164. package/src/utils/common-passwords.ts +0 -170
  165. package/src/utils/context-storage.ts +0 -188
  166. package/src/utils/cookie-names.util.ts +0 -67
  167. package/src/utils/cookies.util.ts +0 -94
  168. package/src/utils/index.ts +0 -12
  169. package/src/utils/ip-extractor.spec.ts +0 -330
  170. package/src/utils/ip-extractor.ts +0 -220
  171. package/src/utils/nauth-logger.spec.ts +0 -388
  172. package/src/utils/nauth-logger.ts +0 -215
  173. package/src/utils/pii-redactor.spec.ts +0 -130
  174. package/src/utils/pii-redactor.ts +0 -288
  175. package/src/utils/setup/get-repositories.ts +0 -140
  176. package/src/utils/setup/init-services.ts +0 -422
  177. package/src/utils/setup/init-social.ts +0 -189
  178. package/src/utils/setup/init-storage.ts +0 -94
  179. package/src/utils/setup/register-mfa.ts +0 -165
  180. package/src/utils/setup/run-nauth-migrations.ts +0 -61
  181. package/src/utils/token-delivery-policy.ts +0 -38
  182. package/src/validators/template.validator.ts +0 -219
  183. package/tsconfig.json +0 -37
  184. package/tsconfig.lint.json +0 -6
@@ -1,130 +0,0 @@
1
- import { PiiRedactor } from './pii-redactor';
2
-
3
- describe('PiiRedactor', () => {
4
- let redactor: PiiRedactor;
5
-
6
- beforeEach(() => {
7
- redactor = new PiiRedactor();
8
- });
9
-
10
- describe('redactMessage', () => {
11
- it('should redact email addresses', () => {
12
- const message = 'User user@example.com logged in';
13
- const redacted = redactor.redactMessage(message);
14
- expect(redacted).toBe('User u***@***.com logged in');
15
- });
16
-
17
- it('should redact IP addresses', () => {
18
- const message = 'Login from 192.168.1.100';
19
- const redacted = redactor.redactMessage(message);
20
- expect(redacted).toBe('Login from 192.168.1.***');
21
- });
22
-
23
- it('should redact JWT tokens', () => {
24
- const message = 'Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
25
- const redacted = redactor.redactMessage(message);
26
- expect(redacted).toBe('Token: [REDACTED_TOKEN]');
27
- });
28
-
29
- it('should redact passwords', () => {
30
- const message = 'password: MySecretPass123';
31
- const redacted = redactor.redactMessage(message);
32
- expect(redacted).toContain('[REDACTED]');
33
- });
34
-
35
- it('should redact Argon2 hashes', () => {
36
- const message =
37
- 'Hash: $argon2id$v=19$m=65536,t=3,p=4$NiHLP1CtwlkNQY105M660Q$o9JAC5CauGAYHIynTirdzAZGQtavL0osvxnYVkmskbo';
38
- const redacted = redactor.redactMessage(message);
39
- expect(redacted).toContain('[REDACTED_HASH]');
40
- expect(redacted).not.toContain('$argon2');
41
- });
42
-
43
- it('should redact phone numbers', () => {
44
- const message = 'Phone: +1234567890';
45
- const redacted = redactor.redactMessage(message);
46
- expect(redacted).toBe('Phone: +123***7890');
47
- });
48
-
49
- it('should redact names', () => {
50
- // Test with full names (two consecutive capitalized words)
51
- const message = 'Meeting with John Doe at 3pm';
52
- const redacted = redactor.redactMessage(message);
53
- // Redacts full names (two consecutive capitalized words)
54
- expect(redacted).toBe('Meeting with J*** D*** at 3pm');
55
- });
56
-
57
- it('should redact firstName and lastName fields in JSON', () => {
58
- const message = 'firstName: John, lastName: Doe';
59
- const redacted = redactor.redactMessage(message);
60
- expect(redacted).toBe('firstName=[REDACTED_NAME], lastName=[REDACTED_NAME]');
61
- });
62
-
63
- it('should handle multiple PII types in one message', () => {
64
- const message = 'User user@example.com from 192.168.1.100 with token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
65
- const redacted = redactor.redactMessage(message);
66
- expect(redacted).toContain('u***@***.com');
67
- expect(redacted).toContain('192.168.1.***');
68
- expect(redacted).toContain('[REDACTED_TOKEN]');
69
- });
70
- });
71
-
72
- describe('redactMetadata', () => {
73
- it('should redact IP address in metadata', () => {
74
- const metadata = {
75
- userId: '123',
76
- ipAddress: '192.168.1.100',
77
- };
78
- const redacted = redactor.redactMetadata(metadata);
79
- expect(redacted?.ipAddress).toBe('192.168.1.***');
80
- expect(redacted?.userId).toBe('123');
81
- });
82
-
83
- it('should redact custom fields', () => {
84
- redactor = new PiiRedactor({
85
- customRedactionFields: ['ssn', 'creditCard'],
86
- });
87
- const metadata = {
88
- userId: '123',
89
- ssn: '123-45-6789',
90
- creditCard: '4111-1111-1111-1111',
91
- };
92
- const redacted = redactor.redactMetadata(metadata);
93
- expect(redacted?.ssn).toBe('[REDACTED]');
94
- expect(redacted?.creditCard).toBe('[REDACTED]');
95
- expect(redacted?.userId).toBe('123');
96
- });
97
-
98
- it('should handle nested objects', () => {
99
- const metadata = {
100
- user: {
101
- email: 'user@example.com',
102
- password: 'secret123',
103
- },
104
- };
105
- const redacted = redactor.redactMetadata(metadata);
106
- expect(redacted?.user).toBeDefined();
107
- expect((redacted?.user as any).email).toContain('***');
108
- });
109
-
110
- it('should return undefined for undefined input', () => {
111
- const redacted = redactor.redactMetadata(undefined);
112
- expect(redacted).toBeUndefined();
113
- });
114
- });
115
-
116
- describe('disable redaction', () => {
117
- it('should not redact if redaction is disabled', () => {
118
- redactor = new PiiRedactor({
119
- redactEmails: false,
120
- redactIpAddresses: false,
121
- redactTokens: false,
122
- redactPasswords: false,
123
- redactPhoneNumbers: false,
124
- });
125
- const message = 'User user@example.com from 192.168.1.100';
126
- const redacted = redactor.redactMessage(message);
127
- expect(redacted).toBe(message); // Should be unchanged
128
- });
129
- });
130
- });
@@ -1,288 +0,0 @@
1
- import { LogMetadata, PiiRedactionOptions } from '../interfaces/logger.interface';
2
-
3
- /**
4
- * PII Redactor Utility
5
- *
6
- * Automatically redacts Personally Identifiable Information (PII) from log messages
7
- * and metadata to ensure privacy compliance (GDPR, CCPA, etc.).
8
- *
9
- * Redaction patterns:
10
- * - Emails: `user@example.com` → `u***@***.com`
11
- * - IP Addresses: `192.168.1.100` → `192.168.1.***`
12
- * - Tokens: `eyJhbGciOiJIUz...` → `[REDACTED_TOKEN]`
13
- * - Passwords: Always `[REDACTED]`
14
- * - Phone Numbers: `+1234567890` → `+123***7890`
15
- * - Names: `John Doe` → `J*** D***`
16
- *
17
- * @example
18
- * ```typescript
19
- * const redactor = new PiiRedactor();
20
- * const safe = redactor.redactMessage('User user@example.com logged in');
21
- * // Output: 'User u***@***.com logged in'
22
- * ```
23
- */
24
- export class PiiRedactor {
25
- private options: Required<PiiRedactionOptions>;
26
-
27
- /**
28
- * Constructor
29
- *
30
- * @param options - PII redaction configuration
31
- */
32
- constructor(options?: PiiRedactionOptions) {
33
- // Default options with all redactions enabled
34
- this.options = {
35
- redactEmails: true,
36
- redactIpAddresses: true,
37
- redactTokens: true,
38
- redactPasswords: true,
39
- redactPhoneNumbers: true,
40
- redactNames: true,
41
- customRedactionFields: ['ssn', 'creditCard', 'bankAccount'],
42
- ...options,
43
- };
44
- }
45
-
46
- /**
47
- * Redact PII from a log message
48
- *
49
- * @param message - Log message that may contain PII
50
- * @returns Redacted message
51
- */
52
- redactMessage(message: string): string {
53
- let redacted = message;
54
-
55
- // Redact emails
56
- if (this.options.redactEmails) {
57
- redacted = this.redactEmails(redacted);
58
- }
59
-
60
- // Redact IP addresses
61
- if (this.options.redactIpAddresses) {
62
- redacted = this.redactIpAddresses(redacted);
63
- }
64
-
65
- // Redact tokens (JWT, Bearer tokens)
66
- if (this.options.redactTokens) {
67
- redacted = this.redactTokens(redacted);
68
- }
69
-
70
- // Redact phone numbers
71
- if (this.options.redactPhoneNumbers) {
72
- redacted = this.redactPhoneNumbers(redacted);
73
- }
74
-
75
- // Redact names (firstName, lastName)
76
- if (this.options.redactNames) {
77
- redacted = this.redactNames(redacted);
78
- }
79
-
80
- // Redact passwords (always)
81
- if (this.options.redactPasswords) {
82
- redacted = this.redactPasswords(redacted);
83
- }
84
-
85
- return redacted;
86
- }
87
-
88
- /**
89
- * Redact PII from log metadata
90
- *
91
- * @param metadata - Log metadata that may contain PII
92
- * @returns Redacted metadata
93
- */
94
- redactMetadata(metadata?: LogMetadata): LogMetadata | undefined {
95
- if (!metadata) {
96
- return undefined;
97
- }
98
-
99
- const redacted: LogMetadata = { ...metadata };
100
-
101
- // Redact IP address (keep first 3 octets for geolocation)
102
- if (redacted.ipAddress && this.options.redactIpAddresses) {
103
- redacted.ipAddress = this.redactIpAddress(redacted.ipAddress);
104
- }
105
-
106
- // Redact custom fields
107
- for (const field of this.options.customRedactionFields) {
108
- if (field in redacted) {
109
- redacted[field] = '[REDACTED]';
110
- }
111
- }
112
-
113
- // Recursively redact object values
114
- for (const [key, value] of Object.entries(redacted)) {
115
- if (typeof value === 'string') {
116
- redacted[key] = this.redactMessage(value);
117
- } else if (typeof value === 'object' && value !== null && !(value instanceof Error)) {
118
- // Redact nested objects (but skip Error objects)
119
- redacted[key] = this.redactObject(value);
120
- }
121
- }
122
-
123
- return redacted;
124
- }
125
-
126
- /**
127
- * Redact email addresses
128
- * @private
129
- */
130
- private redactEmails(text: string): string {
131
- // Match email pattern: user@example.com
132
- return text.replace(
133
- /\b([a-zA-Z0-9])([a-zA-Z0-9._-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})\b/g,
134
- (_match, first, _local, _domain, tld) => {
135
- // Keep first char + *** + @ + *** + . + tld
136
- return `${first}***@***.${tld}`;
137
- },
138
- );
139
- }
140
-
141
- /**
142
- * Redact IP addresses (keep first 3 octets)
143
- * @private
144
- */
145
- private redactIpAddresses(text: string): string {
146
- // IPv4: 192.168.1.100 → 192.168.1.***
147
- let redacted = text.replace(/\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.)\d{1,3}\b/g, '$1***');
148
-
149
- // IPv6: Redact last 4 groups
150
- redacted = redacted.replace(/\b([0-9a-fA-F:]+):([0-9a-fA-F]+):([0-9a-fA-F]+):([0-9a-fA-F]+)\b/g, '$1:***:***:***');
151
-
152
- return redacted;
153
- }
154
-
155
- /**
156
- * Redact a single IP address
157
- * @private
158
- */
159
- private redactIpAddress(ip: string): string {
160
- // IPv4
161
- if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(ip)) {
162
- const parts = ip.split('.');
163
- return `${parts[0]}.${parts[1]}.${parts[2]}.***`;
164
- }
165
-
166
- // IPv6
167
- if (ip.includes(':')) {
168
- const parts = ip.split(':');
169
- return `${parts.slice(0, 4).join(':')}:***:***:***`;
170
- }
171
-
172
- return ip;
173
- }
174
-
175
- /**
176
- * Redact JWT tokens and bearer tokens
177
- * @private
178
- */
179
- private redactTokens(text: string): string {
180
- // JWT tokens (eyJ...)
181
- let redacted = text.replace(/eyJ[A-Za-z0-9_-]{10,}/g, '[REDACTED_TOKEN]');
182
-
183
- // Bearer tokens
184
- redacted = redacted.replace(/Bearer\s+[A-Za-z0-9_-]{20,}/gi, 'Bearer [REDACTED_TOKEN]');
185
-
186
- // Generic long alphanumeric tokens (40+ chars)
187
- redacted = redacted.replace(/\b[A-Za-z0-9]{40,}\b/g, '[REDACTED_TOKEN]');
188
-
189
- return redacted;
190
- }
191
-
192
- /**
193
- * Redact phone numbers
194
- * @private
195
- */
196
- private redactPhoneNumbers(text: string): string {
197
- // E.164 format: +1234567890 → +123***7890
198
- return text.replace(/\+?(\d{1,3})(\d{3,})(\d{4})\b/g, (_match, country, _middle, last) => {
199
- return `+${country}***${last}`;
200
- });
201
- }
202
-
203
- /**
204
- * Redact names (firstName, lastName)
205
- * @private
206
- */
207
- private redactNames(text: string): string {
208
- // Redact specific field patterns in JSON/logs
209
- let redacted = text.replace(
210
- /(firstName|lastName|first_name|last_name)["\s:=]+([^"'\s,}&]+)/gi,
211
- '$1=[REDACTED_NAME]',
212
- );
213
-
214
- // Redact full names (pattern: "FirstName LastName" where both words are capitalized)
215
- // Only match when there are two consecutive capitalized words (likely a full name)
216
- // Exclude common technical words that shouldn't be redacted
217
- const commonWords =
218
- /^(User|Login|Token|Phone|Email|Admin|System|Service|Client|Server|Request|Response|Success|Error|Warning|Info|Debug|Welcome|Hello|Account|Profile|Session|Device)$/i;
219
- redacted = redacted.replace(/\b([A-Z][a-z]{2,})\s+([A-Z][a-z]{2,})\b/g, (match, first, last) => {
220
- // Don't redact if first word is a common technical term
221
- if (commonWords.test(first)) {
222
- return match;
223
- }
224
- return `${first.charAt(0)}*** ${last.charAt(0)}***`;
225
- });
226
-
227
- return redacted;
228
- }
229
-
230
- /**
231
- * Redact passwords and password-related fields
232
- * @private
233
- */
234
- private redactPasswords(text: string): string {
235
- // Redact common password patterns in JSON or query params
236
- let redacted = text.replace(/(password|pwd|passwd|secret)["\s:=]+([^"'\s,}&]+)/gi, '$1=[REDACTED]');
237
-
238
- // Redact Argon2 hashes
239
- redacted = redacted.replace(/\$argon2[^\s"',}]+/g, '[REDACTED_HASH]');
240
-
241
- // Redact bcrypt hashes
242
- redacted = redacted.replace(/\$2[aby]\$\d+\$[./A-Za-z0-9]{53}/g, '[REDACTED_HASH]');
243
-
244
- return redacted;
245
- }
246
-
247
- /**
248
- * Recursively redact an object
249
- * @private
250
- */
251
- private redactObject(obj: unknown, visited = new WeakSet<object>()): unknown {
252
- if (typeof obj !== 'object' || obj === null) {
253
- return obj;
254
- }
255
-
256
- // Circular reference detection
257
- if (visited.has(obj as object)) {
258
- return '[Circular Reference]';
259
- }
260
- visited.add(obj as object);
261
-
262
- try {
263
- if (Array.isArray(obj)) {
264
- return obj.map((item) => this.redactObject(item, visited));
265
- }
266
-
267
- const redacted: Record<string, unknown> = {};
268
-
269
- for (const [key, value] of Object.entries(obj)) {
270
- // Check if key matches custom redaction fields
271
- if (this.options.customRedactionFields.includes(key.toLowerCase())) {
272
- redacted[key] = '[REDACTED]';
273
- } else if (typeof value === 'string') {
274
- redacted[key] = this.redactMessage(value);
275
- } else if (typeof value === 'object' && value !== null) {
276
- redacted[key] = this.redactObject(value, visited);
277
- } else {
278
- redacted[key] = value;
279
- }
280
- }
281
-
282
- return redacted;
283
- } catch {
284
- // If there's an error (e.g., can't stringify), return safe fallback
285
- return '[Object with circular references]';
286
- }
287
- }
288
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * Repository Discovery Helper
3
- *
4
- * Discovers TypeORM repositories from DataSource metadata.
5
- * Supports both explicit entity names and table name matching.
6
- */
7
-
8
- import { DataSource, EntityMetadata, Repository, ObjectLiteral } from 'typeorm';
9
- import { NAuthException, AuthErrorCode } from '../../index';
10
- import {
11
- BaseUser,
12
- BaseSession,
13
- BaseLoginAttempt,
14
- BaseVerificationToken,
15
- BaseSocialAccount,
16
- BaseChallengeSession,
17
- BaseMFADevice,
18
- BaseAuthAudit,
19
- BaseTrustedDevice,
20
- BaseRateLimit,
21
- BaseStorageLock,
22
- } from '../../entities';
23
-
24
- /**
25
- * Get all required repositories from DataSource
26
- *
27
- * Auto-discovers entities by table name from DataSource metadata.
28
- * Falls back to entity name if table not found.
29
- *
30
- * @param dataSource - TypeORM DataSource
31
- * @returns Object containing all required repositories
32
- * @throws {NAuthException} If required entity not found
33
- */
34
- export function getRepositories(dataSource: DataSource): {
35
- userRepository: Repository<BaseUser>;
36
- sessionRepository: Repository<BaseSession>;
37
- loginAttemptRepository: Repository<BaseLoginAttempt>;
38
- verificationTokenRepository: Repository<BaseVerificationToken>;
39
- socialAccountRepository: Repository<BaseSocialAccount>;
40
- challengeSessionRepository: Repository<BaseChallengeSession>;
41
- mfaDeviceRepository: Repository<BaseMFADevice>;
42
- authAuditRepository: Repository<BaseAuthAudit>;
43
- trustedDeviceRepository: Repository<BaseTrustedDevice> | null;
44
- rateLimitRepository: Repository<BaseRateLimit> | null;
45
- storageLockRepository: Repository<BaseStorageLock> | null;
46
- } {
47
- return {
48
- userRepository: getRepository<BaseUser>(dataSource, 'User', 'nauth_users', true),
49
- sessionRepository: getRepository<BaseSession>(dataSource, 'Session', 'nauth_sessions', true),
50
- loginAttemptRepository: getRepository<BaseLoginAttempt>(dataSource, 'LoginAttempt', 'nauth_login_attempts', true),
51
- verificationTokenRepository: getRepository<BaseVerificationToken>(
52
- dataSource,
53
- 'VerificationToken',
54
- 'nauth_verification_tokens',
55
- true,
56
- ),
57
- socialAccountRepository: getRepository<BaseSocialAccount>(
58
- dataSource,
59
- 'SocialAccount',
60
- 'nauth_social_accounts',
61
- true,
62
- ),
63
- challengeSessionRepository: getRepository<BaseChallengeSession>(
64
- dataSource,
65
- 'ChallengeSession',
66
- 'nauth_challenge_sessions',
67
- true,
68
- ),
69
- mfaDeviceRepository: getRepository<BaseMFADevice>(dataSource, 'MFADevice', 'nauth_mfa_devices', true),
70
- authAuditRepository: getRepository<BaseAuthAudit>(dataSource, 'AuthAudit', 'nauth_auth_audit', true),
71
- // Optional repositories (may not exist if features disabled)
72
- trustedDeviceRepository: getRepository<BaseTrustedDevice>(
73
- dataSource,
74
- 'TrustedDevice',
75
- 'nauth_trusted_devices',
76
- false,
77
- ),
78
- rateLimitRepository: getRepository<BaseRateLimit>(dataSource, 'RateLimit', 'nauth_rate_limits', false),
79
- storageLockRepository: getRepository<BaseStorageLock>(dataSource, 'StorageLock', 'nauth_storage_locks', false),
80
- };
81
- }
82
-
83
- /**
84
- * Get a single repository by entity name or table name
85
- *
86
- * @param dataSource - TypeORM DataSource
87
- * @param entityName - Entity class name (e.g., 'User')
88
- * @param tableName - Table name (e.g., 'nauth_users')
89
- * @param required - Whether entity is required (throw if not found)
90
- * @returns Repository or null if not found and not required
91
- * @throws {NAuthException} If required entity not found
92
- */
93
- /* eslint-disable no-redeclare */
94
- function getRepository<T extends ObjectLiteral>(
95
- dataSource: DataSource,
96
- entityName: string,
97
- tableName: string,
98
- required: true,
99
- ): Repository<T>;
100
- function getRepository<T extends ObjectLiteral>(
101
- dataSource: DataSource,
102
- entityName: string,
103
- tableName: string,
104
- required: false,
105
- ): Repository<T> | null;
106
- function getRepository<T extends ObjectLiteral>(
107
- dataSource: DataSource,
108
- entityName: string,
109
- tableName: string,
110
- required: boolean,
111
- ): Repository<T> | null {
112
- // Try to find by table name first (more reliable)
113
- const metadataByTable = dataSource.entityMetadatas.find((m: EntityMetadata) => m.tableName === tableName);
114
-
115
- if (metadataByTable) {
116
- return dataSource.getRepository<T>(metadataByTable.target);
117
- }
118
-
119
- // Fallback: Try to find by entity class name
120
- const metadataByName = dataSource.entityMetadatas.find(
121
- (m: EntityMetadata) => typeof m.target === 'function' && m.target.name === entityName,
122
- );
123
-
124
- if (metadataByName && typeof metadataByName.target === 'function') {
125
- return dataSource.getRepository<T>(metadataByName.target);
126
- }
127
-
128
- // Not found
129
- if (required) {
130
- throw new NAuthException(
131
- AuthErrorCode.VALIDATION_FAILED,
132
- `${entityName} entity not found in DataSource. ` +
133
- `Make sure entities are registered in DataSource configuration. ` +
134
- `Expected table name: ${tableName}`,
135
- );
136
- }
137
-
138
- return null;
139
- }
140
- /* eslint-enable no-redeclare */