@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.
- package/LICENSE +90 -0
- package/README.md +30 -0
- package/package.json +7 -2
- package/jest.config.js +0 -15
- package/jest.setup.ts +0 -6
- package/src/adapters/database-columns.ts +0 -165
- package/src/adapters/express.adapter.ts +0 -385
- package/src/adapters/fastify.adapter.ts +0 -416
- package/src/adapters/index.ts +0 -16
- package/src/adapters/storage.factory.ts +0 -143
- package/src/bootstrap.ts +0 -374
- package/src/dto/auth-challenge.dto.ts +0 -231
- package/src/dto/auth-response.dto.ts +0 -253
- package/src/dto/challenge-response.dto.ts +0 -234
- package/src/dto/change-password-request.dto.ts +0 -50
- package/src/dto/change-password-response.dto.ts +0 -29
- package/src/dto/change-password.dto.ts +0 -57
- package/src/dto/error-response.dto.ts +0 -136
- package/src/dto/get-available-methods.dto.ts +0 -55
- package/src/dto/get-challenge-data-response.dto.ts +0 -28
- package/src/dto/get-challenge-data.dto.ts +0 -69
- package/src/dto/get-client-info.dto.ts +0 -104
- package/src/dto/get-device-token-response.dto.ts +0 -25
- package/src/dto/get-events-by-type.dto.ts +0 -76
- package/src/dto/get-ip-address-response.dto.ts +0 -24
- package/src/dto/get-mfa-status.dto.ts +0 -94
- package/src/dto/get-risk-assessment-history.dto.ts +0 -39
- package/src/dto/get-session-id-response.dto.ts +0 -25
- package/src/dto/get-setup-data-response.dto.ts +0 -31
- package/src/dto/get-setup-data.dto.ts +0 -75
- package/src/dto/get-suspicious-activity.dto.ts +0 -42
- package/src/dto/get-user-agent-response.dto.ts +0 -23
- package/src/dto/get-user-auth-history.dto.ts +0 -95
- package/src/dto/get-user-by-email.dto.ts +0 -61
- package/src/dto/get-user-by-id.dto.ts +0 -46
- package/src/dto/get-user-devices.dto.ts +0 -53
- package/src/dto/get-user-response.dto.ts +0 -17
- package/src/dto/has-provider.dto.ts +0 -56
- package/src/dto/index.ts +0 -57
- package/src/dto/is-trusted-device-response.dto.ts +0 -34
- package/src/dto/list-providers-response.dto.ts +0 -23
- package/src/dto/login.dto.ts +0 -95
- package/src/dto/logout-all-response.dto.ts +0 -24
- package/src/dto/logout-all.dto.ts +0 -65
- package/src/dto/logout-response.dto.ts +0 -25
- package/src/dto/logout.dto.ts +0 -64
- package/src/dto/refresh-token.dto.ts +0 -36
- package/src/dto/remove-devices.dto.ts +0 -85
- package/src/dto/resend-code-response.dto.ts +0 -32
- package/src/dto/resend-code.dto.ts +0 -51
- package/src/dto/reset-password.dto.ts +0 -115
- package/src/dto/respond-challenge.dto.ts +0 -272
- package/src/dto/set-mfa-exemption.dto.ts +0 -112
- package/src/dto/set-must-change-password-response.dto.ts +0 -27
- package/src/dto/set-must-change-password.dto.ts +0 -46
- package/src/dto/set-preferred-method.dto.ts +0 -80
- package/src/dto/setup-mfa.dto.ts +0 -98
- package/src/dto/signup.dto.ts +0 -174
- package/src/dto/social-auth.dto.ts +0 -422
- package/src/dto/trust-device-response.dto.ts +0 -30
- package/src/dto/trust-device.dto.ts +0 -9
- package/src/dto/update-user-attributes-request.dto.ts +0 -51
- package/src/dto/user-response.dto.ts +0 -138
- package/src/dto/user-update.dto.ts +0 -222
- package/src/dto/verify-email.dto.ts +0 -313
- package/src/dto/verify-mfa-code.dto.ts +0 -103
- package/src/dto/verify-phone-by-sub.dto.ts +0 -78
- package/src/dto/verify-phone.dto.ts +0 -245
- package/src/entities/auth-audit.entity.ts +0 -232
- package/src/entities/challenge-session.entity.ts +0 -116
- package/src/entities/index.ts +0 -29
- package/src/entities/login-attempt.entity.ts +0 -64
- package/src/entities/mfa-device.entity.ts +0 -151
- package/src/entities/rate-limit.entity.ts +0 -44
- package/src/entities/session.entity.ts +0 -180
- package/src/entities/social-account.entity.ts +0 -96
- package/src/entities/storage-lock.entity.ts +0 -39
- package/src/entities/trusted-device.entity.ts +0 -112
- package/src/entities/user.entity.ts +0 -243
- package/src/entities/verification-token.entity.ts +0 -141
- package/src/enums/auth-audit-event-type.enum.ts +0 -360
- package/src/enums/error-codes.enum.ts +0 -420
- package/src/enums/mfa-method.enum.ts +0 -97
- package/src/enums/risk-factor.enum.ts +0 -111
- package/src/exceptions/nauth.exception.ts +0 -231
- package/src/handlers/auth.handler.ts +0 -260
- package/src/handlers/client-info.handler.ts +0 -101
- package/src/handlers/csrf.handler.ts +0 -156
- package/src/handlers/token-delivery.handler.ts +0 -118
- package/src/index.ts +0 -118
- package/src/interfaces/client-info.interface.ts +0 -85
- package/src/interfaces/config.interface.ts +0 -2135
- package/src/interfaces/entities.interface.ts +0 -226
- package/src/interfaces/index.ts +0 -15
- package/src/interfaces/logger.interface.ts +0 -283
- package/src/interfaces/mfa-provider.interface.ts +0 -154
- package/src/interfaces/oauth.interface.ts +0 -148
- package/src/interfaces/provider.interface.ts +0 -47
- package/src/interfaces/social-auth-provider.interface.ts +0 -131
- package/src/interfaces/storage-adapter.interface.ts +0 -82
- package/src/interfaces/template.interface.ts +0 -510
- package/src/interfaces/token-verifier.interface.ts +0 -110
- package/src/internal.ts +0 -178
- package/src/platform/interfaces.ts +0 -299
- package/src/schemas/auth-config.schema.ts +0 -646
- package/src/services/adaptive-mfa-decision.service.spec.ts +0 -1058
- package/src/services/adaptive-mfa-decision.service.ts +0 -457
- package/src/services/auth-audit.service.spec.ts +0 -675
- package/src/services/auth-audit.service.ts +0 -558
- package/src/services/auth-challenge-helper.service.spec.ts +0 -3227
- package/src/services/auth-challenge-helper.service.ts +0 -825
- package/src/services/auth-flow-context-builder.service.ts +0 -520
- package/src/services/auth-flow-rules.ts +0 -202
- package/src/services/auth-flow-state-definitions.ts +0 -190
- package/src/services/auth-flow-state-machine.service.ts +0 -207
- package/src/services/auth-flow-state-machine.types.ts +0 -316
- package/src/services/auth.service.spec.ts +0 -4195
- package/src/services/auth.service.ts +0 -3727
- package/src/services/challenge.service.spec.ts +0 -1363
- package/src/services/challenge.service.ts +0 -696
- package/src/services/client-info.service.spec.ts +0 -572
- package/src/services/client-info.service.ts +0 -374
- package/src/services/csrf.service.ts +0 -54
- package/src/services/email-verification.service.spec.ts +0 -1229
- package/src/services/email-verification.service.ts +0 -578
- package/src/services/geo-location.service.spec.ts +0 -603
- package/src/services/geo-location.service.ts +0 -599
- package/src/services/index.ts +0 -13
- package/src/services/jwt.service.spec.ts +0 -882
- package/src/services/jwt.service.ts +0 -621
- package/src/services/mfa-base.service.spec.ts +0 -246
- package/src/services/mfa-base.service.ts +0 -611
- package/src/services/mfa.service.spec.ts +0 -693
- package/src/services/mfa.service.ts +0 -960
- package/src/services/password.service.spec.ts +0 -166
- package/src/services/password.service.ts +0 -309
- package/src/services/phone-verification.service.spec.ts +0 -1120
- package/src/services/phone-verification.service.ts +0 -751
- package/src/services/risk-detection.service.spec.ts +0 -1292
- package/src/services/risk-detection.service.ts +0 -1012
- package/src/services/risk-scoring.service.spec.ts +0 -204
- package/src/services/risk-scoring.service.ts +0 -131
- package/src/services/session.service.spec.ts +0 -1293
- package/src/services/session.service.ts +0 -803
- package/src/services/social-account.service.spec.ts +0 -725
- package/src/services/social-auth-base.service.spec.ts +0 -418
- package/src/services/social-auth-base.service.ts +0 -581
- package/src/services/social-auth.service.spec.ts +0 -238
- package/src/services/social-auth.service.ts +0 -436
- package/src/services/social-provider-registry.service.spec.ts +0 -238
- package/src/services/social-provider-registry.service.ts +0 -122
- package/src/services/trusted-device.service.spec.ts +0 -505
- package/src/services/trusted-device.service.ts +0 -339
- package/src/storage/account-lockout-storage.service.spec.ts +0 -310
- package/src/storage/account-lockout-storage.service.ts +0 -89
- package/src/storage/index.ts +0 -3
- package/src/storage/memory-storage.adapter.ts +0 -443
- package/src/storage/rate-limit-storage.service.spec.ts +0 -247
- package/src/storage/rate-limit-storage.service.ts +0 -38
- package/src/templates/html-template.engine.spec.ts +0 -161
- package/src/templates/html-template.engine.ts +0 -688
- package/src/templates/index.ts +0 -7
- package/src/utils/common-passwords.spec.ts +0 -230
- package/src/utils/common-passwords.ts +0 -170
- package/src/utils/context-storage.ts +0 -188
- package/src/utils/cookie-names.util.ts +0 -67
- package/src/utils/cookies.util.ts +0 -94
- package/src/utils/index.ts +0 -12
- package/src/utils/ip-extractor.spec.ts +0 -330
- package/src/utils/ip-extractor.ts +0 -220
- package/src/utils/nauth-logger.spec.ts +0 -388
- package/src/utils/nauth-logger.ts +0 -215
- package/src/utils/pii-redactor.spec.ts +0 -130
- package/src/utils/pii-redactor.ts +0 -288
- package/src/utils/setup/get-repositories.ts +0 -140
- package/src/utils/setup/init-services.ts +0 -422
- package/src/utils/setup/init-social.ts +0 -189
- package/src/utils/setup/init-storage.ts +0 -94
- package/src/utils/setup/register-mfa.ts +0 -165
- package/src/utils/setup/run-nauth-migrations.ts +0 -61
- package/src/utils/token-delivery-policy.ts +0 -38
- package/src/validators/template.validator.ts +0 -219
- package/tsconfig.json +0 -37
- 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 */
|