Why /logout calling throws "Method not allowed"? - java

My app using Spring Session (with Redis). And i use custom login controller, because i use external React client, not default Spring login page.
Login controller:
#PostMapping(value = "/login", consumes = MediaType.APPLICATION_JSON_VALUE)
public String login(#RequestBody LoginDataTo loginData) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
loginData.getEmail(),
loginData.getPassword());
Authentication authentication = this.authenticationManager.authenticate(token);
SecurityContextHolder
.getContext()
.setAuthentication(authentication);
return "OK";
}
Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http
.httpBasic().disable()
.formLogin().disable() // login form is disable, because i use external React client
.csrf().disable()
.cors().disable();
http
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true);
http
.authorizeRequests()
.antMatchers("/login").anonymous()
.antMatchers("/logout").authenticated()
.anyRequest().authenticated();
}
So... The /login endpoint's work is correct. But /logout endpoint work is incorrect. When calling /logout, it returns json:
{
"timestamp": "2021-03-30T13:45:09.142+00:00",
"status": 405,
"error": "Method Not Allowed",
"message": "",
"path": "/login"
}
Here is the request, which i using in Postman:
GET http://localhost:8080/logout
Cookie and session are deleted, that is logout's work is correct, but why is it returning this json?

I solved the problem by means of logoutSuccessHandler setting:
http
.logout()
.invalidateHttpSession(true)
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK));
Now /logout calling returns 200 OK.

Related

Custom failureHandler throws 401 in Spring security login

I'm working on a custom login failureHandler in my app, and I noticed that if I type anyRequest().authenticated() in authorizeRequest() everything works, but I have no CSS on my login page, but if I type anyRequest().permitAll(), I have CSS on my site, and logging with wrong credentials throws 401 - unauthorized error. It only happens in my custom failureHandler. Can you tell me why it happens?
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.antMatchers("/user/**")
.hasAnyRole("USER", "ADMIN")
.antMatchers("/guest*")
.permitAll()
.anyRequest()
.permitAll()
// .anyRequest()
// .authenticated()
.and()
.formLogin()
.loginPage("/guest/login")
.permitAll()
.defaultSuccessUrl("/user/all-tasks", true)
.failureUrl("/guest/login")
.failureHandler(new MyAuthenticationFailureHandler())
.and()
.logout()
.logoutUrl("/user/logout")
.deleteCookies("JSESSIONID")
.and()
.cors()
.and()
.csrf()
.disable();
}
#Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
request.getSession().setAttribute("error", true);
}
}
logging with wrong credentials throws 401 - unauthorized error
Because you are doing :
.failureUrl("/guest/login")
.failureHandler(new MyAuthenticationFailureHandler())
and what are done by failureUrl() will be reset by the subsequent failureHandler().So the customised SimpleUrlAuthenticationFailureHandler do not configure with failureUrl yet and hence it will send 401 if the authentication fails since it does know which URL to redirect to.Change it to :
.failureHandler(new MyAuthenticationFailureHandler("/guest/login"))
should redirect to "/guest/login" if authentication fails.
I noticed that if I type anyRequest().authenticated() in
authorizeRequest() everything works, but I have no CSS on my login
page, but if I type anyRequest().permitAll(), I have CSS on my site,
Because in case of anyRequest().authenticated() , the CSS 's URL also required an authenticated user to access. But in login page , the user must not be authenticated. Because if they are authenticated , it does not make sense that they can go to login page.So no CSS will be shown in login page since only unauthenticated users can go to it.
You have to exclude all the related url resources required by login page to work from any protections by configuring WebSecurity. Everyone should access them :
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/css/**")
.antMatchers("/anyThingRequiredByLoginPageToWork/**");
}

OAuth2 with Google - CORS Error (Angular + Spring boot) [duplicate]

This question already has an answer here:
CORS issue while making an Ajax request for oauth2 access token
(1 answer)
Closed 3 years ago.
I have problem with CORS error. I do request for Google oAuth2 and i get a CORS ERROR:
I want to get google authentication and generate a JWT token. When I do it without using the client everything is fine. When I send angular requests this is a problem with CORS. I allow all types of CORS. Why am I getting this error?
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=1020159669873-d9r35ssmnejud852bam87d8gqtcj5qf1.apps.googleusercontent.com&scope=openid%20profile%20email&state=8nizHP1X2z9sA8m0vqM4Lzd6VT24R15eSw5flteTywM%3D&redirect_uri=http://localhost:8080/oauth2/callback/google' (redirected from 'http://localhost:8080/oauth2/authorization/google')
from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=1020159669873-d9r35ssmnejud852bam87d8gqtcj5qf1.apps.googleusercontent.com&scope=openid%20profile%20email&state=8nizHP1X2z9sA8m0vqM4Lzd6VT24R15eSw5flteTywM%3D&redirect_uri=http://localhost:8080/oauth2/callback/google with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details.
My Angular request:
googleLogin(): Observable<LoginResponse> {
return this.http.get<LoginResponse>
(environment.baseUrl + '/oauth2/authorization/google')
.pipe(tap(response => {
localStorage.setItem('access_token', response.accessToken);
}));
}
//...
public onGoogleLogin(): void {
this.authService.googleLogin().subscribe();
}
//...
CORS CONFIG:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
.maxAge(MAX_AGE_SECS);
}
Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/api/v1/oauth0/**")
.permitAll()
.antMatchers("/api/v1/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
// włączenie obslugi oauth2
.oauth2Login()
.successHandler(this.successHandler)
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.oidcUserService(customOidcUserService);
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
Success Handler:
#Autowired
private UserRepository userRepository;
#Autowired
private JwtTokenProvider tokenProvider;
private final static String URL = "http://localhost:8080/api/v1/oauth2/authenticate";
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if (response.isCommitted()) {
return; }
DefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal();
System.out.println(oidcUser);
Map attributes = oidcUser.getAttributes();
String email = attributes.get("email").toString();
User user = userRepository.findByEmail(email).orElseThrow(
() -> new ResourceNotFoundException("User", "email", email)
);
String token = tokenProvider.generateToken(user);
String redirectionUrl = UriComponentsBuilder.fromUriString(URL).queryParam("token", token)
.build().toUriString();
getRedirectStrategy().sendRedirect(request, response, redirectionUrl);
}
}
Controller:
#RestController
#RequestMapping("/api/v1/oauth2")
public class OAuth2Controller {
#GetMapping("/authenticate")
public ResponseEntity<?> authenticateUser(#RequestParam String token) {
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
}
You cannot get the token in this example as you need to make actual redirects. There are couple of ways you could circumvent this requirement which is detailed in RFC https://www.rfc-editor.org/rfc/rfc6749#section-1.2
Initiate authorization flow in a popup and pass back the token received by server via postMessage() API provided in the browser, from the popup window back to the webapp.
Save the state, whatever it is, redirect to server which will initiate authorization flow and after token is exchanged for a grant, redirect back to the webapp with a token as a query string parameter. Then use it and restore the state.

spring security not authentication from a post ethod

I want to authenticate the user from a POST method in spring security. The post hits the controller method but the user never gets authenticated. Here is the scenario
#Autowired
private AuthenticationManagerBuilder builder;
#RequestMapping(value="/signin", method = RequestMethod.POST)
public ResponseData<Client> login(#RequestParam(value="username") String name, #RequestParam(value="password") String password,HttpServletRequest req) {
System.out.println("here..."); //this executes
Client ac = accountRepository.findByEmailAndActive(name,true);
//does the authentication
final Authentication authentication = builder.getOrBuild().authenticate(
new UsernamePasswordAuthenticationToken(
name,
password
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
return ResponseData.successData(ac);
}
This is my spring security methods/handler
.antMatchers(HttpMethod.POST,"/signin").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").defaultSuccessUrl("/index")
.loginProcessingUrl("/signin2")
.permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout=true")
.deleteCookies("JSESSIONID", "remember-me")
.invalidateHttpSession(true)
.and().csrf().disable()
.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(1200000);
Kindly assist
Default login path in spring security is something like http://localhost:8080/login. Change this path following this instructions. If you already did this please provide your implementation of it.
If you want the /signin endpoint to be authenticated, you must remove .antMatchers(HttpMethod.POST,"/signin").permitAll() from your security config.

Getting Error 401 on CORS Request from Frontend to Java Spring Boot Backend

I have a frontend with React-Native (0.55.1; localhost:8080) and a backend with Java 8 Spring Boot (2.0.2; localhost: 8081) with Spring Web Security. I want to do a post request from the frontend to the backend to POST some data. Since this should be a CORS Request, I need to config my backend to allow handling CORS-Requests. This is what I tried (see below), but I keep getting a 401 ("Unauthorized"), if I post a request to the Spring Boot Server. This is my Config for backend:
#Configuration
#EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// turn off checking for CSRF tokens
http.csrf().disable();
http
.cors()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // **permit OPTIONS call to all**
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
.and()
.formLogin()
.loginPage("/api/login")
.usernameParameter("userName")
.passwordParameter("password")
.permitAll()
.and()
.logout().logoutUrl("/api/logout");
http.exceptionHandling().authenticationEntryPoint((req, res, exc) -> res.sendError(HttpServletResponse.SC_UNAUTHORIZED));
http.formLogin().successHandler((req, res, auth) -> clearAuthenticationAttributes(req));
http.formLogin().failureHandler((req, res, exc) -> res.sendError(HttpServletResponse.SC_UNAUTHORIZED));
http.logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8081"));
configuration.setAllowedMethods(Arrays.asList("POST, GET, OPTIONS, DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
I do the call (a POST) from the frontend with Axios (via apisauce: https://github.com/infinitered/apisauce) from a React client
on localhost:8081:
import apisauce from 'apisauce'
const create = (baseURL = 'http://localhost:8080/api/') => {
const api = apisauce.create({baseURL,
headers: {
'Accept': 'application/json',
"Content-type": "application/json",
},
withCredentials: true,
dataType: 'json',
// 10 second timeout...
timeout: 10000
})
// this is the Axios POST request with apisauce
api.post('login', data)
How can I achieve a successfull request?

how to send refreshToken to the token endpoint?

I am using JWT Tokens with springboot-security-jwt, that have some documentation about token generation, but none about how to send the refreshToken to the token endpoint: using POST? GET? packing parameters in JSON? there are an example of the JSON pack?
NOTES
My endpoint can be used as localhost, https://localhost:8080/api/user/register, and it is working fine... return a JSON like this,
{
"refreshToken": "eyJhbGciOiJIUzUxMiJ9......Jj3hnQuMd6Im9AJhmmxaA7ILiERqHuTUf0BYCerWe4ziggvs2PiCfB_3J2f_Gc3hOqY1IgJWJRm_LrTs1UcxwQ",
"token": "eyJhbGciOiJIUzUxMiJ9......-CWgg4srJoevN7PVKOQfsQXAE3h5ySkabUb-Q-xPsEQO18KSYXWw"
}
but, how to send refreshToken to api/auth/token endpoint?
(I not see any clues at your article)
Using postman with a POST to https://localhost:8080/api/auth/token with body
{"refreshToken": "eyJhbGciOiJIUzUxMiJ9.......Jj3hnQuMd6Im9AJhmmxaA7ILiERqHuTUf0BYCerWe4ziggvs2PiCfB_3J2f_Gc3hOqY1IgJWJRm_LrTs1UcxwQ",
}
I have response
{
"errorCode": 10,
"message": "Authentication failed",
"status": 401,
"timestamp": 1481753363749
}
Perhaps other problem...
(edit with more clues about my implementation)
My configs at my equivalent WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "Authorization";
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/login";
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/auth/**";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/token";
...
}
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // We don't need CSRF for JWT based authentication
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
.and()
.authorizeRequests()
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
.and()
.addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
so, perhaps (?) no endpoint /token exist (!) with this changes.
... Where the springboot-security-jwt /token implementation? to check it (or a kind of "health endpoint test")...
PS: the spected endpoint "/api/token" and any other "api/Mammy" returns 405 (Method Not Allowed), and testing as authorized endpoint, "auth/token" or "api/Mammy", returns 401 and error 10 (Authentication failed).

Categories

Resources