toArray()); $dataValidador->validate(field: 'nome_completo', rule: 'notEmpty', message: 'Nome completo é obrigatório'); $dataValidador->validate(field: 'email', rule: 'notEmpty', message: 'Email é obrigatório'); $dataValidador->validate(field: 'email', rule: 'validateEmail', message: 'Email inválido'); $dataValidador->validate(field: 'senha', rule: 'notEmpty', message: 'Senha é obrigatória'); $dataValidador->validate(field: 'documentcpf', rule: 'notEmpty', message: 'CPF é obrigatório'); $dataValidador->validate(field: 'documentcpf', rule: 'validateCpf', message: 'CPF informado é inválido.'); $dataValidador->validate(field: 'senha', rule: 'validatePassword', message: 'Senha inválida', additionalParams: [ [ 'minLength' => 8, 'upper' => true, 'digit' => true, 'symbol' => true ] ] ); // Se houver erros de validação, retornar resposta de erro if(!$dataValidador->passes()) { DB::rollBack(); return [ 'response_code' => 400, 'status' => 'error', 'message' => 'Validation errors', 'output' => [ 'errors' => $dataValidador->getErrors() ] ]; } // Verifica se o email já está em uso $existingEmailUser = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'email', value: $usuarioDTO->getEmail()); if($existingEmailUser['status'] === 'success' && !empty($existingEmailUser['output']['data'])) { DB::rollBack(); return [ 'response_code' => 409, 'status' => 'error', 'message' => 'E-mail já está em uso por outro usuário.', 'output' => [] ]; } // Verifica se o CPF já está em uso $existingCpfUser = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'documentcpf', value: $usuarioDTO->getDocumentCpf()); if($existingCpfUser['status'] === 'success' && !empty($existingCpfUser['output']['data'])) { DB::rollBack(); return [ 'response_code' => 409, 'status' => 'error', 'message' => 'CPF já está em uso por outro usuário.', 'output' => [] ]; } // Define dados adicionais $usuarioDTO->setUuid(uuid: Uuid::uuid7()->toString()); $usuarioDTO->setCreatedAt(created_at: (new DateTime())->format(format: 'Y-m-d H:i:s')); $usuarioDTO->setSenha(senha: password_hash(password: $usuarioDTO->getSenha(), algo: PASSWORD_ARGON2ID)); // Persiste o novo usuário no banco de dados $storeUsuario = (new UsuarioModel())->store(usuarioDTO: $usuarioDTO); if($storeUsuario['status'] !== 'success') { DB::rollBack(); return [ 'response_code' => 500, 'status' => 'error', 'message' => 'Erro ao registrar usuário. Tente novamente mais tarde.', 'output' => $storeUsuario['output'] ?? [] ]; } DB::commit(); return [ 'response_code' => $storeUsuario['response_code'], 'status' => $storeUsuario['status'], 'message' => $storeUsuario['message'], 'output' => $storeUsuario['output'] ]; } /** * Realiza a autenticação de um usuário por e-mail e senha, gerando e registrando um token JWT. * * Este método gerencia todo o fluxo de login, incluindo a verificação de credenciais, revogação de tokens antigos, geração de um novo JWT e persistência desse novo token no banco de dados. A operação é atômica e utiliza uma transação de banco de dados para garantir a consistência dos dados. * * #### Fluxo de Operação: * 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`). * 2. **Consulta do Usuário:** Busca o usuário pelo e-mail. Se o usuário não for encontrado, uma `Exception` é lançada com o código `401 Unauthorized`. * 3. **Validação da Senha:** Verifica a senha fornecida contra o hash armazenado. Se a senha for inválida, uma `Exception` é lançada com o código `401 Unauthorized`. * 4. **Revogação de Tokens Antigos:** Chama o método `revokeOldTokens` do `UsuarioModel` para invalidar todas as sessões anteriores do usuário. * 5. **Geração do JWT:** Monta o payload do JWT com os dados essenciais do usuário e os timestamps `iat` (emitido em) e `exp` (expira em), gerando o novo token. * 6. **Registro do Token:** Armazena o novo token no banco de dados usando `storeToken`. Se a operação falhar, uma `Exception` é lançada com o código `500 Internal Server Error`. * 7. **Confirmação e Retorno:** Se todas as etapas forem bem-sucedidas, a transação é confirmada (`DB::commit()`), e o token gerado é retornado com `status` 'success' e código `200 OK`. * * #### Tratamento de Erros: * - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollBack()`). * - Retorna um array com `status` 'error', o código de erro da exceção (ou 500 como fallback) e uma mensagem descritiva. * * @param string $email O e-mail do usuário para autenticação. * @param string $password A senha em texto plano fornecida pelo usuário. * @return array Um array associativo contendo o status da operação, o código de resposta HTTP e o token JWT em caso de sucesso. */ public function login(string $email, string $password): array { try { // Inicia Transação no Banco de dados DB::beginTransaction(); // Consulta as informações do usuário pelo email $userResult = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'email', value: $email); if($userResult['status'] !== 'success' || empty($userResult['output']['data'])) { throw new Exception(message: 'E-mail ou Senha incorretos.', code: 401); } // Valida a senha fornecida com a armazenada if(!password_verify(password: $password, hash: $userResult['output']['data']['senha'])) { throw new Exception(message: 'E-mail ou Senha incorretos.', code: 401); } // Revoga os tokens antigos do usuário $revokeOldTokens = (new UsuarioModel())->revokeOldTokens(user_id: $userResult['output']['data']['id']); if($revokeOldTokens['status'] !== 'success') { throw new Exception(message: 'Erro ao revogar tokens antigos.', code: 500); } // Prepara o Payload do JWT $jwtPayload = [ 'user_uuid' => $userResult['output']['data']['uuid'], 'email' => $userResult['output']['data']['email'], 'nome_completo' => $userResult['output']['data']['nome_completo'], 'iat' => (new DateTime())->getTimestamp(), 'exp' => (new DateTime())->modify(modifier: '+8 hour')->getTimestamp() ]; $token = JWTService::generateToken(payload: $jwtPayload); // Armazena o token no banco de dados $storeToken = (new UsuarioModel())->storeToken(user_id: $userResult['output']['data']['id'], token: $token); if($storeToken['status'] !== 'success') { throw new Exception(message: 'Erro ao armazenar token de autenticação.', code: 500); } DB::commit(); return [ 'response_code' => 200, 'status' => 'success', 'message' => 'Login successful.', 'output' => [ 'data' => [ 'token' => $token ] ] ]; } catch(Exception $e) { DB::rollBack(); return [ 'response_code' => $e->getCode() ?: 500, 'status' => 'error', 'message' => $e->getMessage() ?: 'An unexpected error occurred during login.' ]; } } /** * Finaliza a sessão do usuário revogando um token JWT específico. * * Este método é o responsável por orquestrar o processo de logout seguro, garantindo que o token de autenticação seja validado e, em seguida, invalidado no banco de dados. * * #### Fluxo de Operação: * 1. **Validação do Token:** O método chama `validateToken` para verificar se o token existe e ainda é válido (não expirado ou revogado no DB). Se a validação falhar, uma `Exception` é lançada com o código de erro apropriado. * 2. **Revogação no Banco de Dados:** Tenta revogar o token usando `UsuarioModel::revokeToken()`. Se a revogação falhar (ex: erro de banco de dados), uma `Exception` é lançada. * 3. **Retorno de Sucesso:** Se ambas as operações (validação e revogação) forem bem-sucedidas, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito. * * #### Tratamento de Erros: * - Qualquer `Exception` lançada durante o processo é capturada, e o método retorna um array de erro formatado, contendo o código HTTP e a mensagem da exceção. * * @param string $token O token JWT a ser revogado. * @return array Um array associativo contendo o status da operação ("success" ou "error"), o código de resposta HTTP e uma mensagem. */ public function logout(string $token): array { try { // Validar se o token existe e não está revogado $validateToken = $this->validateToken(token: $token); if($validateToken['status'] !== 'success') { throw new Exception(message: $validateToken['message'], code: $validateToken['response_code']); } // Revoga o token fornecido $revokeToken = (new UsuarioModel())->revokeToken(token: $token); if($revokeToken['status'] !== 'success') { throw new Exception(message: 'Erro ao revogar token.', code: $revokeToken['response_code']); } return [ 'response_code' => 200, 'status' => 'success', 'message' => 'Logout successful.', 'output' => [] ]; } catch(Exception $e) { return [ 'response_code' => $e->getCode() ?: 500, 'status' => 'error', 'message' => $e->getMessage() ?: 'An unexpected error occurred during logout.' ]; } } /** * Valida um token de autenticação verificando sua presença no banco de dados e seu status de expiração. * * Este método realiza uma validação de duas etapas: 1) Checa se o token existe e não está revogado no banco de dados, e 2) verifica se o timestamp de expiração (exp) no payload do token já passou. Se o token estiver expirado, ele é revogado no banco de dados. * * #### Fluxo de Operação: * 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`) para garantir que a revogação, se necessária, seja atômica. * 2. **Busca no DB:** Tenta encontrar o token usando o `UsuarioModel`. Se a busca falhar (token não encontrado ou revogado), uma `Exception` é lançada com o código `401 Unauthorized`. * 3. **Decodificação:** O token é decodificado usando `JWTService::decode()` para acessar o payload e o timestamp de expiração (`exp`). * 4. **Verificação de Expiração:** Compara o timestamp atual com o `exp` do payload. * * **Se Expirado:** O método tenta revogar o token no DB. Se a revogação for bem-sucedida, a transação é confirmada e o método retorna um erro com código `401`. Se a revogação falhar, a transação é revertida e um erro `500` é retornado. * * **Se Válido:** A transação é confirmada e o método retorna `status` 'success' com o código `200 OK` e os dados do payload. * * #### Tratamento de Erros: * - Qualquer `Exception` lançada durante o processo (falha de busca, falha de decodificação JWT, falha de revogação) é capturada. Se a falha ocorrer após o início da transação, um `DB::rollBack()` é implícito ou explicitamente chamado antes do retorno. * - Retorna um array com `status` 'error', o código de erro (ou `500` como fallback) e uma mensagem descritiva. * * @param string $token O token JWT a ser validado. * @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se válido. */ public function validateToken(string $token): array { // Inicia transação no banco de dados DB::beginTransaction(); try { $dbTokenData = (new UsuarioModel())->getTokenByidentifier(identifier: 'token', value: $token); if($dbTokenData['status'] !== 'success' || empty($dbTokenData['output']['data'])) { throw new Exception(message: 'Token inválido ou expirado.', code: 401); } // Decodifica o token para verificar sua validade $decodedToken = JWTService::decode(token: $dbTokenData['output']['data']['token']); // Verifica se o token expirou $currentTimestamp = (new DateTime())->getTimestamp(); switch (true) { case $currentTimestamp > $decodedToken['exp']: // Revoga o token expirado $revokeToken = (new UsuarioModel())->revokeToken(token: $dbTokenData['output']['data']['token']); if($revokeToken['status'] !== 'success') { DB::rollBack(); throw new Exception(message: 'Erro ao revogar token expirado.', code: 500); } DB::commit(); return [ 'response_code' => 401, 'status' => 'error', 'message' => 'Token expirado.', 'output' => [] ]; case $dbTokenData['output']['data']['revoked'] === 1: DB::commit(); return [ 'response_code' => 401, 'status' => 'error', 'message' => 'Token inválido ou expirado.', 'output' => [] ]; } DB::commit(); return [ 'response_code' => 200, 'status' => 'success', 'message' => 'Token is valid.', 'output' => ['token_data' => $decodedToken] ]; } catch(Exception $e) { return [ 'response_code' => $e->getCode() ?: 500, 'status' => 'error', 'message' => $e->getMessage() ?: 'An unexpected error occurred during token validation.' ]; } } public function getUserData(string $token): array { try { // Validar se o token existe e não está revogado $validateToken = $this->validateToken(token: $token); if($validateToken['status'] !== 'success') { throw new Exception(message: $validateToken['message'], code: $validateToken['response_code']); } return [ 'response_code' => 200, 'status' => 'success', 'message' => 'User data retrieved successfully.', 'output' => [ 'data' => $validateToken['output']['token_data'] ] ]; } catch(Exception $e) { return [ 'response_code' => $e->getCode() ?: 500, 'status' => 'error', 'message' => $e->getMessage() ?: 'An unexpected error occurred while retrieving user data.' ]; } } }