@nauth-toolkit/mfa-totp 0.1.13 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nestjs/index.d.ts +5 -0
- package/dist/nestjs/index.d.ts.map +1 -1
- package/dist/nestjs/index.js +6 -0
- package/dist/nestjs/index.js.map +1 -1
- package/dist/nestjs/totp-mfa.module.d.ts +20 -0
- package/dist/nestjs/totp-mfa.module.d.ts.map +1 -1
- package/dist/nestjs/totp-mfa.module.js +27 -1
- package/dist/nestjs/totp-mfa.module.js.map +1 -1
- package/dist/src/dto/mfa.dto.d.ts +476 -0
- package/dist/src/dto/mfa.dto.d.ts.map +1 -1
- package/dist/src/dto/mfa.dto.js +9 -0
- package/dist/src/dto/mfa.dto.js.map +1 -1
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/totp-mfa-provider.service.d.ts +81 -1
- package/dist/src/totp-mfa-provider.service.d.ts.map +1 -1
- package/dist/src/totp-mfa-provider.service.js +101 -3
- package/dist/src/totp-mfa-provider.service.js.map +1 -1
- package/dist/src/totp.service.d.ts +158 -0
- package/dist/src/totp.service.d.ts.map +1 -1
- package/dist/src/totp.service.js +184 -1
- package/dist/src/totp.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -3,12 +3,92 @@ import { BaseMFADevice, BaseUser, IUser, NAuthConfig, NAuthLogger, MFAMethod } f
|
|
|
3
3
|
import { BaseMFAProviderService } from '@nauth-toolkit/core/internal';
|
|
4
4
|
import { TOTPService } from './totp.service';
|
|
5
5
|
import { SetupTOTPResponseDTO } from './dto/mfa.dto';
|
|
6
|
+
/**
|
|
7
|
+
* TOTP MFA Provider Service
|
|
8
|
+
*
|
|
9
|
+
* Implements TOTP (Time-based One-Time Password) MFA method.
|
|
10
|
+
* Extends BaseMFAProviderService to provide TOTP-specific functionality.
|
|
11
|
+
*
|
|
12
|
+
* This service handles:
|
|
13
|
+
* - TOTP secret generation and QR code creation
|
|
14
|
+
* - TOTP code verification during setup and authentication
|
|
15
|
+
* - MFA device creation for TOTP
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* @Module({
|
|
20
|
+
* imports: [TOTPMFAModule],
|
|
21
|
+
* })
|
|
22
|
+
* export class AppModule {}
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
6
25
|
export declare class TOTPMFAProviderService extends BaseMFAProviderService {
|
|
7
26
|
private readonly totpService;
|
|
8
27
|
readonly methodName = MFAMethod.TOTP;
|
|
9
|
-
constructor(mfaDeviceRepository: Repository<BaseMFADevice>, userRepository: Repository<BaseUser>, config: NAuthConfig, logger: NAuthLogger, passwordService: unknown, totpService: TOTPService, challengeService?: unknown,
|
|
28
|
+
constructor(mfaDeviceRepository: Repository<BaseMFADevice>, userRepository: Repository<BaseUser>, config: NAuthConfig, logger: NAuthLogger, passwordService: unknown, totpService: TOTPService, challengeService?: unknown, // ChallengeService (optional)
|
|
29
|
+
auditService?: unknown, // AuthAuditService (optional)
|
|
30
|
+
clientInfoService?: unknown);
|
|
31
|
+
/**
|
|
32
|
+
* Setup TOTP for user
|
|
33
|
+
*
|
|
34
|
+
* Generates TOTP secret and QR code for authenticator app setup.
|
|
35
|
+
* User must scan QR code and verify with a code to complete setup.
|
|
36
|
+
*
|
|
37
|
+
* @param user - User setting up TOTP
|
|
38
|
+
* @param _setupData - Not used for TOTP
|
|
39
|
+
* @returns TOTP setup information (secret, QR code, manual entry key)
|
|
40
|
+
* @throws {NAuthException} If TOTP is not enabled
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const setup = await provider.setup(user);
|
|
45
|
+
* // Client displays setup.qrCode and setup.manualEntryKey
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
10
48
|
setup(user: IUser, _setupData?: unknown): Promise<SetupTOTPResponseDTO>;
|
|
49
|
+
/**
|
|
50
|
+
* Verify and complete TOTP setup
|
|
51
|
+
*
|
|
52
|
+
* Validates the TOTP code and stores the device if valid.
|
|
53
|
+
* Enables MFA for user if this is their first device.
|
|
54
|
+
*
|
|
55
|
+
* **Race Condition Safety:**
|
|
56
|
+
* Device creation uses transaction with pessimistic locking to prevent duplicates.
|
|
57
|
+
* If device already exists (e.g., from concurrent request), returns existing device.
|
|
58
|
+
* Database unique constraint (userId, type) provides final safety net.
|
|
59
|
+
*
|
|
60
|
+
* @param user - User completing TOTP setup
|
|
61
|
+
* @param verificationData - Verification data (must be VerifyTOTPSetupDTO)
|
|
62
|
+
* @param deviceName - Optional device name override
|
|
63
|
+
* @returns MFA device ID (created or existing)
|
|
64
|
+
* @throws {NAuthException} If code is invalid
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const deviceId = await provider.verifySetup(user, {
|
|
69
|
+
* secret: 'base32secret',
|
|
70
|
+
* code: '123456',
|
|
71
|
+
* deviceName: 'Google Authenticator'
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
11
75
|
verifySetup(user: IUser, verificationData: unknown, deviceName?: string): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Verify TOTP code during authentication
|
|
78
|
+
*
|
|
79
|
+
* Validates the TOTP code for an existing device.
|
|
80
|
+
*
|
|
81
|
+
* @param user - User being authenticated
|
|
82
|
+
* @param code - TOTP code (string)
|
|
83
|
+
* @param deviceId - Optional device ID to verify against
|
|
84
|
+
* @returns True if verification succeeds
|
|
85
|
+
* @throws {NAuthException} If device not found or verification fails
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const isValid = await provider.verify(user, '123456');
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
12
92
|
verify(user: IUser, code: unknown, deviceId?: number): Promise<boolean>;
|
|
13
93
|
}
|
|
14
94
|
//# sourceMappingURL=totp-mfa-provider.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"totp-mfa-provider.service.d.ts","sourceRoot":"","sources":["../../src/totp-mfa-provider.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EACL,aAAa,EACb,QAAQ,EACR,KAAK,EACL,WAAW,EACX,WAAW,EAGX,SAAS,EACV,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAsB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"totp-mfa-provider.service.d.ts","sourceRoot":"","sources":["../../src/totp-mfa-provider.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EACL,aAAa,EACb,QAAQ,EACR,KAAK,EACL,WAAW,EACX,WAAW,EAGX,SAAS,EACV,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAsB,MAAM,eAAe,CAAC;AAEzE;;;;;;;;;;;;;;;;;;GAkBG;AAEH,qBAAa,sBAAuB,SAAQ,sBAAsB;IAS9D,OAAO,CAAC,QAAQ,CAAC,WAAW;IAR9B,QAAQ,CAAC,UAAU,kBAAkB;gBAGnC,mBAAmB,EAAE,UAAU,CAAC,aAAa,CAAC,EAC9C,cAAc,EAAE,UAAU,CAAC,QAAQ,CAAC,EACpC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,WAAW,EACnB,eAAe,EAAE,OAAO,EACP,WAAW,EAAE,WAAW,EACzC,gBAAgB,CAAC,EAAE,OAAO,EAAE,8BAA8B;IAC1D,YAAY,CAAC,EAAE,OAAO,EAAE,8BAA8B;IACtD,iBAAiB,CAAC,EAAE,OAAO;IAc7B;;;;;;;;;;;;;;;;OAgBG;IACG,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAgB7E;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA0C/F;;;;;;;;;;;;;;;OAeG;IACG,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAoC9E"}
|
|
@@ -1,47 +1,141 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TOTPMFAProviderService = void 0;
|
|
4
|
+
// Public API imports
|
|
4
5
|
const core_1 = require("@nauth-toolkit/core");
|
|
6
|
+
// Internal API imports (for provider implementations)
|
|
5
7
|
const internal_1 = require("@nauth-toolkit/core/internal");
|
|
8
|
+
/**
|
|
9
|
+
* TOTP MFA Provider Service
|
|
10
|
+
*
|
|
11
|
+
* Implements TOTP (Time-based One-Time Password) MFA method.
|
|
12
|
+
* Extends BaseMFAProviderService to provide TOTP-specific functionality.
|
|
13
|
+
*
|
|
14
|
+
* This service handles:
|
|
15
|
+
* - TOTP secret generation and QR code creation
|
|
16
|
+
* - TOTP code verification during setup and authentication
|
|
17
|
+
* - MFA device creation for TOTP
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* @Module({
|
|
22
|
+
* imports: [TOTPMFAModule],
|
|
23
|
+
* })
|
|
24
|
+
* export class AppModule {}
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
6
27
|
class TOTPMFAProviderService extends internal_1.BaseMFAProviderService {
|
|
7
28
|
totpService;
|
|
8
29
|
methodName = core_1.MFAMethod.TOTP;
|
|
9
|
-
constructor(mfaDeviceRepository, userRepository, config, logger, passwordService, totpService, challengeService,
|
|
30
|
+
constructor(mfaDeviceRepository, userRepository, config, logger, passwordService, totpService, challengeService, // ChallengeService (optional)
|
|
31
|
+
auditService, // AuthAuditService (optional)
|
|
32
|
+
clientInfoService) {
|
|
10
33
|
super(mfaDeviceRepository, userRepository, config, logger, passwordService, challengeService, auditService, clientInfoService);
|
|
11
34
|
this.totpService = totpService;
|
|
12
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Setup TOTP for user
|
|
38
|
+
*
|
|
39
|
+
* Generates TOTP secret and QR code for authenticator app setup.
|
|
40
|
+
* User must scan QR code and verify with a code to complete setup.
|
|
41
|
+
*
|
|
42
|
+
* @param user - User setting up TOTP
|
|
43
|
+
* @param _setupData - Not used for TOTP
|
|
44
|
+
* @returns TOTP setup information (secret, QR code, manual entry key)
|
|
45
|
+
* @throws {NAuthException} If TOTP is not enabled
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const setup = await provider.setup(user);
|
|
50
|
+
* // Client displays setup.qrCode and setup.manualEntryKey
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
13
53
|
async setup(user, _setupData) {
|
|
14
54
|
this.logger?.log?.(`Setting up TOTP for user: ${user.sub}`);
|
|
55
|
+
// Check if TOTP is allowed
|
|
15
56
|
if (!this.isMethodAllowed()) {
|
|
16
57
|
throw new core_1.NAuthException(core_1.AuthErrorCode.VALIDATION_FAILED, 'TOTP is not enabled', { feature: 'totp' });
|
|
17
58
|
}
|
|
59
|
+
// Generate secret and QR code
|
|
18
60
|
const setup = await this.totpService.generateSecret(user.email);
|
|
19
61
|
this.logger?.log?.(`TOTP setup initiated for user: ${user.sub}`);
|
|
20
62
|
return setup;
|
|
21
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Verify and complete TOTP setup
|
|
66
|
+
*
|
|
67
|
+
* Validates the TOTP code and stores the device if valid.
|
|
68
|
+
* Enables MFA for user if this is their first device.
|
|
69
|
+
*
|
|
70
|
+
* **Race Condition Safety:**
|
|
71
|
+
* Device creation uses transaction with pessimistic locking to prevent duplicates.
|
|
72
|
+
* If device already exists (e.g., from concurrent request), returns existing device.
|
|
73
|
+
* Database unique constraint (userId, type) provides final safety net.
|
|
74
|
+
*
|
|
75
|
+
* @param user - User completing TOTP setup
|
|
76
|
+
* @param verificationData - Verification data (must be VerifyTOTPSetupDTO)
|
|
77
|
+
* @param deviceName - Optional device name override
|
|
78
|
+
* @returns MFA device ID (created or existing)
|
|
79
|
+
* @throws {NAuthException} If code is invalid
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const deviceId = await provider.verifySetup(user, {
|
|
84
|
+
* secret: 'base32secret',
|
|
85
|
+
* code: '123456',
|
|
86
|
+
* deviceName: 'Google Authenticator'
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
22
90
|
async verifySetup(user, verificationData, deviceName) {
|
|
23
91
|
this.logger?.log?.(`Verifying TOTP setup for user: ${user.sub}`);
|
|
24
92
|
const dto = verificationData;
|
|
93
|
+
// Validate secret format
|
|
25
94
|
if (!this.totpService.isValidSecret(dto.secret)) {
|
|
26
95
|
throw new core_1.NAuthException(core_1.AuthErrorCode.VALIDATION_FAILED, 'Invalid TOTP secret', { field: 'secret' });
|
|
27
96
|
}
|
|
97
|
+
// Verify code
|
|
28
98
|
const result = this.totpService.verifyCodeWithDetails(dto.secret, dto.code);
|
|
29
99
|
if (!result.valid) {
|
|
30
100
|
throw new core_1.NAuthException(core_1.AuthErrorCode.VERIFICATION_CODE_INVALID, result.error || 'Invalid TOTP code');
|
|
31
101
|
}
|
|
102
|
+
// Get user entity for MFA status check
|
|
32
103
|
const userEntity = user;
|
|
33
104
|
const userId = userEntity.id;
|
|
34
105
|
const userMfaEnabled = userEntity.mfaEnabled || false;
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Create MFA device (transaction-safe with duplicate prevention)
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// createDevice() uses pessimistic locking to prevent race conditions
|
|
110
|
+
// If device already exists, returns existing device instead of creating duplicate
|
|
111
|
+
// Database unique constraint (userId, type) provides additional safety
|
|
35
112
|
const device = await this.createDevice(userId, {
|
|
36
113
|
name: deviceName || dto.deviceName || 'Authenticator App',
|
|
37
|
-
secret: dto.secret,
|
|
114
|
+
secret: dto.secret, // TODO: Encrypt at rest in production
|
|
38
115
|
isActive: true,
|
|
39
|
-
isPrimary: !userMfaEnabled,
|
|
116
|
+
isPrimary: !userMfaEnabled, // First device becomes primary
|
|
40
117
|
});
|
|
118
|
+
// Enable MFA if not already enabled
|
|
41
119
|
await this.enableMFAForUser(user);
|
|
42
120
|
this.logger?.log?.(`TOTP setup completed for user: ${user.sub}`);
|
|
43
121
|
return device.id;
|
|
44
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Verify TOTP code during authentication
|
|
125
|
+
*
|
|
126
|
+
* Validates the TOTP code for an existing device.
|
|
127
|
+
*
|
|
128
|
+
* @param user - User being authenticated
|
|
129
|
+
* @param code - TOTP code (string)
|
|
130
|
+
* @param deviceId - Optional device ID to verify against
|
|
131
|
+
* @returns True if verification succeeds
|
|
132
|
+
* @throws {NAuthException} If device not found or verification fails
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const isValid = await provider.verify(user, '123456');
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
45
139
|
async verify(user, code, deviceId) {
|
|
46
140
|
this.logger?.log?.(`Verifying TOTP code for user: ${user.sub}`);
|
|
47
141
|
const totpCode = code;
|
|
@@ -49,15 +143,19 @@ class TOTPMFAProviderService extends internal_1.BaseMFAProviderService {
|
|
|
49
143
|
this.logger?.warn?.('Invalid TOTP code format');
|
|
50
144
|
return false;
|
|
51
145
|
}
|
|
146
|
+
// Get user entity
|
|
52
147
|
const userEntity = user;
|
|
53
148
|
const userId = userEntity.id;
|
|
149
|
+
// Find device
|
|
54
150
|
const device = await this.findDevice(userId, deviceId);
|
|
55
151
|
if (!device || !device.secret) {
|
|
56
152
|
this.logger?.warn?.(`No active TOTP device found for user: ${user.sub}`);
|
|
57
153
|
return false;
|
|
58
154
|
}
|
|
155
|
+
// Verify code
|
|
59
156
|
const isValid = this.totpService.verifyCode(device.secret, totpCode);
|
|
60
157
|
if (isValid) {
|
|
158
|
+
// Update device usage
|
|
61
159
|
await this.updateDeviceUsage(device.id);
|
|
62
160
|
this.logger?.log?.(`TOTP code verified successfully for user: ${user.sub}`);
|
|
63
161
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"totp-mfa-provider.service.js","sourceRoot":"","sources":["../../src/totp-mfa-provider.service.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"totp-mfa-provider.service.js","sourceRoot":"","sources":["../../src/totp-mfa-provider.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +1,180 @@
|
|
|
1
1
|
import { NAuthConfig, NAuthLogger } from '@nauth-toolkit/core';
|
|
2
2
|
import { SetupTOTPResponseDTO } from './dto/mfa.dto';
|
|
3
|
+
/**
|
|
4
|
+
* TOTP (Time-based One-Time Password) Service
|
|
5
|
+
*
|
|
6
|
+
* Handles authenticator app functionality including:
|
|
7
|
+
* - Secret generation for new TOTP devices
|
|
8
|
+
* - QR code generation for easy setup
|
|
9
|
+
* - TOTP code validation with time window
|
|
10
|
+
* - Compatible with Google Authenticator, Authy, 1Password, etc.
|
|
11
|
+
*
|
|
12
|
+
* Uses industry-standard TOTP (RFC 6238) with configurable parameters.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // Generate TOTP setup
|
|
17
|
+
* const setup = await totpService.generateSecret('user@example.com');
|
|
18
|
+
* // Returns: { secret, qrCode, manualEntryKey, issuer, accountName }
|
|
19
|
+
*
|
|
20
|
+
* // Verify TOTP code
|
|
21
|
+
* const isValid = totpService.verifyCode('base32secret', '123456');
|
|
22
|
+
* // Returns: true if code is valid
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
3
25
|
export declare class TOTPService {
|
|
4
26
|
private readonly config;
|
|
5
27
|
private readonly logger;
|
|
6
28
|
private readonly defaultConfig;
|
|
7
29
|
constructor(config: NAuthConfig, logger: NAuthLogger);
|
|
30
|
+
/**
|
|
31
|
+
* Configure otplib authenticator with config settings
|
|
32
|
+
*
|
|
33
|
+
* Applies TOTP configuration from NAuthConfig or uses defaults.
|
|
34
|
+
* Called during service initialization.
|
|
35
|
+
*
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
8
38
|
private configureAuthenticator;
|
|
39
|
+
/**
|
|
40
|
+
* Get TOTP configuration with defaults
|
|
41
|
+
*
|
|
42
|
+
* @returns Complete TOTP configuration
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
9
45
|
private getTOTPConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Get issuer name for TOTP URIs
|
|
48
|
+
*
|
|
49
|
+
* @returns Issuer name from config or default
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
10
52
|
private getIssuer;
|
|
53
|
+
/**
|
|
54
|
+
* Generate TOTP secret and QR code for user setup
|
|
55
|
+
*
|
|
56
|
+
* Creates a new TOTP secret, generates QR code and manual entry key.
|
|
57
|
+
* User must scan QR code with authenticator app to complete setup.
|
|
58
|
+
*
|
|
59
|
+
* @param accountName - User's email or username (displayed in authenticator app)
|
|
60
|
+
* @returns Setup information including QR code and secret
|
|
61
|
+
* @throws {BadRequestException} If QR code generation fails
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const setup = await totpService.generateSecret('user@example.com');
|
|
66
|
+
* // Client displays QR code and manual entry key
|
|
67
|
+
* // User scans QR with Google Authenticator, Authy, etc.
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
11
70
|
generateSecret(accountName: string): Promise<SetupTOTPResponseDTO>;
|
|
71
|
+
/**
|
|
72
|
+
* Format secret for manual entry
|
|
73
|
+
*
|
|
74
|
+
* Converts base32 secret into groups of 4 characters for easy manual input.
|
|
75
|
+
*
|
|
76
|
+
* @param secret - Base32-encoded secret
|
|
77
|
+
* @returns Formatted secret (e.g., 'ABCD EFGH IJKL MNOP')
|
|
78
|
+
* @private
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* formatSecretForManualEntry('ABCDEFGHIJKLMNOP')
|
|
83
|
+
* // Returns: 'ABCD EFGH IJKL MNOP'
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
12
86
|
private formatSecretForManualEntry;
|
|
87
|
+
/**
|
|
88
|
+
* Verify TOTP code against secret
|
|
89
|
+
*
|
|
90
|
+
* Validates a 6-digit TOTP code using time-based verification.
|
|
91
|
+
* Checks current time step plus configured window (before/after).
|
|
92
|
+
*
|
|
93
|
+
* SECURITY: Uses time window to account for clock drift and user delay.
|
|
94
|
+
* Default window of 1 checks 3 time steps (current, ±1).
|
|
95
|
+
*
|
|
96
|
+
* @param secret - Base32-encoded TOTP secret
|
|
97
|
+
* @param code - 6-digit TOTP code from authenticator app
|
|
98
|
+
* @returns True if code is valid within time window
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // User enters code from Google Authenticator
|
|
103
|
+
* const isValid = totpService.verifyCode(user.totpSecret, '123456');
|
|
104
|
+
* if (isValid) {
|
|
105
|
+
* // Grant access
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
13
109
|
verifyCode(secret: string, code: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Verify TOTP code with additional validation
|
|
112
|
+
*
|
|
113
|
+
* Extended verification that checks code format and provides detailed error messages.
|
|
114
|
+
*
|
|
115
|
+
* @param secret - Base32-encoded TOTP secret
|
|
116
|
+
* @param code - TOTP code to verify
|
|
117
|
+
* @returns Verification result with error message if invalid
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const result = totpService.verifyCodeWithDetails(secret, '123456');
|
|
122
|
+
* if (!result.valid) {
|
|
123
|
+
* throw new BadRequestException(result.error);
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
14
127
|
verifyCodeWithDetails(secret: string, code: string): {
|
|
15
128
|
valid: boolean;
|
|
16
129
|
error?: string;
|
|
17
130
|
};
|
|
131
|
+
/**
|
|
132
|
+
* Generate current TOTP code for secret
|
|
133
|
+
*
|
|
134
|
+
* FOR TESTING ONLY - Do not use in production authentication flow.
|
|
135
|
+
* This method generates the current valid code for a secret.
|
|
136
|
+
*
|
|
137
|
+
* @param secret - Base32-encoded TOTP secret
|
|
138
|
+
* @returns Current 6-digit TOTP code
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* // Testing only
|
|
143
|
+
* const code = totpService.generateCode(secret);
|
|
144
|
+
* // Returns: '123456'
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
18
147
|
generateCode(secret: string): string;
|
|
148
|
+
/**
|
|
149
|
+
* Validate secret format
|
|
150
|
+
*
|
|
151
|
+
* Checks if a secret is valid base32 and has sufficient length.
|
|
152
|
+
*
|
|
153
|
+
* @param secret - Secret to validate
|
|
154
|
+
* @returns True if secret is valid
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* if (!totpService.isValidSecret(secret)) {
|
|
159
|
+
* throw new BadRequestException('Invalid TOTP secret');
|
|
160
|
+
* }
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
19
163
|
isValidSecret(secret: string): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Get time remaining until next code
|
|
166
|
+
*
|
|
167
|
+
* Returns seconds until the current TOTP code expires.
|
|
168
|
+
* Useful for UI countdowns.
|
|
169
|
+
*
|
|
170
|
+
* @returns Seconds remaining (0-30 for default 30s step)
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const remaining = totpService.getTimeRemaining();
|
|
175
|
+
* // Returns: 15 (seconds until code changes)
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
20
178
|
getTimeRemaining(): number;
|
|
21
179
|
}
|
|
22
180
|
//# sourceMappingURL=totp.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"totp.service.d.ts","sourceRoot":"","sources":["../../src/totp.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAc,WAAW,EAAiC,MAAM,qBAAqB,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"totp.service.d.ts","sourceRoot":"","sources":["../../src/totp.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAc,WAAW,EAAiC,MAAM,qBAAqB,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,qBAAa,WAAW;IASpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAK5B;gBAGiB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,WAAW;IAUtC;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IAe9B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAQjB;;;;;;;;;;;;;;;;OAgBG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmCxE;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,0BAA0B;IAQlC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IA6BjD;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IA+BvF;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAIpC;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAkBtC;;;;;;;;;;;;;OAaG;IACH,gBAAgB,IAAI,MAAM;CAM3B"}
|