I am developing a Springboot/Angular application. I am running into an annoying issue where my backend is able to receive and parse credentials (in this case, a jwt) from fetch requests but is not receiving the same credentials when I try to use the HttpClient api.
Here are some brief examples of the two different requests:
this.httpClient.get('http://localhost:8080/api/route', {
withCredentials: true,
headers: { 'Content-Type': 'application/json' }
});
fetch('http://localhost:8080/api/route', {
method: 'GET',
headers: { 'Accept': 'application/json' },
credentials: 'include'
});
Now, in my app.config.ts I do have this line:
provideHttpClient(withFetch()),
However, the angular docs say to set withFetch there in SSR apps. I have tried to remove that, or add withOptions([withCredentials: true]) - but the results are the same.
I have tried a number of things, and since nothing I am doing in angular seems blatantly wrong (it very well could be, please tell me if you see something that is wrong), I started taking a look at my Springboot cors/security configurations:
Security Configuration:
@Configuration
public class SecurityConfig {
private final FirebaseAuthFilter firebaseAuthFilter;
public SecurityConfig(FirebaseAuthFilter firebaseAuthFilter) {
this.firebaseAuthFilter = firebaseAuthFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception {
http.cors(cors -> cors.configurationSource(corsConfigurationSource))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers(PUBLIC_ENDPOINTS).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(firebaseAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Firebase Filter (I have messed with this a significant amount, but this configuration successfully passes the jwt to the rest of the backend when FETCH requests are sent, so I do not think this is the issue):
@Component
public class FirebaseAuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
try {
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(token.substring(7));
UserDetails userDetails = new User(decodedToken.getUid(), "", Collections.emptyList());
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()));
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
}
chain.doFilter(request, response);
}
}
and my Cors Configuration:
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOriginPatterns(List.of("http://localhost:4200"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Requested-With"));
configuration.setExposedHeaders(List.of("Set-Cookie"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
My JWT is set as httpOnly, with SameSite as lax (I have tried both None and Strict, to no avail), and I can see it as such in the browser, so I know that the cookie is being set properly.
If I have provided too little information, please do ask for what you think you need to answer the question. I am somewhat experienced, but this is my first time going solo with writing the foundations for an app like this. Thank you.