Looks to me a csrf issue.
Add the following to the security config
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest().permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
The above code will set the CSRF token in the cookie named XSRF-TOKEN
. Angular reads this cookie for CSRF tokens by default and adds this in the subsequent requests. To make sure Angular can read it, we are setting the Http only
to False
. This has its own security implications as the CSRF token can be accessed via script now. If you don't prefer to do this, the other way would be to read the X-XSRF-TOKEN
token from the response header and send it in the subsequent request header using Angular interceptor.
Update:
When testing locally, where Angular is running on 4200, the Spring Boot app, the Keycloak server on 8080, 8085 ports.
Use the Webpack Dev server proxy with the following steps. (You don't need this config anymore keycloak.cors=true
)
in the environment.ts
update the apiUrl
apiUrl: '', //Spring Boot API
then add a proxy.conf.json
under the src
folder with the following content
{
"/api": {
"target": "http://localhost:8080",
"secure": false
},
"logLevel": "debug"
}
Add the proxy config in the angular.json
serve command
"options": {
"browserTarget": "kc-ang:build",
"proxyConfig": "src/proxy.conf.json"
}
Now you would notice the api requests would go to localhost:4200/api/* and that would be routed to the spring boot app via the above config.
With this the XSRF related cookies and the header would be part of the request and you should not get any issues.
[![Sample working version][1]][1]
Update 2: Support CORS
If your frontend and backend are on different domains and if you must support CORS, then this is what needs to be done
The security config on Spring should allow CORS for the origin of the frontend (in this case http://localhost:4200):
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/api/*").hasRole("app-manager").anyRequest()
.permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().cors();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
configuration.setAllowCredentials(Boolean.TRUE);
configuration.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/*", configuration);
return source;
}
On Angular, update the apiUrl in environment.ts
file to
apiUrl: '//localhost:8085'
While making any write operations (POST/PUT/DELETE) using the Angular http client, set the withCredentials
option to true
. (Also ensure the method is supported for CORS on the server side config. In the above CORS config we have allowed GET and POST only. If you need to support PUT/DELETE ensure you update the CORS configuration accordingly)
In this case update the following method in fournisseur.service.ts
addFournisseur(fournisseur: Fournisseur): Observable<Fournisseur> {
return this.httpClient.post<Fournisseur>(this.envUrl + '/api/fournisseurs', fournisseur, {withCredentials: true});
}
this.securityService.kc.token
decoded payload looks like? Is thereapp-manager
role? – Scannerapp-manager
role? – Josefajosefina