Skip to content

Commit 272012f

Browse files
committed
Updated after CodeRabbit comments
1 parent ac71ce7 commit 272012f

5 files changed

Lines changed: 54 additions & 16 deletions

File tree

src/main/java/demo/codeexample/auth/application/AuthService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public LoginResponse login(LoginRequest request) {
4949
public void changePassword(ChangePasswordRequest request, String authHeader) {
5050

5151
// Extract email from token
52+
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
53+
throw new UnauthorizedException("Invalid authorization header");
54+
}
5255
String token = authHeader.substring(7);
5356
String email = jwtService.extractEmail(token);
5457

src/main/java/demo/codeexample/security/JwtAuthenticationFilter.java

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import demo.codeexample.user.domain.UserRepository;
44
import jakarta.servlet.FilterChain;
55
import jakarta.servlet.ServletException;
6+
import jakarta.servlet.http.Cookie;
67
import jakarta.servlet.http.HttpServletRequest;
78
import jakarta.servlet.http.HttpServletResponse;
89
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -32,40 +33,64 @@ protected void doFilterInternal(HttpServletRequest request,
3233
FilterChain filterChain)
3334
throws ServletException, IOException {
3435

36+
// 1. Try Authorization header first (API calls from Insomnia/frontend)
37+
String token = extractFromHeader(request);
3538

36-
// 1. Look for the Authorization header
37-
String authHeader = request.getHeader("Authorization");
39+
// 2. If no header, try cookie (web browser after OAuth2/form login)
40+
if (token == null) {
41+
token = extractFromCookie(request);
42+
}
3843

39-
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
44+
// 3. If no token found anywhere — pass through unauthenticated
45+
if (token == null) {
4046
filterChain.doFilter(request, response);
4147
return;
4248
}
4349

44-
// 2. Extract the token (remove "Bearer " prefix)
45-
String token = authHeader.substring(7);
46-
47-
// 3. Validate the token
50+
// 4. Validate token
4851
if (!jwtService.isTokenValid(token)) {
4952
filterChain.doFilter(request, response);
5053
return;
5154
}
5255

53-
// 4. Extract email and load user
54-
String role = jwtService.extractRole(token);
55-
Long userId = jwtService.extractUserId(token);
56+
// 5. Extract claims
57+
String role = jwtService.extractRole(token);
58+
Long userId = jwtService.extractUserId(token);
5659

57-
// 5. Tell Spring Security "this user is authenticated"
60+
// 6. Set authentication in Spring Security context
5861
UsernamePasswordAuthenticationToken authentication =
5962
new UsernamePasswordAuthenticationToken(
6063
userId,
6164
null,
6265
List.of(new SimpleGrantedAuthority("ROLE_" + role))
6366
);
6467

65-
SecurityContextHolder.getContext().setAuthentication(authentication); // SecurityContext = Spring's memory of "who is currently logged in"
66-
67-
// 6. Continue to the actual endpoint
68+
SecurityContextHolder.getContext().setAuthentication(authentication);
6869
filterChain.doFilter(request, response);
70+
}
71+
72+
// ─────────────────────────────────────────
73+
// PRIVATE HELPERS
74+
// ─────────────────────────────────────────
75+
76+
private String extractFromHeader(HttpServletRequest request) {
77+
String authHeader = request.getHeader("Authorization");
78+
if (authHeader != null && authHeader.startsWith("Bearer ")) {
79+
return authHeader.substring(7);
80+
}
81+
return null;
82+
}
83+
84+
private String extractFromCookie(HttpServletRequest request) {
85+
if (request.getCookies() == null) return null;
6986

87+
for (Cookie cookie : request.getCookies()) {
88+
if ("jwt".equals(cookie.getName())) {
89+
return cookie.getValue();
90+
}
91+
}
92+
return null;
7093
}
94+
95+
7196
}

src/main/java/demo/codeexample/security/JwtService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import demo.codeexample.user.Role;
44
import io.jsonwebtoken.Claims;
5+
import io.jsonwebtoken.JwtException;
56
import io.jsonwebtoken.Jwts;
67
import io.jsonwebtoken.io.Decoders;
78
import io.jsonwebtoken.security.Keys;
89
import org.springframework.beans.factory.annotation.Value;
9-
import org.springframework.security.oauth2.jwt.JwtException;
10+
1011
import org.springframework.stereotype.Service;
1112

1213
import javax.crypto.SecretKey;
@@ -54,7 +55,7 @@ public boolean isTokenValid(String token) {
5455
try {
5556
parseClaims(token);
5657
return true;
57-
} catch (JwtException e) {
58+
} catch (JwtException | IllegalArgumentException e) {
5859
return false;
5960
}
6061
}

src/main/java/demo/codeexample/security/OAuth2LoginSuccessHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public void onAuthenticationSuccess(HttpServletRequest request,
5656
// 5. Store in cookie + redirect based on role
5757
Cookie cookie = new Cookie("jwt", token);
5858
cookie.setHttpOnly(true);
59+
cookie.setSecure(true); //Only send this over HTTPS
60+
cookie.setAttribute("Samesite", "Strict"); //CSRF protection
5961
cookie.setPath("/");
6062
cookie.setMaxAge(86400);
6163
response.addCookie(cookie);

src/main/java/demo/codeexample/user/application/UserService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ public void deactivateUser(Long id) {
136136
@Override
137137
public UserDto createOAuthUser(String email, String firstName, String lastName) {
138138

139+
//Guard againt race conditions in concurrent OAuth2 logins
140+
if (repository.existsByEmail(email)) {
141+
return repository.findByEmail(email)
142+
.map(entity -> mapper.map(entity, UserDto.class))
143+
.orElseThrow();
144+
}
145+
139146
User newUser = new User();
140147
newUser.setEmail(email);
141148
newUser.setFirstName(firstName != null ? firstName : "Unknown");

0 commit comments

Comments
 (0)