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
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.bugzkit.api.auth.jwt.util.JwtUtil;
import org.bugzkit.api.auth.payload.dto.AuthTokensDTO;
import org.bugzkit.api.auth.payload.request.AuthTokensRequest;
import org.bugzkit.api.auth.payload.request.ForgotPasswordRequest;
import org.bugzkit.api.auth.payload.request.RefreshAuthTokensRequest;
import org.bugzkit.api.auth.payload.request.RegisterUserRequest;
import org.bugzkit.api.auth.payload.request.ResetPasswordRequest;
import org.bugzkit.api.auth.payload.request.VerificationEmailRequest;
Expand All @@ -15,6 +12,8 @@
import org.bugzkit.api.auth.util.AuthUtil;
import org.bugzkit.api.shared.constants.Path;
import org.bugzkit.api.user.payload.dto.UserDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
Expand All @@ -28,6 +27,12 @@
public class AuthController {
private final AuthService authService;

@Value("${jwt.access-token.duration}")
private int accessTokenDuration;

@Value("${jwt.refresh-token.duration}")
private int refreshTokenDuration;

public AuthController(AuthService authService) {
this.authService = authService;
}
Expand All @@ -39,33 +44,57 @@ public ResponseEntity<UserDTO> register(
}

@PostMapping("/tokens")
public ResponseEntity<AuthTokensDTO> authenticate(
public ResponseEntity<Void> authenticate(
@Valid @RequestBody AuthTokensRequest authTokensRequest, HttpServletRequest request) {
final var ipAddress = AuthUtil.getUserIpAddress(request);
return ResponseEntity.ok(authService.authenticate(authTokensRequest, ipAddress));
final var authTokensDTO = authService.authenticate(authTokensRequest, ipAddress);
final var accessTokenCookie =
AuthUtil.createCookie("accessToken", authTokensDTO.accessToken(), accessTokenDuration);
final var refreshTokenCookie =
AuthUtil.createCookie("refreshToken", authTokensDTO.refreshToken(), refreshTokenDuration);
return ResponseEntity.noContent()
.header(HttpHeaders.SET_COOKIE, accessTokenCookie.toString())
.header(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString())
.build();
}

@DeleteMapping("/tokens")
public ResponseEntity<Void> deleteTokens(HttpServletRequest request) {
final var accessToken = JwtUtil.removeBearer(AuthUtil.getAccessTokenFromRequest(request));
final var accessToken = AuthUtil.getValueFromCookie("accessToken", request);
final var ipAddress = AuthUtil.getUserIpAddress(request);
authService.deleteTokens(accessToken, ipAddress);
return ResponseEntity.noContent().build();
final var accessTokenCookie = AuthUtil.createCookie("accessToken", "", 0);
final var refreshTokenCookie = AuthUtil.createCookie("refreshToken", "", 0);
return ResponseEntity.noContent()
.header(HttpHeaders.SET_COOKIE, accessTokenCookie.toString())
.header(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString())
.build();
}

@DeleteMapping("/tokens/devices")
public ResponseEntity<Void> deleteTokensOnAllDevices() {
authService.deleteTokensOnAllDevices();
return ResponseEntity.noContent().build();
final var accessTokenCookie = AuthUtil.createCookie("accessToken", "", 0);
final var refreshTokenCookie = AuthUtil.createCookie("refreshToken", "", 0);
return ResponseEntity.noContent()
.header(HttpHeaders.SET_COOKIE, accessTokenCookie.toString())
.header(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString())
.build();
}

@PostMapping("/tokens/refresh")
public ResponseEntity<AuthTokensDTO> refreshTokens(
@Valid @RequestBody RefreshAuthTokensRequest refreshAuthTokensRequest,
HttpServletRequest request) {
final var refreshToken = JwtUtil.removeBearer(refreshAuthTokensRequest.refreshToken());
public ResponseEntity<Void> refreshTokens(HttpServletRequest request) {
final var refreshToken = AuthUtil.getValueFromCookie("refreshToken", request);
final var ipAddress = AuthUtil.getUserIpAddress(request);
return ResponseEntity.ok(authService.refreshTokens(refreshToken, ipAddress));
final var authTokensDTO = authService.refreshTokens(refreshToken, ipAddress);
final var accessTokenCookie =
AuthUtil.createCookie("accessToken", authTokensDTO.accessToken(), accessTokenDuration);
final var refreshTokenCookie =
AuthUtil.createCookie("refreshToken", authTokensDTO.refreshToken(), refreshTokenDuration);
return ResponseEntity.noContent()
.header(HttpHeaders.SET_COOKIE, accessTokenCookie.toString())
.header(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString())
.build();
}

@PostMapping("/password/forgot")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import org.bugzkit.api.user.payload.dto.RoleDTO;

public class JwtUtil {
private static final String BEARER = "Bearer ";

private JwtUtil() {}

public static void verify(String token, String secret, JwtPurpose purpose) {
Expand All @@ -23,14 +21,6 @@ public static Algorithm getAlgorithm(String secret) {
return Algorithm.HMAC512(secret.getBytes());
}

public static String removeBearer(String token) {
return token.replace(BEARER, "");
}

public static boolean isBearer(String token) {
return token.startsWith(BEARER);
}

public static Long getUserId(String token) {
return Long.parseLong(JWT.decode(token).getIssuer());
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ protected void doFilterInternal(
@Nonnull HttpServletResponse response,
@Nonnull FilterChain chain)
throws IOException, ServletException {
final var accessToken = AuthUtil.getAccessTokenFromRequest(request);
if (accessToken == null || !JwtUtil.isBearer(accessToken)) {
final var accessToken = AuthUtil.getValueFromCookie("accessToken", request);
if (accessToken == null) {
chain.doFilter(request, response);
return;
}
Expand All @@ -45,9 +45,8 @@ protected void doFilterInternal(
}

private UsernamePasswordAuthenticationToken getAuth(String accessToken) {
final var token = JwtUtil.removeBearer(accessToken);
accessTokenService.check(token);
final var userId = JwtUtil.getUserId(token);
accessTokenService.check(accessToken);
final var userId = JwtUtil.getUserId(accessToken);
final var userPrincipal = (UserPrincipal) userDetailsService.loadUserByUserId(userId);
return new UsernamePasswordAuthenticationToken(
userPrincipal, null, userPrincipal.getAuthorities());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.bugzkit.api.auth.util;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import org.bugzkit.api.auth.security.UserPrincipal;
import org.bugzkit.api.user.model.Role.RoleName;
import org.springframework.http.HttpHeaders;
import org.springframework.boot.web.server.Cookie.SameSite;
import org.springframework.http.ResponseCookie;
import org.springframework.security.core.context.SecurityContextHolder;

public class AuthUtil {
Expand Down Expand Up @@ -38,7 +41,23 @@ public static String getUserIpAddress(HttpServletRequest request) {
return ipAddress;
}

public static String getAccessTokenFromRequest(HttpServletRequest request) {
return request.getHeader(HttpHeaders.AUTHORIZATION);
public static String getValueFromCookie(String name, HttpServletRequest request) {
final var cookies = request.getCookies();
if (cookies == null) return null;
return Arrays.stream(cookies)
.filter(cookie -> name.equals(cookie.getName()))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
}

public static ResponseCookie createCookie(String name, String value, int maxAge) {
return ResponseCookie.from(name, value)
.httpOnly(true)
.secure(true)
.path("/")
.maxAge(maxAge)
.sameSite(SameSite.STRICT.attributeValue())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package org.bugzkit.api.shared.config;

import org.bugzkit.api.shared.interceptor.RequestInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${ui.url}")
private String uiUrl;

@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("*")
.allowedOrigins(uiUrl)
.allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}

Expand Down
Loading