-
Notifications
You must be signed in to change notification settings - Fork 68
Feature force 2fa #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
vncloudsco
merged 3 commits into
TinyActive:feature_force_2fa
from
vncloudsco:feature_force_2fa
Oct 8, 2025
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
2 changes: 2 additions & 0 deletions
2
apps/api/prisma/migrations/20251008110124_add_first_login_flag/migration.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| -- AlterTable | ||
| ALTER TABLE "users" ADD COLUMN "isFirstLogin" BOOLEAN NOT NULL DEFAULT true; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import { comparePassword } from '../../utils/password'; | ||
| import { generateAccessToken, generateRefreshToken } from '../../utils/jwt'; | ||
| import { comparePassword, hashPassword } from '../../utils/password'; | ||
| import { generateAccessToken, generateRefreshToken, generateTempToken, verifyTempToken } from '../../utils/jwt'; | ||
| import { verify2FAToken } from '../../utils/twoFactor'; | ||
| import logger from '../../utils/logger'; | ||
| import { AuthRepository } from './auth.repository'; | ||
|
|
@@ -8,11 +8,13 @@ import { | |
| RefreshTokenDto, | ||
| Verify2FADto, | ||
| LogoutDto, | ||
| FirstLoginPasswordDto, | ||
| } from './dto'; | ||
| import { | ||
| LoginResponse, | ||
| LoginResult, | ||
| Login2FARequiredResult, | ||
| LoginFirstTimeResult, | ||
| RefreshTokenResult, | ||
| RequestMetadata, | ||
| TokenPayload, | ||
|
|
@@ -81,6 +83,23 @@ export class AuthService { | |
| throw new AuthenticationError('Invalid credentials'); | ||
| } | ||
|
|
||
| // Check if this is first login | ||
| if (user.isFirstLogin) { | ||
| logger.info(`User ${username} is logging in for the first time`); | ||
|
|
||
| const userData = this.mapUserData(user); | ||
| const tempToken = generateTempToken(user.id); | ||
|
|
||
| const result: LoginFirstTimeResult = { | ||
| requirePasswordChange: true, | ||
| userId: user.id, | ||
| tempToken, | ||
| user: userData, | ||
| }; | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Check if 2FA is enabled | ||
| if (user.twoFactor?.enabled) { | ||
| logger.info(`User ${username} requires 2FA verification`); | ||
|
|
@@ -197,6 +216,64 @@ export class AuthService { | |
| return { accessToken }; | ||
| } | ||
|
|
||
| /** | ||
| * Change password on first login | ||
| */ | ||
| async changePasswordFirstLogin( | ||
| dto: FirstLoginPasswordDto, | ||
| metadata: RequestMetadata | ||
| ): Promise<LoginResult> { | ||
| const { userId, tempToken, newPassword } = dto; | ||
|
|
||
| // Verify temp token | ||
| try { | ||
| const payload = verifyTempToken(tempToken); | ||
| if (payload.userId !== userId) { | ||
| throw new AuthenticationError('Invalid token'); | ||
| } | ||
| } catch (error) { | ||
| throw new AuthenticationError('Invalid or expired token'); | ||
| } | ||
|
|
||
| // Find user | ||
| const user = await this.authRepository.findUserById(userId); | ||
| if (!user) { | ||
| throw new NotFoundError('User not found'); | ||
| } | ||
|
|
||
| // Check if user is still in first login state | ||
| if (!user.isFirstLogin) { | ||
| throw new ValidationError('Password has already been changed'); | ||
| } | ||
|
|
||
| // Hash new password | ||
| const hashedPassword = await hashPassword(newPassword); | ||
|
|
||
| // Update password and set isFirstLogin to false | ||
| await this.authRepository.updateUserPassword(userId, hashedPassword); | ||
| await this.authRepository.updateUserFirstLoginStatus(userId, false); | ||
|
|
||
| // Log activity | ||
| await this.authRepository.createActivityLog( | ||
| userId, | ||
| 'Changed password on first login', | ||
| 'security', | ||
| metadata, | ||
| true | ||
| ); | ||
|
|
||
| logger.info(`User ${user.username} changed password on first login`); | ||
|
|
||
| // Generate tokens and complete login (no need to login again) | ||
| const result = await this.completeLogin(user, metadata, false); | ||
|
|
||
| // Add flag to indicate if 2FA setup is needed | ||
| return { | ||
| ...result, | ||
| require2FASetup: !user.twoFactor?.enabled, | ||
| }; | ||
|
Comment on lines
+268
to
+274
|
||
| } | ||
|
|
||
| /** | ||
| * Complete login process (generate tokens, update user, create session, log activity) | ||
| */ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using type guards with string property checks is fragile and doesn't provide type safety. Consider using discriminated unions with explicit type fields or proper type narrowing.