@vynelix/nestjs-multi-auth 0.3.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +309 -18
- package/dist/auth/auth.controller.d.ts +3 -2
- package/dist/auth/auth.controller.js +32 -5
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.module.js +116 -68
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +52 -25
- package/dist/auth/auth.service.js +238 -327
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/core/auth-mapper.d.ts +1 -1
- package/dist/auth/core/database-otp.provider.d.ts +12 -0
- package/dist/auth/core/database-otp.provider.js +122 -0
- package/dist/auth/core/database-otp.provider.js.map +1 -0
- package/dist/auth/dto/requests/magic-link.dto.d.ts +1 -0
- package/dist/auth/dto/requests/magic-link.dto.js +6 -0
- package/dist/auth/dto/requests/magic-link.dto.js.map +1 -1
- package/dist/auth/dto/requests/mfa.dto.d.ts +5 -1
- package/dist/auth/dto/requests/mfa.dto.js +29 -8
- package/dist/auth/dto/requests/mfa.dto.js.map +1 -1
- package/dist/auth/enums/identifier-type.enum.d.ts +11 -0
- package/dist/auth/enums/identifier-type.enum.js +17 -0
- package/dist/auth/enums/identifier-type.enum.js.map +1 -0
- package/dist/auth/enums/mfa-type.enum.d.ts +5 -0
- package/dist/auth/enums/mfa-type.enum.js +10 -0
- package/dist/auth/enums/mfa-type.enum.js.map +1 -0
- package/dist/auth/enums/otp-purpose.enum.d.ts +8 -0
- package/dist/auth/enums/otp-purpose.enum.js +13 -0
- package/dist/auth/enums/otp-purpose.enum.js.map +1 -0
- package/dist/auth/enums/session-event.enum.d.ts +7 -0
- package/dist/auth/enums/session-event.enum.js +12 -0
- package/dist/auth/enums/session-event.enum.js.map +1 -0
- package/dist/auth/interfaces/auth-module-options.interface.d.ts +36 -1
- package/dist/auth/interfaces/auth-notification-provider.interface.d.ts +7 -1
- package/dist/auth/interfaces/auth-otp-provider.interface.d.ts +60 -0
- package/dist/auth/interfaces/auth-otp-provider.interface.js +7 -0
- package/dist/auth/interfaces/auth-otp-provider.interface.js.map +1 -0
- package/dist/auth/interfaces/models.interface.d.ts +91 -0
- package/dist/auth/interfaces/models.interface.js +3 -0
- package/dist/auth/interfaces/models.interface.js.map +1 -0
- package/dist/auth/interfaces/oauth-strategy.interface.d.ts +2 -2
- package/dist/auth/interfaces/repositories.interface.d.ts +70 -0
- package/dist/auth/interfaces/repositories.interface.js +3 -0
- package/dist/auth/interfaces/repositories.interface.js.map +1 -0
- package/dist/auth/interfaces/repository-tokens.d.ts +7 -0
- package/dist/auth/interfaces/repository-tokens.js +11 -0
- package/dist/auth/interfaces/repository-tokens.js.map +1 -0
- package/dist/auth/interfaces/signup-event-body.interface.d.ts +1 -1
- package/dist/auth/strategies/local-auth.strategy.d.ts +3 -5
- package/dist/auth/strategies/local-auth.strategy.js +64 -99
- package/dist/auth/strategies/local-auth.strategy.js.map +1 -1
- package/dist/auth/strategies/oauth/apple.strategy.d.ts +4 -6
- package/dist/auth/strategies/oauth/apple.strategy.js +57 -70
- package/dist/auth/strategies/oauth/apple.strategy.js.map +1 -1
- package/dist/auth/strategies/oauth/facebook.strategy.d.ts +4 -6
- package/dist/auth/strategies/oauth/facebook.strategy.js +58 -71
- package/dist/auth/strategies/oauth/facebook.strategy.js.map +1 -1
- package/dist/auth/strategies/oauth/google.strategy.d.ts +4 -6
- package/dist/auth/strategies/oauth/google.strategy.js +84 -155
- package/dist/auth/strategies/oauth/google.strategy.js.map +1 -1
- package/dist/auth/strategies/oauth/oauth.strategy.d.ts +2 -2
- package/dist/database/prisma/prisma-auth.adapter.d.ts +11 -0
- package/dist/database/prisma/prisma-auth.adapter.js +64 -0
- package/dist/database/prisma/prisma-auth.adapter.js.map +1 -0
- package/dist/database/prisma/repositories/auth-identifier.repository.d.ts +16 -0
- package/dist/database/prisma/repositories/auth-identifier.repository.js +41 -0
- package/dist/database/prisma/repositories/auth-identifier.repository.js.map +1 -0
- package/dist/database/prisma/repositories/auth.repository.d.ts +19 -0
- package/dist/database/prisma/repositories/auth.repository.js +49 -0
- package/dist/database/prisma/repositories/auth.repository.js.map +1 -0
- package/dist/database/prisma/repositories/index.d.ts +7 -0
- package/dist/database/prisma/repositories/index.js +24 -0
- package/dist/database/prisma/repositories/index.js.map +1 -0
- package/dist/database/prisma/repositories/mfa-method.repository.d.ts +11 -0
- package/dist/database/prisma/repositories/mfa-method.repository.js +33 -0
- package/dist/database/prisma/repositories/mfa-method.repository.js.map +1 -0
- package/dist/database/prisma/repositories/oauth-provider.repository.d.ts +14 -0
- package/dist/database/prisma/repositories/oauth-provider.repository.js +39 -0
- package/dist/database/prisma/repositories/oauth-provider.repository.js.map +1 -0
- package/dist/database/prisma/repositories/otp-token.repository.d.ts +13 -0
- package/dist/database/prisma/repositories/otp-token.repository.js +52 -0
- package/dist/database/prisma/repositories/otp-token.repository.js.map +1 -0
- package/dist/database/prisma/repositories/session-log.repository.d.ts +12 -0
- package/dist/database/prisma/repositories/session-log.repository.js +42 -0
- package/dist/database/prisma/repositories/session-log.repository.js.map +1 -0
- package/dist/database/prisma/repositories/session.repository.d.ts +16 -0
- package/dist/database/prisma/repositories/session.repository.js +41 -0
- package/dist/database/prisma/repositories/session.repository.js.map +1 -0
- package/dist/database/typeorm/entities/auth-identify.entity.d.ts +23 -0
- package/dist/database/typeorm/entities/auth-identify.entity.js +61 -0
- package/dist/database/typeorm/entities/auth-identify.entity.js.map +1 -0
- package/dist/database/typeorm/entities/auth.entity.d.ts +45 -0
- package/dist/database/typeorm/entities/auth.entity.js +129 -0
- package/dist/database/typeorm/entities/auth.entity.js.map +1 -0
- package/dist/database/typeorm/entities/base.entity.d.ts +7 -0
- package/dist/database/typeorm/entities/base.entity.js +44 -0
- package/dist/database/typeorm/entities/base.entity.js.map +1 -0
- package/dist/database/typeorm/entities/entities.d.ts +7 -0
- package/dist/database/typeorm/entities/entities.js +18 -0
- package/dist/database/typeorm/entities/entities.js.map +1 -0
- package/dist/database/typeorm/entities/mfa-method.entity.d.ts +10 -0
- package/dist/database/typeorm/entities/mfa-method.entity.js +47 -0
- package/dist/database/typeorm/entities/mfa-method.entity.js.map +1 -0
- package/dist/database/typeorm/entities/oauth-provider.entity.d.ts +38 -0
- package/dist/database/typeorm/entities/oauth-provider.entity.js +88 -0
- package/dist/database/typeorm/entities/oauth-provider.entity.js.map +1 -0
- package/dist/database/typeorm/entities/otp-token.entity.d.ts +15 -0
- package/dist/database/typeorm/entities/otp-token.entity.js +53 -0
- package/dist/database/typeorm/entities/otp-token.entity.js.map +1 -0
- package/dist/database/typeorm/entities/session.entity.d.ts +21 -0
- package/dist/database/typeorm/entities/session.entity.js +71 -0
- package/dist/database/typeorm/entities/session.entity.js.map +1 -0
- package/dist/database/typeorm/entities/session_log.entity.d.ts +25 -0
- package/dist/database/typeorm/entities/session_log.entity.js +78 -0
- package/dist/database/typeorm/entities/session_log.entity.js.map +1 -0
- package/dist/database/typeorm/migrations/auth-entities.d.ts +7 -0
- package/dist/database/typeorm/migrations/auth-entities.js +18 -0
- package/dist/database/typeorm/migrations/auth-entities.js.map +1 -0
- package/dist/database/typeorm/migrations/auth-schema.initializer.d.ts +9 -0
- package/dist/database/typeorm/migrations/auth-schema.initializer.js +36 -0
- package/dist/database/typeorm/migrations/auth-schema.initializer.js.map +1 -0
- package/dist/database/typeorm/migrations/auth.migration.v1.d.ts +12 -0
- package/dist/database/typeorm/migrations/auth.migration.v1.js +157 -0
- package/dist/database/typeorm/migrations/auth.migration.v1.js.map +1 -0
- package/dist/database/typeorm/migrations/auth.migration.v2.d.ts +8 -0
- package/dist/database/typeorm/migrations/auth.migration.v2.js +23 -0
- package/dist/database/typeorm/migrations/auth.migration.v2.js.map +1 -0
- package/dist/database/typeorm/migrations/auth.migrations.d.ts +2 -0
- package/dist/database/typeorm/migrations/auth.migrations.js +10 -0
- package/dist/database/typeorm/migrations/auth.migrations.js.map +1 -0
- package/dist/database/typeorm/migrations/migration.service.d.ts +9 -0
- package/dist/database/typeorm/migrations/migration.service.js +75 -0
- package/dist/database/typeorm/migrations/migration.service.js.map +1 -0
- package/dist/database/typeorm/repositories/auth-identifier.repository.d.ts +18 -0
- package/dist/database/typeorm/repositories/auth-identifier.repository.js +64 -0
- package/dist/database/typeorm/repositories/auth-identifier.repository.js.map +1 -0
- package/dist/database/typeorm/repositories/auth.repository.d.ts +21 -0
- package/dist/database/typeorm/repositories/auth.repository.js +71 -0
- package/dist/database/typeorm/repositories/auth.repository.js.map +1 -0
- package/dist/database/typeorm/repositories/index.d.ts +7 -0
- package/dist/database/typeorm/repositories/index.js +24 -0
- package/dist/database/typeorm/repositories/index.js.map +1 -0
- package/dist/database/typeorm/repositories/mfa-method.repository.d.ts +13 -0
- package/dist/database/typeorm/repositories/mfa-method.repository.js +46 -0
- package/dist/database/typeorm/repositories/mfa-method.repository.js.map +1 -0
- package/dist/database/typeorm/repositories/oauth-provider.repository.d.ts +16 -0
- package/dist/database/typeorm/repositories/oauth-provider.repository.js +52 -0
- package/dist/database/typeorm/repositories/oauth-provider.repository.js.map +1 -0
- package/dist/database/typeorm/repositories/otp-token.repository.d.ts +13 -0
- package/dist/database/typeorm/repositories/otp-token.repository.js +52 -0
- package/dist/database/typeorm/repositories/otp-token.repository.js.map +1 -0
- package/dist/database/typeorm/repositories/session-log.repository.d.ts +12 -0
- package/dist/database/typeorm/repositories/session-log.repository.js +47 -0
- package/dist/database/typeorm/repositories/session-log.repository.js.map +1 -0
- package/dist/database/typeorm/repositories/session.repository.d.ts +18 -0
- package/dist/database/typeorm/repositories/session.repository.js +84 -0
- package/dist/database/typeorm/repositories/session.repository.js.map +1 -0
- package/dist/database/typeorm/typeorm-auth.adapter.d.ts +2 -0
- package/dist/database/typeorm/typeorm-auth.adapter.js +50 -0
- package/dist/database/typeorm/typeorm-auth.adapter.js.map +1 -0
- package/dist/index.d.ts +11 -9
- package/dist/index.js +18 -10
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/fix-imports.js +46 -0
- package/fix-syntax.js +66 -0
- package/package.json +22 -20
- package/patch-local-specs.js +52 -0
- package/patch-oauth-specs.js +41 -0
- package/patch-specs.js +36 -0
- package/prompts/database_adapter.md +157 -0
- package/prompts/otp_provider.md +292 -0
- package/scratch/create-prisma-repos-final.js +139 -0
- package/scratch/create-prisma-repos.js +280 -0
- package/scratch/fix-events-spec.js +12 -0
- package/scratch/fix-imports.js +53 -0
- package/scratch/fix-prisma-interfaces.js +41 -0
- package/scratch/fix-prisma-interfaces2.js +36 -0
- package/scratch/fix-prisma-interfaces3.js +26 -0
- package/scratch/fix-prisma.js +28 -0
- package/scratch/fix-test-imports.js +51 -0
- package/scratch/fix-tests-advanced.js +68 -0
- package/scratch/fix-tests-advanced2.js +23 -0
- package/scratch/fix-tests-concrete.js +40 -0
- package/scratch/fix-tests-final.js +35 -0
- package/scratch/fix-tests-final2.js +28 -0
- package/scratch/fix-tests-final3.js +26 -0
- package/scratch/fix-tests-methods.js +40 -0
- package/scratch/fix-tests-proxy.js +67 -0
- package/scratch/fix-tests-types.js +24 -0
- package/scratch/rewrite-tests.js +110 -0
- package/tsc_errors.txt +60 -0
- package/tsc_errors2.txt +93 -0
- package/tmp-cv-test/package-lock.json +0 -250
- package/tmp-cv-test/package.json +0 -17
- package/tmp-cv-test/test.ts +0 -11
package/fix-imports.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const specsDir = path.join(__dirname, 'src', 'auth', 'specs');
|
|
5
|
+
|
|
6
|
+
const filesToFix = ['auth.controller.spec.ts', 'jwt.strategy.spec.ts', 'account-linking.spec.ts'];
|
|
7
|
+
|
|
8
|
+
for (const file of filesToFix) {
|
|
9
|
+
const filePath = path.join(specsDir, file);
|
|
10
|
+
if (fs.existsSync(filePath)) {
|
|
11
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
12
|
+
if (!content.includes('SESSION_LOG_REPOSITORY_TOKEN } from')) {
|
|
13
|
+
content = `import { SESSION_LOG_REPOSITORY_TOKEN } from '../interfaces/repository-tokens';\n` + content;
|
|
14
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
15
|
+
console.log(`Fixed import in ${file}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Also fix mfa-logic.spec.ts syntax again because it was badly formatted:
|
|
21
|
+
const mfaPath = path.join(specsDir, 'mfa-logic.spec.ts');
|
|
22
|
+
let mfaContent = fs.readFileSync(mfaPath, 'utf8');
|
|
23
|
+
if (mfaContent.includes('useValue: { }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN')) {
|
|
24
|
+
mfaContent = mfaContent.replace(/useValue: { }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN/g, 'useValue: { } }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN');
|
|
25
|
+
fs.writeFileSync(mfaPath, mfaContent, 'utf8');
|
|
26
|
+
console.log(`Fixed mfa-logic.spec.ts syntax again`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Ensure IdentifierType is in login-verification.spec.ts
|
|
30
|
+
const lvPath = path.join(specsDir, 'login-verification.spec.ts');
|
|
31
|
+
let lvContent = fs.readFileSync(lvPath, 'utf8');
|
|
32
|
+
if (!lvContent.includes('IdentifierType')) {
|
|
33
|
+
lvContent = lvContent.replace(/import { AuthStrategy } from '\.\.\/enums\/auth-type.enum';/, "import { AuthStrategy } from '../enums/auth-type.enum';\nimport { IdentifierType } from '../enums/identifier-type.enum';");
|
|
34
|
+
fs.writeFileSync(lvPath, lvContent, 'utf8');
|
|
35
|
+
console.log(`Fixed IdentifierType import`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Ensure MfaType is in mfa-logic.spec.ts
|
|
39
|
+
if (!mfaContent.includes('MfaType')) {
|
|
40
|
+
mfaContent = mfaContent.replace(/import { AuthStrategy } from '\.\.\/enums\/auth-type.enum';/, "import { AuthStrategy } from '../enums/auth-type.enum';\nimport { MfaType } from '../enums/mfa-type.enum';");
|
|
41
|
+
if (!mfaContent.includes('MfaType')) {
|
|
42
|
+
mfaContent = `import { MfaType } from '../enums/mfa-type.enum';\n` + mfaContent;
|
|
43
|
+
}
|
|
44
|
+
fs.writeFileSync(mfaPath, mfaContent, 'utf8');
|
|
45
|
+
console.log(`Fixed MfaType import`);
|
|
46
|
+
}
|
package/fix-syntax.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const specsDir = path.join(__dirname, 'src', 'auth', 'specs');
|
|
5
|
+
const files = fs.readdirSync(specsDir).filter(f => f.endsWith('.spec.ts'));
|
|
6
|
+
|
|
7
|
+
for (const file of files) {
|
|
8
|
+
const filePath = path.join(specsDir, file);
|
|
9
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
10
|
+
let changed = false;
|
|
11
|
+
|
|
12
|
+
// Fix syntax error from previous AI
|
|
13
|
+
if (content.includes('useValue: { }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN')) {
|
|
14
|
+
content = content.replace(/useValue: { }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN/g, 'useValue: { } }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN');
|
|
15
|
+
changed = true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (content.includes('useValue: mockRepo }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN')) {
|
|
19
|
+
// This one is actually syntactically okay but might be weirdly formatted:
|
|
20
|
+
// { provide: AUTH_REPOSITORY_TOKEN, useValue: mockRepo }, { provide: AUTH_IDENTIFIER_REPOSITORY_TOKEN, ... },
|
|
21
|
+
// Wait, in events.spec.ts it was:
|
|
22
|
+
// { provide: AUTH_REPOSITORY_TOKEN, useValue: mockRepo }, { provide: ... }
|
|
23
|
+
// This is two elements on one line, which is valid JS!
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Fix IdentifierType missing
|
|
27
|
+
if (file === 'login-verification.spec.ts' && !content.includes('IdentifierType')) {
|
|
28
|
+
content = content.replace(/import { AuthStrategy } from '\.\.\/enums\/auth-type.enum';/, "import { AuthStrategy } from '../enums/auth-type.enum';\nimport { IdentifierType } from '../enums/identifier-type.enum';");
|
|
29
|
+
changed = true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Fix MfaType missing
|
|
33
|
+
if (file === 'mfa-logic.spec.ts' && !content.includes('MfaType')) {
|
|
34
|
+
content = content.replace(/import { AuthStrategy } from '\.\.\/enums\/auth-type.enum';/, "import { AuthStrategy } from '../enums/auth-type.enum';\nimport { MfaType } from '../enums/mfa-type.enum';");
|
|
35
|
+
if (!content.includes('MfaType')) {
|
|
36
|
+
// if AuthStrategy wasn't imported
|
|
37
|
+
content = `import { MfaType } from '../enums/mfa-type.enum';\n` + content;
|
|
38
|
+
}
|
|
39
|
+
changed = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Fix missing imports in auth.controller.spec.ts
|
|
43
|
+
if (file === 'auth.controller.spec.ts' && !content.includes('SESSION_LOG_REPOSITORY_TOKEN')) {
|
|
44
|
+
content = content.replace(/import\s+{([^}]+)}\s+from\s+['"]\.\.\/interfaces\/repository-tokens['"];/, (match, p1) => {
|
|
45
|
+
return `import { ${p1.trim()}, SESSION_LOG_REPOSITORY_TOKEN } from '../interfaces/repository-tokens';`;
|
|
46
|
+
});
|
|
47
|
+
changed = true;
|
|
48
|
+
}
|
|
49
|
+
if (file === 'jwt.strategy.spec.ts' && !content.includes('SESSION_LOG_REPOSITORY_TOKEN')) {
|
|
50
|
+
content = content.replace(/import\s+{([^}]+)}\s+from\s+['"]\.\.\/interfaces\/repository-tokens['"];/, (match, p1) => {
|
|
51
|
+
return `import { ${p1.trim()}, SESSION_LOG_REPOSITORY_TOKEN } from '../interfaces/repository-tokens';`;
|
|
52
|
+
});
|
|
53
|
+
changed = true;
|
|
54
|
+
}
|
|
55
|
+
if (file === 'account-linking.spec.ts' && !content.includes('SESSION_LOG_REPOSITORY_TOKEN')) {
|
|
56
|
+
content = content.replace(/import\s+{([^}]+)}\s+from\s+['"]\.\.\/interfaces\/repository-tokens['"];/, (match, p1) => {
|
|
57
|
+
return `import { ${p1.trim()}, SESSION_LOG_REPOSITORY_TOKEN } from '../interfaces/repository-tokens';`;
|
|
58
|
+
});
|
|
59
|
+
changed = true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (changed) {
|
|
63
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
64
|
+
console.log(`Fixed syntax in ${file}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vynelix/nestjs-multi-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "An authentication service/module for NestJs that supports multiple authentication methods such as email, phone, username, OAuth, SSO, etc",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -55,47 +55,49 @@
|
|
|
55
55
|
"@nestjs/jwt": "^11.0.2",
|
|
56
56
|
"@nestjs/passport": "^11.0.5",
|
|
57
57
|
"@nestjs/swagger": "^11.2.6",
|
|
58
|
-
"@nestjs/typeorm": "^11.0.0",
|
|
59
58
|
"@nestjs/throttler": "^6.5.0",
|
|
59
|
+
"@nestjs/typeorm": "^11.0.0",
|
|
60
60
|
"@types/passport-jwt": "^4.0.1",
|
|
61
|
+
"class-transformer": "^0.5.1",
|
|
61
62
|
"class-validator": "^0.15.1",
|
|
62
63
|
"crypto": "^1.0.1",
|
|
63
64
|
"express": "^5.2.1",
|
|
64
65
|
"google-auth-library": "^9.15.1",
|
|
65
|
-
"passport-jwt": "^4.0.1",
|
|
66
66
|
"otplib": "^12.0.1",
|
|
67
|
-
"
|
|
67
|
+
"passport-jwt": "^4.0.1",
|
|
68
68
|
"typeorm": "^0.3.28"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
+
"@nestjs/common": "^11.1.15",
|
|
72
|
+
"@nestjs/config": "^4.0.3",
|
|
73
|
+
"@nestjs/core": "^11.1.15",
|
|
71
74
|
"@nestjs/event-emitter": "^3.0.1",
|
|
75
|
+
"@nestjs/jwt": "^11.0.2",
|
|
76
|
+
"@nestjs/passport": "^11.0.5",
|
|
77
|
+
"@nestjs/swagger": "^11.2.6",
|
|
72
78
|
"@nestjs/testing": "^11.1.15",
|
|
79
|
+
"@nestjs/typeorm": "^11.0.0",
|
|
80
|
+
"@prisma/client": "^6.19.3",
|
|
73
81
|
"@types/bcrypt": "^6.0.0",
|
|
74
82
|
"@types/jest": "^30.0.0",
|
|
83
|
+
"@types/passport-jwt": "^4.0.1",
|
|
75
84
|
"@types/supertest": "^7.2.0",
|
|
76
85
|
"bcrypt": "^6.0.0",
|
|
86
|
+
"class-validator": "^0.15.1",
|
|
87
|
+
"express": "^5.2.1",
|
|
88
|
+
"google-auth-library": "^9.15.1",
|
|
77
89
|
"jest": "^30.2.0",
|
|
90
|
+
"passport-jwt": "^4.0.1",
|
|
91
|
+
"prisma": "^6.19.3",
|
|
92
|
+
"reflect-metadata": "^0.2.2",
|
|
93
|
+
"rxjs": "^7.8.1",
|
|
78
94
|
"supertest": "^7.2.2",
|
|
79
95
|
"ts-jest": "^29.4.6",
|
|
80
|
-
"typescript": "^5.7.3",
|
|
81
|
-
"@nestjs/common": "^11.1.15",
|
|
82
|
-
"@nestjs/core": "^11.1.15",
|
|
83
|
-
"@nestjs/jwt": "^11.0.2",
|
|
84
|
-
"@nestjs/typeorm": "^11.0.0",
|
|
85
96
|
"typeorm": "^0.3.28",
|
|
86
|
-
"
|
|
87
|
-
"rxjs": "^7.8.1",
|
|
88
|
-
"@nestjs/swagger": "^11.2.6",
|
|
89
|
-
"@nestjs/config": "^4.0.3",
|
|
90
|
-
"@nestjs/passport": "^11.0.5",
|
|
91
|
-
"google-auth-library": "^9.15.1",
|
|
92
|
-
"class-validator": "^0.15.1",
|
|
93
|
-
"express": "^5.2.1",
|
|
94
|
-
"passport-jwt": "^4.0.1",
|
|
95
|
-
"@types/passport-jwt": "^4.0.1"
|
|
97
|
+
"typescript": "^5.7.3"
|
|
96
98
|
},
|
|
97
99
|
"dependencies": {
|
|
98
100
|
"commander": "^14.0.3",
|
|
99
101
|
"ts-node": "^10.9.2"
|
|
100
102
|
}
|
|
101
|
-
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const specsToPatch = [
|
|
5
|
+
'local-auth.strategy.spec.ts',
|
|
6
|
+
'password-features.spec.ts',
|
|
7
|
+
'mfa-logic.spec.ts'
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
specsToPatch.forEach(file => {
|
|
11
|
+
const filePath = path.join('/home/isaiah/devspace/my-open-source-projects/nestjs-multi-auth/src/auth/specs', file);
|
|
12
|
+
if (!fs.existsSync(filePath)) return;
|
|
13
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
14
|
+
|
|
15
|
+
// Add findByValue
|
|
16
|
+
if (!content.includes('findByValue: jest.fn(),')) {
|
|
17
|
+
content = content.replace('findByUidAndNamespace: jest.fn(),', 'findByUidAndNamespace: jest.fn(),\n findByValue: jest.fn(),');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Add findByUidAndStrategies
|
|
21
|
+
if (!content.includes('findByUidAndStrategies: jest.fn(),')) {
|
|
22
|
+
content = content.replace('findByValue: jest.fn(),', 'findByValue: jest.fn(),\n findByUidAndStrategies: jest.fn(),');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Add findByUidAndType
|
|
26
|
+
if (!content.includes('findByUidAndType: jest.fn(),')) {
|
|
27
|
+
content = content.replace('findByUidAndStrategies: jest.fn(),', 'findByUidAndStrategies: jest.fn(),\n findByUidAndType: jest.fn(),');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// For password-features.spec.ts
|
|
31
|
+
if (file === 'password-features.spec.ts') {
|
|
32
|
+
// otpProvider.verify should return success: true
|
|
33
|
+
content = content.replace(
|
|
34
|
+
/otpProvider\.verify\.mockResolvedValue\(\{ valid: true \}\);/g,
|
|
35
|
+
'otpProvider.verify.mockResolvedValue({ success: true, valid: true });'
|
|
36
|
+
);
|
|
37
|
+
content = content.replace(
|
|
38
|
+
/expect\(otpRepo\.save\)\.toHaveBeenCalled\(\);/g,
|
|
39
|
+
'expect(otpProvider.issue).toHaveBeenCalled();'
|
|
40
|
+
);
|
|
41
|
+
// authIdentifierRepo.findWithAuthByValue
|
|
42
|
+
if (!content.includes('authIdentifierRepo.findWithAuthByValue.mockResolvedValue')) {
|
|
43
|
+
content = content.replace(
|
|
44
|
+
/authRepo\.query\.mockResolvedValue\(\[\{ value: 'test@test\.com'/g,
|
|
45
|
+
"authIdentifierRepo.findWithAuthByValue.mockResolvedValue({ identifier: { value: 'test@test.com', type: 'EMAIL' }, auth: { id: 'auth-id', uid: 'user-uid' } });\n authRepo.query.mockResolvedValue([{ value: 'test@test.com'"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fs.writeFileSync(filePath, content);
|
|
51
|
+
console.log('Patched', file);
|
|
52
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const specs = [
|
|
5
|
+
'apple.strategy.spec.ts',
|
|
6
|
+
'facebook.strategy.spec.ts',
|
|
7
|
+
'google.strategy.spec.ts'
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
specs.forEach(file => {
|
|
11
|
+
const filePath = path.join('/home/isaiah/devspace/my-open-source-projects/nestjs-multi-auth/src/auth/specs', file);
|
|
12
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
13
|
+
|
|
14
|
+
// Add findByValue, findWithAuthByValue, findWithIdentifiers to createMockRepo
|
|
15
|
+
if (!content.includes('findByValue: jest.fn(),')) {
|
|
16
|
+
content = content.replace('findWithAuthByValue: jest.fn(),', 'findWithAuthByValue: jest.fn(),\n findByValue: jest.fn(),\n findWithIdentifiers: jest.fn(),');
|
|
17
|
+
} else if (!content.includes('findWithIdentifiers: jest.fn(),')) {
|
|
18
|
+
content = content.replace('findByValue: jest.fn(),', 'findByValue: jest.fn(),\n findWithIdentifiers: jest.fn(),');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!content.includes('findWithIdentifiers: jest.fn(),')) {
|
|
22
|
+
content = content.replace('findByUidAndEnabled: jest.fn(),', 'findByUidAndEnabled: jest.fn(),\n findWithIdentifiers: jest.fn(),');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Google specific mocks
|
|
26
|
+
if (file === 'google.strategy.spec.ts') {
|
|
27
|
+
content = content.replace(
|
|
28
|
+
'findOne: jest.fn(),',
|
|
29
|
+
'findOne: jest.fn(),\n findWithAuthByValue: jest.fn(),\n findByValue: jest.fn(),'
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Also replace `authRepo.findWithIdentifiers` in google test to avoid error
|
|
34
|
+
content = content.replace(
|
|
35
|
+
'const updatedAuth = await this.authRepo.findWithIdentifiers(auth.id);',
|
|
36
|
+
'const updatedAuth = await this.authRepo.findWithIdentifiers(auth.id);'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
fs.writeFileSync(filePath, content);
|
|
40
|
+
console.log('Patched', file);
|
|
41
|
+
});
|
package/patch-specs.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const specsDir = path.join(__dirname, 'src', 'auth', 'specs');
|
|
5
|
+
const files = fs.readdirSync(specsDir).filter(f => f.endsWith('.spec.ts'));
|
|
6
|
+
|
|
7
|
+
for (const file of files) {
|
|
8
|
+
const filePath = path.join(specsDir, file);
|
|
9
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
10
|
+
let changed = false;
|
|
11
|
+
|
|
12
|
+
// 1. Add import
|
|
13
|
+
if (content.includes('interfaces/repository-tokens') && !content.includes('SESSION_LOG_REPOSITORY_TOKEN')) {
|
|
14
|
+
content = content.replace(/import\s+{([^}]+)}\s+from\s+['"]\.\.\/interfaces\/repository-tokens['"];/, (match, p1) => {
|
|
15
|
+
return `import { ${p1.trim()}, SESSION_LOG_REPOSITORY_TOKEN } from '../interfaces/repository-tokens';`;
|
|
16
|
+
});
|
|
17
|
+
changed = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 2. Add provider
|
|
21
|
+
if (content.includes('Test.createTestingModule') && content.includes('providers:') && !content.includes('SESSION_LOG_REPOSITORY_TOKEN,')) {
|
|
22
|
+
const useValueStr = content.includes('mockRepo') ? 'mockRepo' : '{}';
|
|
23
|
+
content = content.replace(/providers:\s*\[([\s\S]*?)\]/, (match, p1) => {
|
|
24
|
+
if (!p1.includes('SESSION_LOG_REPOSITORY_TOKEN')) {
|
|
25
|
+
return `providers: [${p1} { provide: SESSION_LOG_REPOSITORY_TOKEN, useValue: ${useValueStr} },\n ]`;
|
|
26
|
+
}
|
|
27
|
+
return match;
|
|
28
|
+
});
|
|
29
|
+
changed = true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (changed) {
|
|
33
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
34
|
+
console.log(`Patched ${file}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Decouple the Authentication Core from TypeORM — nestjs-multi-auth
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
You are working on **nestjs-multi-auth**, a production-grade authentication library for NestJS.
|
|
6
|
+
|
|
7
|
+
## Objective
|
|
8
|
+
|
|
9
|
+
Refactor the authentication library so that **all persistence is abstracted behind repository interfaces**, removing any hard dependency on TypeORM from the authentication core.
|
|
10
|
+
|
|
11
|
+
## Problem
|
|
12
|
+
|
|
13
|
+
The authentication core currently depends directly on TypeORM. It imports and uses:
|
|
14
|
+
|
|
15
|
+
- `Repository`
|
|
16
|
+
- `Entity`
|
|
17
|
+
- `QueryBuilder`
|
|
18
|
+
- TypeORM decorators
|
|
19
|
+
- Raw SQL
|
|
20
|
+
|
|
21
|
+
This couples core authentication logic to a single ORM, making it difficult or impossible for developers to use **Prisma**, **Drizzle ORM**, **MikroORM**, **Sequelize**, **MongoDB**, or a custom data store without forking the library.
|
|
22
|
+
|
|
23
|
+
## Design Principles
|
|
24
|
+
|
|
25
|
+
### The authentication core IS responsible for
|
|
26
|
+
|
|
27
|
+
- Authentication and authorization business logic
|
|
28
|
+
- Identity verification workflows
|
|
29
|
+
- Session lifecycle rules
|
|
30
|
+
- OTP orchestration
|
|
31
|
+
- MFA orchestration
|
|
32
|
+
- OAuth provider linking logic
|
|
33
|
+
- All validation and decision-making
|
|
34
|
+
|
|
35
|
+
### The authentication core is NOT responsible for
|
|
36
|
+
|
|
37
|
+
- How data is queried or persisted
|
|
38
|
+
- Which database or ORM is used
|
|
39
|
+
- Schema definitions
|
|
40
|
+
- Query construction, connection management, or transactions specific to any ORM
|
|
41
|
+
|
|
42
|
+
Persistence mechanics are the responsibility of a **repository adapter**, never the core.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## New Architecture
|
|
47
|
+
|
|
48
|
+
### 1. Repository Interfaces (NEW)
|
|
49
|
+
|
|
50
|
+
Define abstract repository interfaces for every persisted concern in the library, including at minimum:
|
|
51
|
+
|
|
52
|
+
- `AuthMethodRepository`
|
|
53
|
+
- `IdentifierRepository`
|
|
54
|
+
- `SessionRepository`
|
|
55
|
+
- `OtpRepository`
|
|
56
|
+
- `MfaRepository`
|
|
57
|
+
- `OAuthProviderRepository`
|
|
58
|
+
|
|
59
|
+
Each interface should:
|
|
60
|
+
|
|
61
|
+
- Expose only the operations the authentication core actually needs (e.g. `findByIdentifier`, `create`, `update`, `delete`, `markUsed`) — not generic ORM-style methods.
|
|
62
|
+
- Use plain domain types (not ORM entities) for inputs and outputs.
|
|
63
|
+
- Contain no TypeORM types, decorators, or query-builder references anywhere in their signatures.
|
|
64
|
+
|
|
65
|
+
**Example shape:**
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
export interface SessionRepository {
|
|
69
|
+
create(session: CreateSessionInput): Promise<AuthSession>;
|
|
70
|
+
findById(id: string): Promise<AuthSession | null>;
|
|
71
|
+
revoke(id: string): Promise<void>;
|
|
72
|
+
findActiveByAuthId(authId: string): Promise<AuthSession[]>;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2. Domain Models (NEW)
|
|
77
|
+
|
|
78
|
+
Introduce plain TypeScript interfaces/types (e.g. `AuthSession`, `AuthMethod`, `AuthIdentifier`, `OtpRecord`, `MfaFactor`, `OAuthLink`) that represent persisted data within the core. These types must be:
|
|
79
|
+
|
|
80
|
+
- Free of TypeORM decorators (`@Entity`, `@Column`, `@ManyToOne`, etc.)
|
|
81
|
+
- The only shapes the authentication core ever operates on
|
|
82
|
+
- Mapped to and from ORM entities exclusively inside adapters
|
|
83
|
+
|
|
84
|
+
### 3. First-Party TypeORM Adapter
|
|
85
|
+
|
|
86
|
+
Provide a `TypeOrmAuthAdapter` package/module that:
|
|
87
|
+
|
|
88
|
+
- Implements every repository interface using TypeORM `Repository`, `Entity`, and `QueryBuilder` as needed.
|
|
89
|
+
- Preserves 100% of current behavior — this is the default, drop-in implementation.
|
|
90
|
+
- Contains all TypeORM-specific code, entity definitions, and migrations.
|
|
91
|
+
- Is the **only** place in the library where TypeORM is imported.
|
|
92
|
+
|
|
93
|
+
### 4. Adapter Extensibility
|
|
94
|
+
|
|
95
|
+
The architecture must make it straightforward to build equivalent adapters for:
|
|
96
|
+
|
|
97
|
+
- Prisma
|
|
98
|
+
- Drizzle ORM
|
|
99
|
+
- MikroORM
|
|
100
|
+
- Sequelize
|
|
101
|
+
- MongoDB (or other non-relational stores)
|
|
102
|
+
- Fully custom data stores
|
|
103
|
+
|
|
104
|
+
An adapter author should only need to implement the repository interfaces and provide domain-model mapping — no changes to the authentication core should ever be required.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Dependency Injection
|
|
109
|
+
|
|
110
|
+
- Introduce injection tokens for each repository interface (e.g. `SESSION_REPOSITORY`, `OTP_REPOSITORY`, `AUTH_METHOD_REPOSITORY`, etc.), following the same pattern as existing tokens like `AUTH_NOTIFICATION_PROVIDER`.
|
|
111
|
+
- If no adapter is registered, automatically register the `TypeOrmAuthAdapter` implementations so existing applications continue to work without any change.
|
|
112
|
+
- Allow applications to override individual repositories independently (e.g. use the TypeORM adapter for sessions but a custom repository for OTPs), rather than forcing an all-or-nothing swap.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## `AuthService` and Core Changes
|
|
117
|
+
|
|
118
|
+
- Remove all direct TypeORM imports, `@InjectRepository()` usages, and query-builder calls from the authentication core.
|
|
119
|
+
- Replace all persistence calls with calls to the appropriate repository interface.
|
|
120
|
+
- Ensure business logic (validation, expiration checks, state transitions, error handling) stays in the core services — adapters should contain **no business logic**, only data access and mapping.
|
|
121
|
+
|
|
122
|
+
**Before:**
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const session = await this.sessionRepo
|
|
126
|
+
.createQueryBuilder('session')
|
|
127
|
+
.where('session.authId = :authId', { authId })
|
|
128
|
+
.getOne();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**After:**
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const session = await this.sessionRepository.findActiveByAuthId(authId);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Backward Compatibility
|
|
140
|
+
|
|
141
|
+
This refactor must **not** break existing applications.
|
|
142
|
+
|
|
143
|
+
- Applications using the library today (implicitly relying on TypeORM) should continue to work with **zero configuration changes**, since `TypeOrmAuthAdapter` is registered by default.
|
|
144
|
+
- Only developers who wish to switch persistence layers need to provide alternate repository implementations.
|
|
145
|
+
- Public authentication API surface (methods, DTOs, module configuration options unrelated to persistence) must remain unchanged.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Code Quality Requirements
|
|
150
|
+
|
|
151
|
+
- Follow SOLID principles, in particular the Dependency Inversion Principle — the core depends on abstractions, not on TypeORM.
|
|
152
|
+
- Keep all TypeORM-specific code isolated inside the `TypeOrmAuthAdapter`; no leakage into shared/core packages.
|
|
153
|
+
- Avoid leaking ORM-specific types (entities, query builders, decorators) across the core/adapter boundary in either direction.
|
|
154
|
+
- Ensure each repository interface is small and focused (Interface Segregation) rather than one large generic repository.
|
|
155
|
+
- Ensure the authentication core is fully unit testable using in-memory or mock repository implementations, with no database required.
|
|
156
|
+
- Preserve all current tests and behavior where possible; add adapter-level tests against the interfaces.
|
|
157
|
+
- Document the repository interfaces clearly enough that a third party could implement a new adapter without reading the core source.
|