Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/main/java/project/flipnote/auth/controller/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import project.flipnote.auth.controller.docs.AuthControllerDocs;
import project.flipnote.auth.model.ChangePasswordRequest;
import project.flipnote.auth.model.EmailVerificationRequest;
import project.flipnote.auth.model.EmailVerifyRequest;
import project.flipnote.auth.model.PasswordResetCreateRequest;
import project.flipnote.auth.model.PasswordResetRequest;
import project.flipnote.auth.model.TokenPair;
import project.flipnote.auth.model.UserLoginRequest;
import project.flipnote.auth.model.UserLoginResponse;
import project.flipnote.auth.model.UserRegisterRequest;
import project.flipnote.auth.model.UserRegisterResponse;
import project.flipnote.auth.model.request.ChangePasswordRequest;
import project.flipnote.auth.model.request.EmailVerificationRequest;
import project.flipnote.auth.model.request.EmailVerifyRequest;
import project.flipnote.auth.model.request.PasswordResetCreateRequest;
import project.flipnote.auth.model.request.PasswordResetRequest;
import project.flipnote.auth.model.vo.TokenPair;
import project.flipnote.auth.model.request.UserLoginRequest;
import project.flipnote.auth.model.response.UserLoginResponse;
import project.flipnote.auth.model.request.UserRegisterRequest;
import project.flipnote.auth.model.response.UserRegisterResponse;
import project.flipnote.auth.service.AuthService;
import project.flipnote.common.security.dto.AuthPrinciple;
import project.flipnote.common.security.jwt.JwtConstants;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import project.flipnote.auth.constants.OAuthConstants;
import project.flipnote.auth.controller.docs.OAuthControllerDocs;
import project.flipnote.auth.exception.AuthErrorCode;
import project.flipnote.auth.model.AuthorizationRedirect;
import project.flipnote.auth.model.TokenPair;
import project.flipnote.auth.model.vo.AuthorizationRedirect;
import project.flipnote.auth.model.vo.TokenPair;
import project.flipnote.auth.service.OAuthService;
import project.flipnote.common.config.ClientProperties;
import project.flipnote.common.exception.BizException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import project.flipnote.auth.model.ChangePasswordRequest;
import project.flipnote.auth.model.EmailVerificationRequest;
import project.flipnote.auth.model.EmailVerifyRequest;
import project.flipnote.auth.model.PasswordResetCreateRequest;
import project.flipnote.auth.model.PasswordResetRequest;
import project.flipnote.auth.model.UserLoginRequest;
import project.flipnote.auth.model.UserLoginResponse;
import project.flipnote.auth.model.UserRegisterRequest;
import project.flipnote.auth.model.UserRegisterResponse;
import project.flipnote.auth.model.request.ChangePasswordRequest;
import project.flipnote.auth.model.request.EmailVerificationRequest;
import project.flipnote.auth.model.request.EmailVerifyRequest;
import project.flipnote.auth.model.request.PasswordResetCreateRequest;
import project.flipnote.auth.model.request.PasswordResetRequest;
import project.flipnote.auth.model.request.UserLoginRequest;
import project.flipnote.auth.model.response.UserLoginResponse;
import project.flipnote.auth.model.request.UserRegisterRequest;
import project.flipnote.auth.model.response.UserRegisterResponse;
import project.flipnote.common.security.dto.AuthPrinciple;
import project.flipnote.user.model.SocialLinksResponse;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package project.flipnote.auth.listener;

import org.springframework.context.event.EventListener;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import project.flipnote.auth.constants.VerificationConstants;
import project.flipnote.auth.event.EmailVerificationSendEvent;
import project.flipnote.auth.model.event.EmailVerificationSendEvent;
import project.flipnote.common.exception.EmailSendException;
import project.flipnote.infra.email.EmailService;

Expand All @@ -27,7 +28,7 @@ public class EmailVerificationEventListener {
retryFor = { EmailSendException.class },
backoff = @Backoff(delay = 2000, multiplier = 2)
)
@EventListener
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleEmailVerificationSendEvent(EmailVerificationSendEvent event) {
emailService.sendEmailVerificationCode(event.to(), event.code(), VerificationConstants.CODE_TTL_MINUTES);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import project.flipnote.auth.constants.PasswordResetConstants;
import project.flipnote.auth.event.PasswordResetCreateEvent;
import project.flipnote.auth.model.event.PasswordResetCreateEvent;
import project.flipnote.common.exception.EmailSendException;
import project.flipnote.infra.email.EmailService;

Expand All @@ -24,10 +24,10 @@ public class PasswordResetCreateEventListener {
@Async
@Retryable(
maxAttempts = 3,
retryFor = { EmailSendException.class },
retryFor = {EmailSendException.class},
backoff = @Backoff(delay = 2000, multiplier = 2)
)
@EventListener
@EventListener()
public void handlePasswordResetCreateEvent(PasswordResetCreateEvent event) {
emailService.sendPasswordResetLink(event.to(), event.link(), PasswordResetConstants.TOKEN_TTL_MINUTES);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.event;
package project.flipnote.auth.model.event;

public record EmailVerificationSendEvent(
String to,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.event;
package project.flipnote.auth.model.event;

public record PasswordResetCreateEvent(
String to,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import project.flipnote.common.validation.annotation.ValidPassword;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import jakarta.validation.constraints.NotBlank;
import project.flipnote.common.validation.annotation.ValidPassword;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.response;

public record UserLoginResponse(
String accessToken
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.response;

public record UserRegisterResponse(
Long userId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.vo;

import org.springframework.http.ResponseCookie;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package project.flipnote.auth.model;
package project.flipnote.auth.model.vo;

public record TokenPair(
String accessToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public interface OAuthLinkRepository extends JpaRepository<OAuthLink, Long> {

boolean existsByUserAuth_IdAndProviderId(Long authId, String providerId);
boolean existsByUserAuth_IdAndProviderAndProviderId(Long authId, String provider, String providerId);

List<OAuthLink> findByUserAuth_Id(Long authId);

Expand Down
76 changes: 76 additions & 0 deletions src/main/java/project/flipnote/auth/service/AuthPolicyService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package project.flipnote.auth.service;

import java.util.Objects;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import project.flipnote.auth.exception.AuthErrorCode;
import project.flipnote.auth.repository.EmailVerificationRedisRepository;
import project.flipnote.auth.repository.OAuthLinkRepository;
import project.flipnote.auth.repository.PasswordResetRedisRepository;
import project.flipnote.auth.repository.TokenBlacklistRedisRepository;
import project.flipnote.auth.repository.UserAuthRepository;
import project.flipnote.common.exception.BizException;

@RequiredArgsConstructor
@Service
public class AuthPolicyService {

private final EmailVerificationRedisRepository emailVerificationRedisRepository;
private final PasswordEncoder passwordEncoder;
private final UserAuthRepository userAuthRepository;
private final OAuthLinkRepository oAuthLinkRepository;
private final TokenBlacklistRedisRepository tokenBlacklistRedisRepository;
private final PasswordResetRedisRepository passwordResetRedisRepository;


public void validateEmailVerified(String email) {
if (!emailVerificationRedisRepository.isVerified(email)) {
throw new BizException(AuthErrorCode.UNVERIFIED_EMAIL);
}
}

public void validatePasswordMatch(String rawPassword, String encodedPassword) {
if (!passwordEncoder.matches(rawPassword, encodedPassword)) {
throw new BizException(AuthErrorCode.INVALID_CREDENTIALS);
}
}

public void validateEmailDuplicate(String email) {
if (userAuthRepository.existsByEmail(email)) {
throw new BizException(AuthErrorCode.EXISTING_EMAIL);
}
}

public void validateVerificationCodeNotExists(String email) {
if (emailVerificationRedisRepository.existCode(email)) {
throw new BizException(AuthErrorCode.ALREADY_ISSUED_VERIFICATION_CODE);
}
}

public void validateVerificationCode(String inputCode, String savedCode) {
if (!Objects.equals(inputCode, savedCode)) {
throw new BizException(AuthErrorCode.INVALID_VERIFICATION_CODE);
}
}

public void validateSocialLinkExists(Long socialLinkId, Long authId) {
if (!oAuthLinkRepository.existsByIdAndUserAuth_Id(socialLinkId, authId)) {
throw new BizException(AuthErrorCode.SOCIAL_LINK_NOT_FOUND);
}
}

public void validateRefreshTokenExists(String refreshToken) {
if (tokenBlacklistRedisRepository.exist(refreshToken)) {
throw new BizException(AuthErrorCode.INVALID_REFRESH_TOKEN);
}
}

public void validatePasswordResetTokenNotExists(String email) {
if (passwordResetRedisRepository.hasActiveToken(email)) {
throw new BizException(AuthErrorCode.ALREADY_SENT_PASSWORD_RESET_LINK);
}
}
}
41 changes: 41 additions & 0 deletions src/main/java/project/flipnote/auth/service/AuthReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package project.flipnote.auth.service;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import project.flipnote.auth.entity.AccountStatus;
import project.flipnote.auth.entity.UserAuth;
import project.flipnote.auth.exception.AuthErrorCode;
import project.flipnote.auth.repository.EmailVerificationRedisRepository;
import project.flipnote.auth.repository.PasswordResetRedisRepository;
import project.flipnote.auth.repository.UserAuthRepository;
import project.flipnote.common.exception.BizException;

@RequiredArgsConstructor
@Service
public class AuthReader {

private final UserAuthRepository userAuthRepository;
private final EmailVerificationRedisRepository emailVerificationRedisRepository;
private final PasswordResetRedisRepository passwordResetRedisRepository;

public UserAuth findActiveAuthAccountByEmail(String email) {
return userAuthRepository.findByEmailAndStatus(email, AccountStatus.ACTIVE)
.orElseThrow(() -> new BizException(AuthErrorCode.INVALID_CREDENTIALS));
}

public UserAuth findActiveAuthAccount(Long authId) {
return userAuthRepository.findByIdAndStatus(authId, AccountStatus.ACTIVE)
.orElseThrow(() -> new BizException(AuthErrorCode.INVALID_CREDENTIALS));
}

public String findVerificationCodeOrThrow(String email) {
return emailVerificationRedisRepository.findCode(email)
.orElseThrow(() -> new BizException(AuthErrorCode.NOT_ISSUED_VERIFICATION_CODE));
}

public String findActivePasswordResetToken(String token) {
return passwordResetRedisRepository.findEmailByToken(token)
.orElseThrow(() -> new BizException(AuthErrorCode.INVALID_PASSWORD_RESET_TOKEN));
}
}
Loading
Loading