I am in the process of implementing JWT authentication via cookies and am unfortunately faced with the problem that the first authentication always works without any problems. I get a cookie back in the response from the auth server (with jwt). Now I want to send this cookie with every subsequent request so that the server can validate the JWT token. Unfortunately, however, I always get the problem that CORS causes problems after I go to a secure endpoint as a test.
To be precise, I always get the error:
Cors Browser on:
- Access to XMLHttpRequest at ‘https://XYZ.germanywestcentral-01.azurewebsites.net/api/test’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.
Cors Browser off:
- Access to XMLHttpRequest at ‘https://XYZ.germanywestcentral-01.azurewebsites.net/api/test’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
What also seems strange to me is that the cookie is never sent in the second request after authentication according to browser dev-tools. Shouldn’t I see the cookie under the headers in Request Headers? I have installed an HTTP interceptor in Angular 18 that extends all requests with “withCredentials: true”. Is that correct? I’ve also tried deactivating CORS in the browser (Google Chrome) but unfortunately that doesn’t work either. Do I have any general problems in my understanding or does anyone have any tips as to why this might be?
Any answers would be greatly appreciated! 🙂
Frontend:
import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {BehaviorSubject, finalize, Observable} from 'rxjs';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
const AUTH_API = 'http://localhost:8080/auth';
@Injectable({
providedIn: 'root'
})
export class AuthService {
public loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false)
constructor(private http: HttpClient) {}
login(email: string, password: string): Observable<any> {
this.loading$.next(true);
return this.http.post(
AUTH_API + '/authenticate',
{
email,
password,
},
httpOptions
).pipe(finalize(() => this.loading$.next(false)));
}
}
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor,
HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
withCredentials: true,
});
return next.handle(req);
}
}
import { Injectable } from '@angular/core';
import {BehaviorSubject, finalize, Observable} from 'rxjs';
import {HttpClient, HttpHeaders} from '@angular/common/http';
const httpOptions = {
//withCredentials: true,
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
const AUTH_API = 'http://localhost:8080/api';
@Injectable({
providedIn: 'root'
})
export class DashboardService {
public loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false)
constructor(private http: HttpClient) { }
test(): Observable<any> {
this.loading$.next(true);
return this.http.get(
AUTH_API + '/test',
httpOptions
).pipe(finalize(() => this.loading$.next(false)));
}
}
bootstrapApplication(AppComponent, {
providers: [
provideRouter(ROUTES),
provideHttpClient(),
{
provide: HTTP_INTERCEPTORS,
useClass: HttpRequestInterceptor,
multi: true
}
]
}).catch((err) => console.error(err));
Backend:
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:8080", "http://localhost:4200"));
configuration.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("Authorization","Content-Type", "*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return source;
}
public ResponseEntity<AuthenticationResponse> authenticate(AuthenticationRequest request, HttpServletResponse response) {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getEmail(),
request.getPassword()
)
);
var user = userRepository.findByEmail(request.getEmail())
.orElseThrow();
var jwtToken = jwtService.generateToken(user, AUTH);
revokeAllUserTokens(user, TokenType.AUTH);
saveUserToken(user, jwtToken, TokenType.AUTH);
ResponseCookie jwtCookie = ResponseCookie.from("jwt", jwtToken)
.httpOnly(true)
.secure(env.acceptsProfiles(Profiles.of("prod")))
.path("/")
.maxAge(Duration.ofDays(1))
.sameSite("None")
.build();
response.addHeader("Set-Cookie", jwtCookie.toString());
return ResponseEntity.ok(AuthenticationResponse.builder().build());
}
Browser Cors off/on, Adding manually //”Access-Control-Allow-Origin”: “http://localhost:4200” to frontend header, creating auth-interceptor, …