* @category Class * @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License * @link www.splendidbear.org * @since 2026. 04. 11. */ class SecurityController extends AbstractController { #[Route('/login', name: 'MineSeekerBundle_login')] public function login(AuthenticationUtils $authenticationUtils): Response { if ($this->getUser()) { return $this->redirectToRoute('MineSeekerBundle_homepage'); } return $this->render('Security/login.html.twig', [ 'last_username' => $authenticationUtils->getLastUsername(), 'error' => $authenticationUtils->getLastAuthenticationError(), ]); } #[Route('/logout', name: 'MineSeekerBundle_logout', methods: ['POST'])] public function logout(): void { // Intercepted by the security firewall — never executed. } #[Route('/register', name: 'MineSeekerBundle_register')] public function register( Request $request, UserPasswordHasherInterface $hasher, EntityManagerInterface $em, MailerInterface $mailer, ): Response { if ($this->getUser()) { return $this->redirectToRoute('MineSeekerBundle_homepage'); } $errors = []; if ($request->isMethod('POST')) { $username = trim((string) $request->request->get('_username', '')); $email = trim((string) $request->request->get('_email', '')); $password = (string) $request->request->get('_password', ''); $passwordConfirm = (string) $request->request->get('_password_confirm', ''); if (mb_strlen($username) < 3) { $errors['username'] = 'Username must be at least 3 characters.'; } elseif ($em->getRepository(User::class)->findOneBy(['username' => $username])) { $errors['username'] = 'This username is already taken.'; } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors['email'] = 'Please enter a valid email address.'; } elseif ($em->getRepository(User::class)->findOneBy(['email' => $email])) { $errors['email'] = 'This email address is already registered.'; } if (mb_strlen($password) < 6) { $errors['password'] = 'Password must be at least 6 characters.'; } elseif ($password !== $passwordConfirm) { $errors['password_confirm'] = 'Passwords do not match.'; } if (empty($errors)) { $token = bin2hex(random_bytes(32)); $user = (new User()) ->setUsername($username) ->setEmail($email) ->setIsVerified(false) ->setVerificationToken($token); $user->setPassword($hasher->hashPassword($user, $password)); $em->persist($user); $em->flush(); $activationUrl = $this->generateUrl( 'MineSeekerBundle_activate', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL, ); $mailer->send( (new TemplatedEmail()) ->from('noreply@mineseeker.ninja') ->to($email) ->subject('Activate your MineSeeker account') ->htmlTemplate('emails/activation.html.twig') ->context([ 'username' => $username, 'activation_url' => $activationUrl, ]) ); $this->addFlash('verify_email', $email); return $this->redirectToRoute('MineSeekerBundle_register'); } } return $this->render('Security/register.html.twig', [ 'errors' => $errors, 'last_username' => $request->request->get('_username', ''), 'last_email' => $request->request->get('_email', ''), ]); } #[Route('/activate/{token}', name: 'MineSeekerBundle_activate')] public function activate(string $token, EntityManagerInterface $em): Response { $user = $em->getRepository(User::class)->findOneBy(['verificationToken' => $token]); if (!$user) { $this->addFlash('error', 'This activation link is invalid or has already been used.'); return $this->redirectToRoute('MineSeekerBundle_login'); } $user->setIsVerified(true)->setVerificationToken(null); $em->flush(); $this->addFlash('success', 'Your account is now active. Welcome, ' . $user->getUsername() . '!'); return $this->redirectToRoute('MineSeekerBundle_login'); } }