AuthServiceImpl.java

package cl.prgm.portaltrip.application.service;

import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.util.Locale;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cl.prgm.portaltrip.domain.exception.DuplicateUserException;
import cl.prgm.portaltrip.domain.exception.InvalidCredentialsException;
import cl.prgm.portaltrip.domain.model.UserAccount;
import cl.prgm.portaltrip.infrastructure.persistence.UserEntity;
import cl.prgm.portaltrip.infrastructure.persistence.repository.UserJpaRepository;
import cl.prgm.portaltrip.infrastructure.security.JwtTokenService;

@Service
public class AuthServiceImpl implements AuthService {

	private final UserJpaRepository userJpaRepository;
	private final PasswordEncoder passwordEncoder;
	private final AuthenticationManager authenticationManager;
	private final JwtTokenService jwtTokenService;
	private final BigDecimal registrationCredit;

	public AuthServiceImpl(
			UserJpaRepository userJpaRepository,
			PasswordEncoder passwordEncoder,
			AuthenticationManager authenticationManager,
			JwtTokenService jwtTokenService,
			@Value("${app.wallet.registration-credit}") BigDecimal registrationCredit) {
		this.userJpaRepository = userJpaRepository;
		this.passwordEncoder = passwordEncoder;
		this.authenticationManager = authenticationManager;
		this.jwtTokenService = jwtTokenService;
		this.registrationCredit = registrationCredit;
	}

	@Override
	@Transactional
	public AuthResult register(String fullName, String email, String password) {
		String normalizedEmail = normalizeEmail(email);
		if (userJpaRepository.existsByEmail(normalizedEmail)) {
			throw new DuplicateUserException();
		}

		OffsetDateTime now = OffsetDateTime.now();
		UserAccount user = new UserAccount(
				UUID.randomUUID(),
				normalizedEmail,
				passwordEncoder.encode(password),
				fullName.trim(),
				"ROLE_USER",
				registrationCredit,
				now,
				now);
		try {
			UserAccount saved = userJpaRepository.saveAndFlush(UserEntity.fromDomain(user)).toDomain();
			return tokenFor(saved);
		} catch (DataIntegrityViolationException exception) {
			throw new DuplicateUserException();
		}
	}

	@Override
	@Transactional(readOnly = true)
	public AuthResult login(String email, String password) {
		String normalizedEmail = normalizeEmail(email);
		try {
			authenticationManager.authenticate(
					new UsernamePasswordAuthenticationToken(normalizedEmail, password));
		} catch (AuthenticationException exception) {
			throw new InvalidCredentialsException();
		}
		UserAccount user = userJpaRepository.findByEmail(normalizedEmail)
				.map(UserEntity::toDomain)
				.orElseThrow(InvalidCredentialsException::new);
		return tokenFor(user);
	}

	private AuthResult tokenFor(UserAccount user) {
		JwtTokenService.IssuedToken token = jwtTokenService.issue(user);
		return new AuthResult(token.value(), token.expiresAt(), user);
	}

	private String normalizeEmail(String email) {
		return email.trim().toLowerCase(Locale.ROOT);
	}

}