403Webshell
Server IP : 65.108.144.40  /  Your IP : 216.73.217.165
Web Server : Apache/2.4.52 (Ubuntu)
System : Linux ubuntu-8gb-hel1-1 5.15.0-173-generic #183-Ubuntu SMP Fri Mar 6 13:29:34 UTC 2026 x86_64
User : dev ( 1000)
PHP Version : 8.2.30
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/html/project-slim/app/Services/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/html/project-slim/app/Services/AuthService.php
<?php
namespace App\Services;

use App\Repositories\Interfaces\UserRepositoryInterface;
use App\Response\ApiResponse;
use App\Exceptions\ValidationException;
use App\Exceptions\UnauthorizedException;
use App\Messages\MessageConstants;
use App\Messages\MessageHelper;
use App\Services\PublicIdService;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class AuthService {
    protected UserRepositoryInterface $userRepo;
    protected array $jwtConfig;

    public function __construct(UserRepositoryInterface $userRepo, array $jwtConfig) {
        $this->userRepo = $userRepo;
        $this->jwtConfig = $jwtConfig;
    }

    public function login(string $email, string $password): array {
        $user = $this->userRepo->findByEmail($email);

        if (!$user || !$this->verifyPassword($password, $user->password_hash)) {
            throw new UnauthorizedException(MessageConstants::AUTH_INVALID_CREDENTIALS);
        }

        if (!$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_INACTIVE);
        }

        $tokens = $this->generateTokens($user);
        
        return [
            'access_token' => $tokens['access_token'],
            'refresh_token' => $tokens['refresh_token'],
            'token_type' => 'Bearer',
            'expires_in' => $this->jwtConfig['expiry'],
            'user' => $user->toArray()
        ];
    }

    public function register(array $data): array {
        // Check if email already exists
        $existingUser = $this->userRepo->findByEmail($data['email']);
        if ($existingUser) {
            throw new ValidationException(MessageHelper::alreadyExists(MessageConstants::FIELD_EMAIL), [
                MessageConstants::FIELD_EMAIL => MessageConstants::EMAIL_ALREADY_REGISTERED
            ]);
        }

        // Prepare user data
        $userData = [
            'first_name' => $data['first_name'],
            'last_name' => $data['last_name'],
            'email' => $data['email'],
            'company_name' => $data['company_name'],
            'company_domain' => $data['company_domain'],
            'password_hash' => $this->hashPassword($data['password']),
            'is_active' => true,
            'email_verified' => false,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s')
        ];
        
        // Create user
        $user = $this->userRepo->create($userData);
        
        // Generate verification token (in real app, send email)
        $verificationToken = $this->generateVerificationToken($user);
        
        return [
            'user' => $user->toArray(),
            'message' => MessageConstants::USER_REGISTERED_VERIFY_EMAIL,
            'verification_token' => $verificationToken
        ];
    }

    public function logout(string $token): array {
        // In a real application, you would add this token to a blacklist
        // For now, we'll just return success
        return ['message' => MessageConstants::AUTH_LOGOUT_SUCCESSFUL];
    }

    public function refreshToken(string $refreshToken): array {
        try {
            $decoded = JWT::decode($refreshToken, new Key($this->jwtConfig['secret'], $this->jwtConfig['algorithm']));
            
            if ($decoded->type !== 'refresh') {
                throw new UnauthorizedException(MessageHelper::invalid('refresh token'));
            }

            $user = $this->userRepo->findByEmail($decoded->email);
            if (!$user || !$user->isActive()) {
                throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
            }

            $tokens = $this->generateTokens($user);
            
            return [
                'access_token' => $tokens['access_token'],
                'refresh_token' => $tokens['refresh_token'],
                'token_type' => 'Bearer',
                'expires_in' => $this->jwtConfig['expiry']
            ];
        } catch (\Exception $e) {
            throw new UnauthorizedException(MessageHelper::invalid('refresh token'));
        }
    }

    public function forgotPassword(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            // Don't reveal if email exists or not
            return ApiResponse::success(['message' => MessageConstants::PASSWORD_RESET_IF_EMAIL_EXISTS]);
        }

        $resetToken = $this->generatePasswordResetToken($user);
        
        // In a real application, send email with reset link
        // For now, return the token (remove this in production)
        return ApiResponse::success([
            'message' => MessageConstants::AUTH_RESET_LINK_SENT,
            'reset_token' => $resetToken // Remove this in production
        ]);
    }

    public function resetPassword(string $token, string $newPassword): array {
        try {
            $decoded = JWT::decode($token, new Key($this->jwtConfig['secret'], $this->jwtConfig['algorithm']));
            
            if ($decoded->type !== 'password_reset') {
                throw new UnauthorizedException(MessageHelper::invalid('reset token'));
            }

            $user = $this->userRepo->findByEmail($decoded->email);
            if (!$user) {
                throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
            }

            $hashedPassword = $this->hashPassword($newPassword);
            $this->userRepo->updatePassword($user->id, $hashedPassword);
            
            return ApiResponse::success(['message' => MessageConstants::AUTH_PASSWORD_RESET_SUCCESSFUL]);
        } catch (\Exception $e) {
            throw new UnauthorizedException(MessageHelper::invalid('reset token') . ' or ' . MessageHelper::expired('reset token'));
        }
    }

    public function changePassword(int $userId, string $currentPassword, string $newPassword): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        if (!$this->verifyPassword($currentPassword, $user->password_hash)) {
            throw new UnauthorizedException(MessageHelper::incorrect('current password'));
        }

        $hashedPassword = $this->hashPassword($newPassword);
        $this->userRepo->updatePassword($userId, $hashedPassword);
        
        return ApiResponse::success(['message' => MessageConstants::AUTH_PASSWORD_CHANGED]);
    }

    public function verifyEmail(string $token): array {
        try {
            $decoded = JWT::decode($token, new Key($this->jwtConfig['secret'], $this->jwtConfig['algorithm']));
            
            if ($decoded->type !== 'email_verification') {
                throw new UnauthorizedException(MessageHelper::invalid('verification token'));
            }

            $user = $this->userRepo->findByEmail($decoded->email);
            if (!$user) {
                throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
            }

            if ($user->email_verified) {
                return ApiResponse::success(['message' => MessageConstants::VERIFICATION_ALREADY_VERIFIED]);
            }

            $this->userRepo->verifyEmail($user->id);
            
            return ApiResponse::success(['message' => MessageConstants::AUTH_EMAIL_VERIFIED]);
        } catch (\Exception $e) {
            throw new UnauthorizedException(MessageHelper::invalid('verification token') . ' or ' . MessageHelper::expired('verification token'));
        }
    }

    public function resendVerificationEmail(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            return ApiResponse::success(['message' => MessageConstants::VERIFICATION_IF_EMAIL_EXISTS]);
        }

        if ($user->email_verified) {
            return ApiResponse::success(['message' => MessageConstants::VERIFICATION_ALREADY_VERIFIED]);
        }

        $verificationToken = $this->generateVerificationToken($user);
        
        return ApiResponse::success([
            'message' => MessageConstants::AUTH_VERIFICATION_SENT,
            'verification_token' => $verificationToken // Remove this in production
        ]);
    }

    /**
     * Alias for resendVerificationEmail for controller compatibility
     */
    public function resendVerification(string $email): array {
        return $this->resendVerificationEmail($email);
    }

    /**
     * Get current user - alias for getAuthenticatedUser
     */
    public function getCurrentUser(int $userId): array {
        return $this->getAuthenticatedUser($userId);
    }

    /**
     * Enable two-factor authentication
     */
    public function enableTwoFactor(int $userId, string $password): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        if (!$this->verifyPassword($password, $user->password_hash)) {
            return false; // Return false for controller to handle
        }

        return $this->setupTwoFactor($userId);
    }

    /**
     * Disable two-factor authentication
     */
    public function disableTwoFactor(int $userId, string $password): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        if (!$this->verifyPassword($password, $user->password_hash)) {
            return false; // Return false for controller to handle
        }

        // Disable 2FA
        $this->userRepo->updateTwoFactor($userId, false, null);
        
        return ['message' => MessageConstants::AUTH_TWO_FACTOR_DISABLED];
    }

    public function checkToken(string $token): array {
        try {
            $decoded = JWT::decode($token, new Key($this->jwtConfig['secret'], $this->jwtConfig['algorithm']));
            
            if ($decoded->type !== 'access') {
                throw new UnauthorizedException(MessageHelper::invalid('token type'));
            }

            $user = $this->userRepo->findByEmail($decoded->email);
            if (!$user || !$user->isActive()) {
                throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
            }

            return ApiResponse::success([
                'valid' => true,
                'user' => $user->toArray()
            ]);
        } catch (\Exception $e) {
            throw new UnauthorizedException(MessageConstants::AUTH_TOKEN_INVALID);
        }
    }

    public function setupTwoFactor(int $userId): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        // Generate secret for 2FA (in real app, use Google Authenticator library)
        $secret = bin2hex(random_bytes(16));
        
        $this->userRepo->updateTwoFactor($userId, false, $secret);
        
        return ApiResponse::success([
            'secret' => $secret,
            'qr_code_url' => "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" . urlencode("otpauth://totp/YourApp:$user->email?secret=$secret&issuer=YourApp"),
            'message' => 'Scan the QR code with your authenticator app and verify to enable 2FA'
        ]);
    }

    public function verifyTwoFactor(int $userId, string $code): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        // In real app, verify the TOTP code
        // For demo purposes, accept "123456" as valid code
        if ($code === "123456" || $code === $user->two_factor_secret) {
            $this->userRepo->updateTwoFactor($userId, true, $user->two_factor_secret);
            return ApiResponse::success(['message' => MessageConstants::AUTH_TWO_FACTOR_ENABLED]);
        }

        throw new UnauthorizedException(MessageHelper::invalid('verification code'));
    }

    public function getUserSessions(int $userId): array {
        // In a real application, you would fetch active sessions from a sessions table
        // For demo purposes, return mock data
        return [
            [
                'id' => '1',
                'device' => 'Chrome on Windows',
                'ip_address' => '192.168.1.100',
                'location' => 'New York, US',
                'last_activity' => date('Y-m-d H:i:s'),
                'current' => true
            ],
            [
                'id' => '2',
                'device' => 'Safari on iPhone',
                'ip_address' => '192.168.1.101',
                'location' => 'New York, US',
                'last_activity' => date('Y-m-d H:i:s', strtotime('-1 hour')),
                'current' => false
            ]
        ];
    }

    public function revokeSession(int $userId, string $sessionId): array {
        // In a real application, you would revoke the specific session
        // For demo purposes, just return success
        return ApiResponse::success(['message' => MessageConstants::AUTH_SESSION_REVOKED]);
    }

    private function generateTokens($user): array {
        $now = time();
        
        $accessPayload = [
            'iat' => $now,
            'exp' => $now + $this->jwtConfig['expiry'],
            'type' => 'access',
            'email' => $user->email, // Use email as identifier instead of user_id
            'role' => $user->role
        ];

        $refreshPayload = [
            'iat' => $now,
            'exp' => $now + $this->jwtConfig['refresh_expiry'],
            'type' => 'refresh',
            'email' => $user->email // Use email as identifier instead of user_id
        ];

        return [
            'access_token' => JWT::encode($accessPayload, $this->jwtConfig['secret'], $this->jwtConfig['algorithm']),
            'refresh_token' => JWT::encode($refreshPayload, $this->jwtConfig['secret'], $this->jwtConfig['algorithm'])
        ];
    }

    private function generateVerificationToken($user): string {
        $payload = [
            'iat' => time(),
            'exp' => time() + 86400, // 24 hours
            'type' => 'email_verification',
            'email' => $user->email // Use email instead of user_id
        ];

        return JWT::encode($payload, $this->jwtConfig['secret'], $this->jwtConfig['algorithm']);
    }

    /**
     * Get authenticated user details
     */
    public function getAuthenticatedUser(int $userId): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        return $user->toArray();
    }

    /**
     * Revoke all user sessions
     */
    public function revokeAllSessions(int $userId): array {
        // In a real application, you would mark all tokens as revoked in a blacklist
        // For now, we'll just return success
        return ['message' => MessageConstants::AUTH_SESSIONS_REVOKED];
    }

    /**
     * Get user profile information
     */
    public function getUserProfile(int $userId): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        // Return user profile without sensitive data
        $profile = $user->toArray();
        unset($profile['password_hash']);
        
        return $profile;
    }

    /**
     * Update user profile
     */
    public function updateUserProfile(int $userId, array $data): array {
        $user = $this->userRepo->findById($userId);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        // Remove sensitive fields that shouldn't be updated via profile
        $allowedFields = ['first_name', 'last_name', 'phone', 'email'];
        $updateData = [];
        
        foreach ($allowedFields as $field) {
            if (isset($data[$field])) {
                $updateData[$field] = $data[$field];
            }
        }

        if (empty($updateData)) {
            throw new \InvalidArgumentException(MessageHelper::required('valid fields to update'));
        }

        // If email is being updated, check if it's already taken
        if (isset($updateData['email']) && $updateData['email'] !== $user->email) {
            $existingUser = $this->userRepo->findByEmail($updateData['email']);
            if ($existingUser) {
                throw new \InvalidArgumentException(MessageConstants::EMAIL_ALREADY_REGISTERED);
            }
        }

        $updatedUser = $this->userRepo->update($userId, $updateData);
        
        return $updatedUser->toArray();
    }

    // Email-based authentication methods for enhanced security

    /**
     * Get current user by email
     */
    public function getCurrentUserByEmail(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        return $user->toArray();
    }

    /**
     * Change password by email
     */
    public function changePasswordByEmail(string $email, string $currentPassword, string $newPassword): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        if (!$this->verifyPassword($currentPassword, $user->password_hash)) {
            throw new UnauthorizedException(MessageHelper::incorrect('current password'));
        }

        $hashedPassword = $this->hashPassword($newPassword);
        $this->userRepo->updatePassword($user->id, $hashedPassword);
        
        return ['message' => MessageConstants::AUTH_PASSWORD_CHANGED];
    }

    /**
     * Enable two-factor authentication by email
     */
    public function enableTwoFactorByEmail(string $email, string $password): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        if (!$this->verifyPassword($password, $user->password_hash)) {
            throw new UnauthorizedException(MessageHelper::incorrect(MessageConstants::FIELD_PASSWORD));
        }

        // Generate 2FA secret and QR code
        $secret = $this->generateTwoFactorSecret();
        $qrCode = $this->generateQRCode($user->email, $secret);
        
        // Save 2FA secret to user
        $this->userRepo->update($user->id, ['two_factor_secret' => $secret]);
        
        return [
            'secret' => $secret,
            'qr_code' => $qrCode,
            'message' => MessageConstants::AUTH_TWO_FACTOR_ENABLED
        ];
    }

    /**
     * Disable two-factor authentication by email
     */
    public function disableTwoFactorByEmail(string $email, string $password): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        if (!$this->verifyPassword($password, $user->password_hash)) {
            throw new UnauthorizedException(MessageHelper::incorrect(MessageConstants::FIELD_PASSWORD));
        }

        // Remove 2FA secret from user
        $this->userRepo->update($user->id, ['two_factor_secret' => null, 'two_factor_enabled' => false]);
        
        return ['message' => MessageConstants::AUTH_TWO_FACTOR_DISABLED];
    }

    /**
     * Get authenticated user by email
     */
    public function getAuthenticatedUserByEmail(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        return $user->toArray();
    }

    /**
     * Get user sessions by email
     */
    public function getUserSessionsByEmail(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        // Get user sessions (implementation depends on session storage)
        return $this->getUserSessions($user->id);
    }

    /**
     * Revoke all sessions by email
     */
    public function revokeAllSessionsByEmail(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        // Revoke all sessions for user
        return $this->revokeAllSessions($user->id);
    }

    /**
     * Revoke session by email and public session ID
     */
    public function revokeSessionByEmail(string $email, string $publicSessionId): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND);
        }

        // Convert public session ID to internal ID if needed
        // For now, assume session ID is already public-safe
        return $this->revokeSession($user->id, $publicSessionId);
    }

    /**
     * Get user profile by email
     */
    public function getUserProfileByEmail(string $email): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        // Return user profile without sensitive data
        $profile = $user->toArray();
        unset($profile['password_hash']);
        
        return $profile;
    }

    /**
     * Update user profile by email
     */
    public function updateUserProfileByEmail(string $email, array $data): array {
        $user = $this->userRepo->findByEmail($email);
        
        if (!$user || !$user->isActive()) {
            throw new UnauthorizedException(MessageConstants::USER_NOT_FOUND . ' or ' . MessageConstants::USER_INACTIVE);
        }

        // Remove sensitive fields that shouldn't be updated via profile
        $allowedFields = ['first_name', 'last_name', 'phone'];
        $updateData = [];
        
        foreach ($allowedFields as $field) {
            if (isset($data[$field])) {
                $updateData[$field] = $data[$field];
            }
        }

        if (empty($updateData)) {
            throw new \InvalidArgumentException(MessageHelper::required('valid fields to update'));
        }

        $updatedUser = $this->userRepo->update($user->id, $updateData);
        
        return $updatedUser->toArray();
    }

    private function generatePasswordResetToken($user): string {
        $payload = [
            'iat' => time(),
            'exp' => time() + 3600, // 1 hour
            'type' => 'password_reset',
            'email' => $user->email // Use email instead of user_id
        ];

        return JWT::encode($payload, $this->jwtConfig['secret'], $this->jwtConfig['algorithm']);
    }

    private function generateTwoFactorSecret(): string {
        // Generate a random 32-character secret for 2FA
        return $this->base32_encode(random_bytes(20));
    }

    private function generateQRCode(string $email, string $secret): string {
        // Generate QR code URL for 2FA setup
        $appName = urlencode('Project Slim');
        $label = urlencode($email);
        
        return "otpauth://totp/{$label}?secret={$secret}&issuer={$appName}";
    }

    private function hashPassword(string $password): string {
        return password_hash($password, PASSWORD_DEFAULT);
    }

    private function verifyPassword(string $password, string $hash): bool {
        return password_verify($password, $hash);
    }

    /**
     * Helper function for base32 encoding (simplified version)
     */
    private function base32_encode(string $data): string {
        $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
        $output = '';
        $input = str_split($data);
        
        $buffer = 0;
        $bitsLeft = 0;
        
        foreach ($input as $byte) {
            $buffer = ($buffer << 8) | ord($byte);
            $bitsLeft += 8;
            
            while ($bitsLeft >= 5) {
                $output .= $alphabet[($buffer >> ($bitsLeft - 5)) & 31];
                $bitsLeft -= 5;
            }
        }
        
        if ($bitsLeft > 0) {
            $output .= $alphabet[($buffer << (5 - $bitsLeft)) & 31];
        }
        
        return $output;
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit