Created LoginExceptionView to explain to client why login or refresh have failed.
This commit is contained in:
parent
1976e345b6
commit
08787d50b0
@ -46,29 +46,24 @@ public final class AuthController {
|
||||
.body(loginView);
|
||||
}
|
||||
|
||||
@ExceptionHandler(LoginException.class)
|
||||
public ResponseEntity<LoginExceptionView> onLoginException(LoginException ex) {
|
||||
final LoginExceptionView loginExceptionView = new LoginExceptionView(ex.getReason(), ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(loginExceptionView);
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<LoginView> login(@RequestBody LoginBody loginBody) {
|
||||
try {
|
||||
final LoginDetails loginDetails = this.authService.login(loginBody.getUsername(), loginBody.getPassword());
|
||||
return this.getLoginViewResponseEntity(loginDetails);
|
||||
} catch (LoginException loginException) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
||||
}
|
||||
public ResponseEntity<LoginView> login(@RequestBody LoginBody loginBody) throws LoginException {
|
||||
final LoginDetails loginDetails = this.authService.login(loginBody.getUsername(), loginBody.getPassword());
|
||||
return this.getLoginViewResponseEntity(loginDetails);
|
||||
}
|
||||
|
||||
@PostMapping("/refresh")
|
||||
public ResponseEntity<LoginView> refresh(
|
||||
@CookieValue(value = "refresh-token", required = false) @Nullable String oldRefreshToken
|
||||
) {
|
||||
if (oldRefreshToken != null) {
|
||||
try {
|
||||
final LoginDetails loginDetails = this.authService.refresh(oldRefreshToken);
|
||||
return this.getLoginViewResponseEntity(loginDetails);
|
||||
} catch (LoginException loginException) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
||||
}
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
||||
) throws LoginException {
|
||||
final LoginDetails loginDetails = this.authService.refresh(oldRefreshToken);
|
||||
return this.getLoginViewResponseEntity(loginDetails);
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
|
@ -1,7 +1,9 @@
|
||||
package app.mealsmadeeasy.api.auth;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface AuthService {
|
||||
LoginDetails login(String username, String password) throws LoginException;
|
||||
void logout(String refreshToken);
|
||||
LoginDetails refresh(String refreshToken) throws LoginException;
|
||||
LoginDetails refresh(@Nullable String refreshToken) throws LoginException;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package app.mealsmadeeasy.api.auth;
|
||||
|
||||
import app.mealsmadeeasy.api.jwt.JwtService;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
@ -45,10 +45,9 @@ public final class AuthServiceImpl implements AuthService {
|
||||
@Override
|
||||
public LoginDetails login(String username, String password) throws LoginException {
|
||||
try {
|
||||
final Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
username,
|
||||
password
|
||||
));
|
||||
final Authentication authentication = this.authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(username, password)
|
||||
);
|
||||
final UserEntity principal = (UserEntity) authentication.getPrincipal();
|
||||
return new LoginDetails(
|
||||
username,
|
||||
@ -56,7 +55,7 @@ public final class AuthServiceImpl implements AuthService {
|
||||
this.createRefreshToken(principal)
|
||||
);
|
||||
} catch (AuthenticationException e) {
|
||||
throw new LoginException(e);
|
||||
throw new LoginException(LoginExceptionReason.INVALID_CREDENTIALS, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,28 +65,32 @@ public final class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginDetails refresh(String refreshToken) throws LoginException {
|
||||
try {
|
||||
final RefreshTokenEntity old = this.refreshTokenRepository.findByToken(refreshToken)
|
||||
.orElseThrow(() -> new LoginException("No such refresh-token: " + refreshToken));
|
||||
if (old.isRevoked()) {
|
||||
throw new LoginException("RefreshToken is revoked.");
|
||||
}
|
||||
if (old.getExpires().isBefore(LocalDateTime.now())) {
|
||||
throw new LoginException("RefreshToken is expired.");
|
||||
}
|
||||
final UserEntity principal = old.getOwner();
|
||||
this.refreshTokenRepository.delete(old);
|
||||
|
||||
final String username = principal.getUsername();
|
||||
return new LoginDetails(
|
||||
username,
|
||||
this.jwtService.generateAccessToken(username),
|
||||
this.createRefreshToken(principal)
|
||||
);
|
||||
} catch (JwtException e) {
|
||||
throw new LoginException(e);
|
||||
public LoginDetails refresh(@Nullable String refreshToken) throws LoginException {
|
||||
if (refreshToken == null) {
|
||||
throw new LoginException(LoginExceptionReason.NO_REFRESH_TOKEN, "No refresh token provided.");
|
||||
}
|
||||
|
||||
final RefreshTokenEntity old = this.refreshTokenRepository.findByToken(refreshToken)
|
||||
.orElseThrow(() -> new LoginException(
|
||||
LoginExceptionReason.INVALID_REFRESH_TOKEN,
|
||||
"No such refresh-token: " + refreshToken
|
||||
));
|
||||
if (old.isRevoked()) {
|
||||
throw new LoginException(LoginExceptionReason.INVALID_REFRESH_TOKEN, "RefreshToken is revoked.");
|
||||
}
|
||||
if (old.getExpires().isBefore(LocalDateTime.now())) {
|
||||
throw new LoginException(LoginExceptionReason.EXPIRED_REFRESH_TOKEN, "RefreshToken is expired.");
|
||||
}
|
||||
|
||||
final UserEntity principal = old.getOwner();
|
||||
this.refreshTokenRepository.delete(old);
|
||||
|
||||
final String username = principal.getUsername();
|
||||
return new LoginDetails(
|
||||
username,
|
||||
this.jwtService.generateAccessToken(username),
|
||||
this.createRefreshToken(principal)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,16 +2,20 @@ package app.mealsmadeeasy.api.auth;
|
||||
|
||||
public final class LoginException extends Exception {
|
||||
|
||||
public LoginException(String message) {
|
||||
private final LoginExceptionReason reason;
|
||||
|
||||
public LoginException(LoginExceptionReason reason, String message) {
|
||||
super(message);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public LoginException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LoginException(Throwable cause) {
|
||||
public LoginException(LoginExceptionReason reason, Throwable cause) {
|
||||
super(cause);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public LoginExceptionReason getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
package app.mealsmadeeasy.api.auth;
|
||||
|
||||
public enum LoginExceptionReason {
|
||||
INVALID_CREDENTIALS,
|
||||
EXPIRED_REFRESH_TOKEN,
|
||||
INVALID_REFRESH_TOKEN,
|
||||
NO_REFRESH_TOKEN
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package app.mealsmadeeasy.api.auth;
|
||||
|
||||
public class LoginExceptionView {
|
||||
|
||||
private final LoginExceptionReason reason;
|
||||
private final String message;
|
||||
|
||||
public LoginExceptionView(LoginExceptionReason reason, String message) {
|
||||
this.reason = reason;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public LoginExceptionReason getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user